fe5854652255dc2adadfce72db684d7647a7c622
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8valuetypewrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8valuetypewrapper_p.h"
43 #include "qv8engine_p.h"
44
45 #include <private/qqmlvaluetype_p.h>
46 #include <private/qqmlbinding_p.h>
47 #include <private/qqmlglobal_p.h>
48
49 QT_BEGIN_NAMESPACE
50
51 class QV8ValueTypeResource : public QV8ObjectResource
52 {
53     V8_RESOURCE_TYPE(ValueTypeType);
54
55 public:
56     enum ObjectType { Reference, Copy };
57
58     QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType);
59
60     ObjectType objectType;
61     QQmlValueType *type;
62 };
63
64 class QV8ValueTypeReferenceResource : public QV8ValueTypeResource
65 {
66 public:
67     QV8ValueTypeReferenceResource(QV8Engine *engine);
68
69     QQmlGuard<QObject> object;
70     int property;
71 };
72
73 class QV8ValueTypeCopyResource : public QV8ValueTypeResource
74 {
75 public:
76     QV8ValueTypeCopyResource(QV8Engine *engine);
77
78     QVariant value;
79 };
80
81 QV8ValueTypeResource::QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType)
82 : QV8ObjectResource(engine), objectType(objectType)
83 {
84 }
85
86 QV8ValueTypeReferenceResource::QV8ValueTypeReferenceResource(QV8Engine *engine)
87 : QV8ValueTypeResource(engine, Reference)
88 {
89 }
90
91 QV8ValueTypeCopyResource::QV8ValueTypeCopyResource(QV8Engine *engine)
92 : QV8ValueTypeResource(engine, Copy)
93 {
94 }
95
96 QV8ValueTypeWrapper::QV8ValueTypeWrapper()
97 : m_engine(0)
98 {
99 }
100
101 QV8ValueTypeWrapper::~QV8ValueTypeWrapper()
102 {
103 }
104
105 void QV8ValueTypeWrapper::destroy()
106 {
107     qPersistentDispose(m_toString);
108     qPersistentDispose(m_constructor);
109     qPersistentDispose(m_toStringSymbol);
110 }
111
112 static quint32 toStringHash = -1;
113
114 void QV8ValueTypeWrapper::init(QV8Engine *engine)
115 {
116     m_engine = 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());
126
127     m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
128     m_toStringString = QHashedV8String(m_toStringSymbol);
129     toStringHash = m_toStringString.hash();
130 }
131
132 v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(QObject *object, int property, QQmlValueType *type)
133 {
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);
139     return rv;
140 }
141
142 v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(const QVariant &value, QQmlValueType *type)
143 {
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);
149     return rv;
150 }
151
152 static bool readReferenceValue(QV8ValueTypeReferenceResource *reference)
153 {
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) {
171                     return false;
172                 }
173             } else {
174                 return false;
175             }
176         }
177         reference->type->setValue(variantReferenceValue);
178     } else {
179         // value-type reference
180         reference->type->read(reference->object, reference->property);
181     }
182     return true;
183 }
184
185 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj, int typeHint, bool *succeeded)
186 {
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);
191 }
192
193 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj)
194 {
195     QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj);
196     if (r) return toVariant(r);
197     else return QVariant();
198 }
199
200 QVariant QV8ValueTypeWrapper::toVariant(QV8ObjectResource *r)
201 {
202     Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
203     QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
204     
205     if (resource->objectType == QV8ValueTypeResource::Reference) {
206         QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
207
208         if (reference->object && readReferenceValue(reference)) {
209             return reference->type->value();
210         } else {
211             return QVariant();
212         }
213
214     } else {
215         Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
216
217         QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
218
219         return copy->value;
220     }
221 }
222
223 bool QV8ValueTypeWrapper::isEqual(QV8ObjectResource *r, const QVariant& value)
224 {
225     Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
226     QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
227
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);
232         } else {
233             return false;
234         }
235     } else {
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))
240             return true;
241         return (value == copy->value);
242     }
243 }
244
245 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToStringGetter(v8::Local<v8::String> property,
246                                                         const v8::AccessorInfo &info)
247 {
248     Q_UNUSED(property);
249     return info.Data();
250 }
251
252 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToString(const v8::Arguments &args)
253 {
254     QV8ValueTypeResource *resource = v8_resource_cast<QV8ValueTypeResource>(args.This());
255     if (resource) {
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());
260             } else {
261                 return v8::Undefined();
262             }
263         } else {
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());
268         }
269     } else {
270         return v8::Undefined();
271     }
272 }
273
274 v8::Handle<v8::Value> QV8ValueTypeWrapper::Getter(v8::Local<v8::String> property, 
275                                                   const v8::AccessorInfo &info)
276 {
277     QV8ValueTypeResource *r =  v8_resource_cast<QV8ValueTypeResource>(info.This());
278     if (!r) return v8::Handle<v8::Value>();
279
280     QHashedV8String propertystring(property);
281
282     {
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;
288         }
289     }
290
291     // Note: readReferenceValue() can change the reference->type.
292     if (r->objectType == QV8ValueTypeResource::Reference) {
293         QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r);
294
295         if (!reference->object || !readReferenceValue(reference))
296             return v8::Handle<v8::Value>();
297
298     } else {
299         Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
300
301         QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
302
303         r->type->setValue(copy->value);
304     }
305
306     QQmlPropertyData local;
307     QQmlPropertyData *result = 0;
308     {
309         QQmlData *ddata = QQmlData::get(r->type, false);
310         if (ddata && ddata->propertyCache)
311             result = ddata->propertyCache->property(propertystring);
312         else
313             result = QQmlPropertyCache::property(r->engine->engine(), r->type,
314                                                          propertystring, local);
315     }
316
317     if (!result)
318         return v8::Handle<v8::Value>();
319
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);
323     }
324
325 #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
326     if (result->propType == metatype) { \
327         cpptype v; \
328         void *args[] = { &v, 0 }; \
329         r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \
330         return constructor(v); \
331     }
332
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);
338
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
344 }
345
346 v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property, 
347                                                   v8::Local<v8::Value> value,
348                                                   const v8::AccessorInfo &info)
349 {
350     QV8ValueTypeResource *r =  v8_resource_cast<QV8ValueTypeResource>(info.This());
351     if (!r) return value;
352
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);
357
358         if (!reference->object || !writebackProperty.isWritable() || !readReferenceValue(reference))
359             return value;
360
361         // we lookup the index after readReferenceValue() since it can change the reference->type.
362         int index = r->type->metaObject()->indexOfProperty(propName.constData());
363         if (index == -1)
364             return value;
365         QMetaProperty p = r->type->metaObject()->property(index);
366
367         QQmlBinding *newBinding = 0;
368
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)));
374                 return value;
375             }
376
377             QQmlContextData *context = r->engine->callingContext();
378             v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
379
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();
388
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());
397
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);
403         }
404
405         QQmlAbstractBinding *oldBinding = 
406             QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding);
407         if (oldBinding)
408             oldBinding->destroy();
409
410         if (!value->IsFunction()) {
411             QVariant v = r->engine->toVariant(value, -1);
412
413             if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
414                 v = v.toInt();
415
416             p.write(reference->type, v);
417
418             if (writebackProperty.userType() == QMetaType::QVariant) {
419                 QVariant variantReferenceValue = r->type->value();
420                 reference->type->writeVariantValue(reference->object, reference->property, 0, &variantReferenceValue);
421             } else {
422                 reference->type->write(reference->object, reference->property, 0);
423             }
424         }
425
426     } else {
427         Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
428
429         QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
430
431         int index = r->type->metaObject()->indexOfProperty(propName.constData());
432         if (index == -1)
433             return value;
434
435         QVariant v = r->engine->toVariant(value, -1);
436
437         r->type->setValue(copy->value);
438         QMetaProperty p = r->type->metaObject()->property(index);
439         p.write(r->type, v);
440         copy->value = r->type->value();
441     }
442
443     return value;
444 }
445
446 QT_END_NAMESPACE