1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8valuetypewrapper_p.h"
43 #include "qv8engine_p.h"
45 #include <private/qqmlvaluetype_p.h>
46 #include <private/qqmlbinding_p.h>
47 #include <private/qqmlglobal_p.h>
51 class QV8ValueTypeResource : public QV8ObjectResource
53 V8_RESOURCE_TYPE(ValueTypeType);
56 enum ObjectType { Reference, Copy };
58 QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType);
60 ObjectType objectType;
64 class QV8ValueTypeReferenceResource : public QV8ValueTypeResource
67 QV8ValueTypeReferenceResource(QV8Engine *engine);
69 QQmlGuard<QObject> object;
73 class QV8ValueTypeCopyResource : public QV8ValueTypeResource
76 QV8ValueTypeCopyResource(QV8Engine *engine);
81 QV8ValueTypeResource::QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType)
82 : QV8ObjectResource(engine), objectType(objectType)
86 QV8ValueTypeReferenceResource::QV8ValueTypeReferenceResource(QV8Engine *engine)
87 : QV8ValueTypeResource(engine, Reference)
91 QV8ValueTypeCopyResource::QV8ValueTypeCopyResource(QV8Engine *engine)
92 : QV8ValueTypeResource(engine, Copy)
96 QV8ValueTypeWrapper::QV8ValueTypeWrapper()
101 QV8ValueTypeWrapper::~QV8ValueTypeWrapper()
105 void QV8ValueTypeWrapper::destroy()
107 qPersistentDispose(m_toString);
108 qPersistentDispose(m_constructor);
109 qPersistentDispose(m_toStringSymbol);
112 static quint32 toStringHash = -1;
114 void QV8ValueTypeWrapper::init(QV8Engine *engine)
117 m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction());
118 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
119 ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter);
120 ft->InstanceTemplate()->SetHasExternalResource(true);
121 ft->InstanceTemplate()->MarkAsUseUserObjectComparison();
122 ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0,
123 m_toString, v8::DEFAULT,
124 v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete));
125 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
127 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
128 m_toStringString = QHashedV8String(m_toStringSymbol);
129 toStringHash = m_toStringString.hash();
132 v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(QObject *object, int property, QQmlValueType *type)
134 // XXX NewInstance() should be optimized
135 v8::Local<v8::Object> rv = m_constructor->NewInstance();
136 QV8ValueTypeReferenceResource *r = new QV8ValueTypeReferenceResource(m_engine);
137 r->type = type; r->object = object; r->property = property;
138 rv->SetExternalResource(r);
142 v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(const QVariant &value, QQmlValueType *type)
144 // XXX NewInstance() should be optimized
145 v8::Local<v8::Object> rv = m_constructor->NewInstance();
146 QV8ValueTypeCopyResource *r = new QV8ValueTypeCopyResource(m_engine);
147 r->type = type; r->value = value;
148 rv->SetExternalResource(r);
152 static bool readReferenceValue(QV8ValueTypeReferenceResource *reference)
154 // A reference resource may be either a "true" reference (eg, to a QVector3D property)
155 // or a "variant" reference (eg, to a QVariant property which happens to contain a value-type).
156 QMetaProperty writebackProperty = reference->object->metaObject()->property(reference->property);
157 if (writebackProperty.userType() == QMetaType::QVariant) {
158 // variant-containing-value-type reference
159 QVariant variantReferenceValue;
160 reference->type->readVariantValue(reference->object, reference->property, &variantReferenceValue);
161 int variantReferenceType = variantReferenceValue.userType();
162 if (variantReferenceType != reference->type->userType()) {
163 // This is a stale VariantReference. That is, the variant has been
164 // overwritten with a different type in the meantime.
165 // We need to modify this reference to the updated value type, if
166 // possible, or return false if it is not a value type.
167 QQmlEngine *e = reference->engine->engine();
168 if (QQmlValueTypeFactory::isValueType(variantReferenceType) && e) {
169 reference->type = QQmlEnginePrivate::get(e)->valueTypes[variantReferenceType];
170 if (!reference->type) {
177 reference->type->setValue(variantReferenceValue);
179 // value-type reference
180 reference->type->read(reference->object, reference->property);
185 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj, int typeHint, bool *succeeded)
187 // NOTE: obj must not be an external resource object (ie, wrapper object)
188 // instead, it is a normal js object which one of the value-type providers
189 // may know how to convert to the given type.
190 return QQml_valueTypeProvider()->createVariantFromJsObject(typeHint, QQmlV8Handle::fromHandle(obj), m_engine, succeeded);
193 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj)
195 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj);
196 if (r) return toVariant(r);
197 else return QVariant();
200 QVariant QV8ValueTypeWrapper::toVariant(QV8ObjectResource *r)
202 Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
203 QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
205 if (resource->objectType == QV8ValueTypeResource::Reference) {
206 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
208 if (reference->object && readReferenceValue(reference)) {
209 return reference->type->value();
215 Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
217 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
223 bool QV8ValueTypeWrapper::isEqual(QV8ObjectResource *r, const QVariant& value)
225 Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
226 QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
228 if (resource->objectType == QV8ValueTypeResource::Reference) {
229 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
230 if (reference->object && readReferenceValue(reference)) {
231 return reference->type->isEqual(value);
236 Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
237 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
238 resource->type->setValue(copy->value);
239 if (resource->type->isEqual(value))
241 return (value == copy->value);
245 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToStringGetter(v8::Local<v8::String> property,
246 const v8::AccessorInfo &info)
252 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToString(const v8::Arguments &args)
254 QV8ValueTypeResource *resource = v8_resource_cast<QV8ValueTypeResource>(args.This());
256 if (resource->objectType == QV8ValueTypeResource::Reference) {
257 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
258 if (reference->object && readReferenceValue(reference)) {
259 return resource->engine->toString(resource->type->toString());
261 return v8::Undefined();
264 Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
265 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
266 resource->type->setValue(copy->value);
267 return resource->engine->toString(resource->type->toString());
270 return v8::Undefined();
274 v8::Handle<v8::Value> QV8ValueTypeWrapper::Getter(v8::Local<v8::String> property,
275 const v8::AccessorInfo &info)
277 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This());
278 if (!r) return v8::Handle<v8::Value>();
280 QHashedV8String propertystring(property);
283 // Comparing the hash first actually makes a measurable difference here, at least on x86
284 quint32 hash = propertystring.hash();
285 if (hash == toStringHash &&
286 r->engine->valueTypeWrapper()->m_toStringString == propertystring) {
287 return r->engine->valueTypeWrapper()->m_toString;
291 // Note: readReferenceValue() can change the reference->type.
292 if (r->objectType == QV8ValueTypeResource::Reference) {
293 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r);
295 if (!reference->object || !readReferenceValue(reference))
296 return v8::Handle<v8::Value>();
299 Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
301 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
303 r->type->setValue(copy->value);
306 QQmlPropertyData local;
307 QQmlPropertyData *result = 0;
309 QQmlData *ddata = QQmlData::get(r->type, false);
310 if (ddata && ddata->propertyCache)
311 result = ddata->propertyCache->property(propertystring);
313 result = QQmlPropertyCache::property(r->engine->engine(), r->type,
314 propertystring, local);
318 return v8::Handle<v8::Value>();
320 if (result->isFunction()) {
321 // calling a Q_INVOKABLE function of a value type
322 return r->engine->qobjectWrapper()->getProperty(r->type, propertystring, QV8QObjectWrapper::IgnoreRevision);
325 #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
326 if (result->propType == metatype) { \
328 void *args[] = { &v, 0 }; \
329 r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \
330 return constructor(v); \
333 // These four types are the most common used by the value type wrappers
334 VALUE_TYPE_LOAD(QMetaType::QReal, qreal, v8::Number::New);
335 VALUE_TYPE_LOAD(QMetaType::Int, int, v8::Integer::New);
336 VALUE_TYPE_LOAD(QMetaType::QString, QString, r->engine->toString);
337 VALUE_TYPE_LOAD(QMetaType::Bool, bool, v8::Boolean::New);
339 QVariant v(result->propType, (void *)0);
340 void *args[] = { v.data(), 0 };
341 r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args);
342 return r->engine->fromVariant(v);
343 #undef VALUE_TYPE_ACCESSOR
346 v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property,
347 v8::Local<v8::Value> value,
348 const v8::AccessorInfo &info)
350 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This());
351 if (!r) return value;
353 QByteArray propName = r->engine->toString(property).toUtf8();
354 if (r->objectType == QV8ValueTypeResource::Reference) {
355 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r);
356 QMetaProperty writebackProperty = reference->object->metaObject()->property(reference->property);
358 if (!reference->object || !writebackProperty.isWritable() || !readReferenceValue(reference))
361 // we lookup the index after readReferenceValue() since it can change the reference->type.
362 int index = r->type->metaObject()->indexOfProperty(propName.constData());
365 QMetaProperty p = r->type->metaObject()->property(index);
367 QQmlBinding *newBinding = 0;
369 if (value->IsFunction()) {
370 if (value->ToObject()->GetHiddenValue(r->engine->bindingFlagKey()).IsEmpty()) {
371 // assigning a JS function to a non-var-property is not allowed.
372 QString error = QLatin1String("Cannot assign JavaScript function to value-type property");
373 v8::ThrowException(v8::Exception::Error(r->engine->toString(error)));
377 QQmlContextData *context = r->engine->callingContext();
378 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
380 QQmlPropertyData cacheData;
381 cacheData.setFlags(QQmlPropertyData::IsWritable |
382 QQmlPropertyData::IsValueTypeVirtual);
383 cacheData.propType = reference->object->metaObject()->property(reference->property).userType();
384 cacheData.coreIndex = reference->property;
385 cacheData.valueTypeFlags = 0;
386 cacheData.valueTypeCoreIndex = index;
387 cacheData.valueTypePropType = p.userType();
389 v8::Local<v8::StackTrace> trace =
390 v8::StackTrace::CurrentStackTrace(1,
391 (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
392 v8::StackTrace::kScriptName));
393 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
394 int lineNumber = frame->GetLineNumber();
395 int columnNumber = frame->GetColumn();
396 QString url = r->engine->toString(frame->GetScriptName());
398 newBinding = new QQmlBinding(&function, reference->object, context,
399 url, lineNumber, columnNumber);
400 newBinding->setTarget(reference->object, cacheData, context);
401 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
402 QQmlBinding::RequiresThisObject);
405 QQmlAbstractBinding *oldBinding =
406 QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding);
408 oldBinding->destroy();
410 if (!value->IsFunction()) {
411 QVariant v = r->engine->toVariant(value, -1);
413 if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
416 p.write(reference->type, v);
418 if (writebackProperty.userType() == QMetaType::QVariant) {
419 QVariant variantReferenceValue = r->type->value();
420 reference->type->writeVariantValue(reference->object, reference->property, 0, &variantReferenceValue);
422 reference->type->write(reference->object, reference->property, 0);
427 Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
429 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
431 int index = r->type->metaObject()->indexOfProperty(propName.constData());
435 QVariant v = r->engine->toVariant(value, -1);
437 r->type->setValue(copy->value);
438 QMetaProperty p = r->type->metaObject()->property(index);
440 copy->value = r->type->value();