1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 if (QQmlValueTypeFactory::isValueType(variantReferenceType)) {
168 reference->type = QQmlValueTypeFactory::valueType(variantReferenceType);
169 if (!reference->type) {
176 reference->type->setValue(variantReferenceValue);
178 // value-type reference
179 reference->type->read(reference->object, reference->property);
184 bool QV8ValueTypeWrapper::isValueType(v8::Handle<v8::Object> obj) const
186 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj);
190 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj, int typeHint, bool *succeeded)
192 // NOTE: obj must not be an external resource object (ie, wrapper object)
193 // instead, it is a normal js object which one of the value-type providers
194 // may know how to convert to the given type.
195 return QQml_valueTypeProvider()->createVariantFromJsObject(typeHint, QQmlV8Handle::fromHandle(obj), m_engine, succeeded);
198 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj)
200 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj);
201 if (r) return toVariant(r);
202 else return QVariant();
205 QVariant QV8ValueTypeWrapper::toVariant(QV8ObjectResource *r)
207 Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
208 QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
210 if (resource->objectType == QV8ValueTypeResource::Reference) {
211 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
213 if (reference->object && readReferenceValue(reference)) {
214 return reference->type->value();
220 Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
222 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
228 bool QV8ValueTypeWrapper::isEqual(QV8ObjectResource *r, const QVariant& value)
230 Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
231 QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
233 if (resource->objectType == QV8ValueTypeResource::Reference) {
234 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
235 if (reference->object && readReferenceValue(reference)) {
236 return reference->type->isEqual(value);
241 Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
242 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
243 resource->type->setValue(copy->value);
244 if (resource->type->isEqual(value))
246 return (value == copy->value);
250 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToStringGetter(v8::Local<v8::String> property,
251 const v8::AccessorInfo &info)
257 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToString(const v8::Arguments &args)
259 QV8ValueTypeResource *resource = v8_resource_cast<QV8ValueTypeResource>(args.This());
261 if (resource->objectType == QV8ValueTypeResource::Reference) {
262 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
263 if (reference->object && readReferenceValue(reference)) {
264 return resource->engine->toString(resource->type->toString());
266 return v8::Undefined();
269 Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
270 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
271 resource->type->setValue(copy->value);
272 return resource->engine->toString(resource->type->toString());
275 return v8::Undefined();
279 v8::Handle<v8::Value> QV8ValueTypeWrapper::Getter(v8::Local<v8::String> property,
280 const v8::AccessorInfo &info)
282 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This());
283 if (!r) return v8::Handle<v8::Value>();
285 QHashedV8String propertystring(property);
288 // Comparing the hash first actually makes a measurable difference here, at least on x86
289 quint32 hash = propertystring.hash();
290 if (hash == toStringHash &&
291 r->engine->valueTypeWrapper()->m_toStringString == propertystring) {
292 return r->engine->valueTypeWrapper()->m_toString;
296 // Note: readReferenceValue() can change the reference->type.
297 if (r->objectType == QV8ValueTypeResource::Reference) {
298 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r);
300 if (!reference->object || !readReferenceValue(reference))
301 return v8::Handle<v8::Value>();
304 Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
306 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
308 r->type->setValue(copy->value);
311 QQmlPropertyData local;
312 QQmlPropertyData *result = 0;
314 QQmlData *ddata = QQmlData::get(r->type, false);
315 if (ddata && ddata->propertyCache)
316 result = ddata->propertyCache->property(propertystring, 0, 0);
318 result = QQmlPropertyCache::property(r->engine->engine(), r->type,
319 propertystring, 0, local);
323 return v8::Handle<v8::Value>();
325 if (result->isFunction()) {
326 // calling a Q_INVOKABLE function of a value type
327 QQmlContextData *context = r->engine->callingContext();
328 return r->engine->qobjectWrapper()->getProperty(r->type, propertystring, context, QV8QObjectWrapper::IgnoreRevision);
331 #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
332 if (result->propType == metatype) { \
334 void *args[] = { &v, 0 }; \
335 r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \
336 return constructor(v); \
339 // These four types are the most common used by the value type wrappers
340 VALUE_TYPE_LOAD(QMetaType::QReal, qreal, v8::Number::New);
341 VALUE_TYPE_LOAD(QMetaType::Int, int, v8::Integer::New);
342 VALUE_TYPE_LOAD(QMetaType::QString, QString, r->engine->toString);
343 VALUE_TYPE_LOAD(QMetaType::Bool, bool, v8::Boolean::New);
345 QVariant v(result->propType, (void *)0);
346 void *args[] = { v.data(), 0 };
347 r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args);
348 return r->engine->fromVariant(v);
349 #undef VALUE_TYPE_ACCESSOR
352 v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property,
353 v8::Local<v8::Value> value,
354 const v8::AccessorInfo &info)
356 QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This());
357 if (!r) return value;
359 QByteArray propName = r->engine->toString(property).toUtf8();
360 if (r->objectType == QV8ValueTypeResource::Reference) {
361 QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r);
362 QMetaProperty writebackProperty = reference->object->metaObject()->property(reference->property);
364 if (!reference->object || !writebackProperty.isWritable() || !readReferenceValue(reference))
367 // we lookup the index after readReferenceValue() since it can change the reference->type.
368 int index = r->type->metaObject()->indexOfProperty(propName.constData());
371 QMetaProperty p = r->type->metaObject()->property(index);
373 QQmlBinding *newBinding = 0;
375 if (value->IsFunction()) {
376 if (value->ToObject()->GetHiddenValue(r->engine->bindingFlagKey()).IsEmpty()) {
377 // assigning a JS function to a non-var-property is not allowed.
378 QString error = QLatin1String("Cannot assign JavaScript function to value-type property");
379 v8::ThrowException(v8::Exception::Error(r->engine->toString(error)));
383 QQmlContextData *context = r->engine->callingContext();
384 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
386 QQmlPropertyData cacheData;
387 cacheData.setFlags(QQmlPropertyData::IsWritable |
388 QQmlPropertyData::IsValueTypeVirtual);
389 cacheData.propType = reference->object->metaObject()->property(reference->property).userType();
390 cacheData.coreIndex = reference->property;
391 cacheData.valueTypeFlags = 0;
392 cacheData.valueTypeCoreIndex = index;
393 cacheData.valueTypePropType = p.userType();
395 v8::Local<v8::StackTrace> trace =
396 v8::StackTrace::CurrentStackTrace(1,
397 (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
398 v8::StackTrace::kScriptName));
399 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
400 int lineNumber = frame->GetLineNumber();
401 int columnNumber = frame->GetColumn();
402 QString url = r->engine->toString(frame->GetScriptName());
404 newBinding = new QQmlBinding(&function, reference->object, context,
405 url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
406 newBinding->setTarget(reference->object, cacheData, context);
407 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
408 QQmlBinding::RequiresThisObject);
411 QQmlAbstractBinding *oldBinding =
412 QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding);
414 oldBinding->destroy();
416 if (!value->IsFunction()) {
417 QVariant v = r->engine->toVariant(value, -1);
419 if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
422 p.write(reference->type, v);
424 if (writebackProperty.userType() == QMetaType::QVariant) {
425 QVariant variantReferenceValue = r->type->value();
426 reference->type->writeVariantValue(reference->object, reference->property, 0, &variantReferenceValue);
428 reference->type->write(reference->object, reference->property, 0);
433 Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
435 QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
437 int index = r->type->metaObject()->indexOfProperty(propName.constData());
441 QVariant v = r->engine->toVariant(value, -1);
443 r->type->setValue(copy->value);
444 QMetaProperty p = r->type->metaObject()->property(index);
446 copy->value = r->type->value();