Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8valuetypewrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
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             if (QQmlValueTypeFactory::isValueType(variantReferenceType)) {
168                 reference->type = QQmlValueTypeFactory::valueType(variantReferenceType);
169                 if (!reference->type) {
170                     return false;
171                 }
172             } else {
173                 return false;
174             }
175         }
176         reference->type->setValue(variantReferenceValue);
177     } else {
178         // value-type reference
179         reference->type->read(reference->object, reference->property);
180     }
181     return true;
182 }
183
184 bool QV8ValueTypeWrapper::isValueType(v8::Handle<v8::Object> obj) const
185 {
186     QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj);
187     return (r != 0);
188 }
189
190 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj, int typeHint, bool *succeeded)
191 {
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);
196 }
197
198 QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj)
199 {
200     QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj);
201     if (r) return toVariant(r);
202     else return QVariant();
203 }
204
205 QVariant QV8ValueTypeWrapper::toVariant(QV8ObjectResource *r)
206 {
207     Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
208     QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
209     
210     if (resource->objectType == QV8ValueTypeResource::Reference) {
211         QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource);
212
213         if (reference->object && readReferenceValue(reference)) {
214             return reference->type->value();
215         } else {
216             return QVariant();
217         }
218
219     } else {
220         Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy);
221
222         QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource);
223
224         return copy->value;
225     }
226 }
227
228 bool QV8ValueTypeWrapper::isEqual(QV8ObjectResource *r, const QVariant& value)
229 {
230     Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType);
231     QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r);
232
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);
237         } else {
238             return false;
239         }
240     } else {
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))
245             return true;
246         return (value == copy->value);
247     }
248 }
249
250 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToStringGetter(v8::Local<v8::String> property,
251                                                         const v8::AccessorInfo &info)
252 {
253     Q_UNUSED(property);
254     return info.Data();
255 }
256
257 v8::Handle<v8::Value> QV8ValueTypeWrapper::ToString(const v8::Arguments &args)
258 {
259     QV8ValueTypeResource *resource = v8_resource_cast<QV8ValueTypeResource>(args.This());
260     if (resource) {
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());
265             } else {
266                 return v8::Undefined();
267             }
268         } else {
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());
273         }
274     } else {
275         return v8::Undefined();
276     }
277 }
278
279 v8::Handle<v8::Value> QV8ValueTypeWrapper::Getter(v8::Local<v8::String> property, 
280                                                   const v8::AccessorInfo &info)
281 {
282     QV8ValueTypeResource *r =  v8_resource_cast<QV8ValueTypeResource>(info.This());
283     if (!r) return v8::Handle<v8::Value>();
284
285     QHashedV8String propertystring(property);
286
287     {
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;
293         }
294     }
295
296     // Note: readReferenceValue() can change the reference->type.
297     if (r->objectType == QV8ValueTypeResource::Reference) {
298         QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r);
299
300         if (!reference->object || !readReferenceValue(reference))
301             return v8::Handle<v8::Value>();
302
303     } else {
304         Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
305
306         QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
307
308         r->type->setValue(copy->value);
309     }
310
311     QQmlPropertyData local;
312     QQmlPropertyData *result = 0;
313     {
314         QQmlData *ddata = QQmlData::get(r->type, false);
315         if (ddata && ddata->propertyCache)
316             result = ddata->propertyCache->property(propertystring, 0, 0);
317         else
318             result = QQmlPropertyCache::property(r->engine->engine(), r->type,
319                                                          propertystring, 0, local);
320     }
321
322     if (!result)
323         return v8::Handle<v8::Value>();
324
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);
329     }
330
331 #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
332     if (result->propType == metatype) { \
333         cpptype v; \
334         void *args[] = { &v, 0 }; \
335         r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \
336         return constructor(v); \
337     }
338
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);
344
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
350 }
351
352 v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property, 
353                                                   v8::Local<v8::Value> value,
354                                                   const v8::AccessorInfo &info)
355 {
356     QV8ValueTypeResource *r =  v8_resource_cast<QV8ValueTypeResource>(info.This());
357     if (!r) return value;
358
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);
363
364         if (!reference->object || !writebackProperty.isWritable() || !readReferenceValue(reference))
365             return value;
366
367         // we lookup the index after readReferenceValue() since it can change the reference->type.
368         int index = r->type->metaObject()->indexOfProperty(propName.constData());
369         if (index == -1)
370             return value;
371         QMetaProperty p = r->type->metaObject()->property(index);
372
373         QQmlBinding *newBinding = 0;
374
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)));
380                 return value;
381             }
382
383             QQmlContextData *context = r->engine->callingContext();
384             v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
385
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();
394
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());
403
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);
409         }
410
411         QQmlAbstractBinding *oldBinding = 
412             QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding);
413         if (oldBinding)
414             oldBinding->destroy();
415
416         if (!value->IsFunction()) {
417             QVariant v = r->engine->toVariant(value, -1);
418
419             if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
420                 v = v.toInt();
421
422             p.write(reference->type, v);
423
424             if (writebackProperty.userType() == QMetaType::QVariant) {
425                 QVariant variantReferenceValue = r->type->value();
426                 reference->type->writeVariantValue(reference->object, reference->property, 0, &variantReferenceValue);
427             } else {
428                 reference->type->write(reference->object, reference->property, 0);
429             }
430         }
431
432     } else {
433         Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy);
434
435         QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r);
436
437         int index = r->type->metaObject()->indexOfProperty(propName.constData());
438         if (index == -1)
439             return value;
440
441         QVariant v = r->engine->toVariant(value, -1);
442
443         r->type->setValue(copy->value);
444         QMetaProperty p = r->type->metaObject()->property(index);
445         p.write(r->type, v);
446         copy->value = r->type->value();
447     }
448
449     return value;
450 }
451
452 QT_END_NAMESPACE