Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8typewrapper.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 "qv8contextwrapper_p.h"
43 #include "qv8engine_p.h"
44
45 #include <private/qqmlengine_p.h>
46 #include <private/qqmlcontext_p.h>
47
48 #include <private/qjsvalue_p.h>
49 #include <private/qscript_impl_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 class QV8TypeResource : public QV8ObjectResource
54 {
55     V8_RESOURCE_TYPE(TypeType);
56
57 public:
58     QV8TypeResource(QV8Engine *engine);
59     virtual ~QV8TypeResource();
60
61     QV8TypeWrapper::TypeNameMode mode;
62
63     QQmlGuard<QObject> object;
64
65     QQmlType *type;
66     QQmlTypeNameCache *typeNamespace;
67     const void *importNamespace;
68 };
69
70 QV8TypeResource::QV8TypeResource(QV8Engine *engine)
71 : QV8ObjectResource(engine), mode(QV8TypeWrapper::IncludeEnums), type(0), typeNamespace(0), importNamespace(0)
72 {
73 }
74
75 QV8TypeResource::~QV8TypeResource()
76 {
77     if (typeNamespace) typeNamespace->release();
78 }
79
80 QV8TypeWrapper::QV8TypeWrapper()
81 : m_engine(0)
82 {
83 }
84
85 QV8TypeWrapper::~QV8TypeWrapper()
86 {
87 }
88
89 void QV8TypeWrapper::destroy()
90 {
91     qPersistentDispose(m_constructor);
92 }
93
94 void QV8TypeWrapper::init(QV8Engine *engine)
95 {
96     m_engine = engine;
97     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
98     ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter);
99     ft->InstanceTemplate()->SetHasExternalResource(true);
100     m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
101 }
102
103 // Returns a type wrapper for type t on o.  This allows access of enums, and attached properties.
104 v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QQmlType *t, TypeNameMode mode)
105 {
106     Q_ASSERT(t);
107     // XXX NewInstance() should be optimized
108     v8::Local<v8::Object> rv = m_constructor->NewInstance(); 
109     QV8TypeResource *r = new QV8TypeResource(m_engine);
110     r->mode = mode; r->object = o; r->type = t;
111     rv->SetExternalResource(r);
112     return rv;
113 }
114
115 // Returns a type wrapper for importNamespace (of t) on o.  This allows nested resolution of a type in a 
116 // namespace.
117 v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QQmlTypeNameCache *t, 
118                                                 const void *importNamespace, TypeNameMode mode)
119 {
120     Q_ASSERT(t);
121     Q_ASSERT(importNamespace);
122     // XXX NewInstance() should be optimized
123     v8::Local<v8::Object> rv = m_constructor->NewInstance(); 
124     QV8TypeResource *r = new QV8TypeResource(m_engine);
125     t->addref();
126     r->mode = mode; r->object = o; r->typeNamespace = t; r->importNamespace = importNamespace;
127     rv->SetExternalResource(r);
128     return rv;
129 }
130
131 QVariant QV8TypeWrapper::toVariant(QV8ObjectResource *r)
132 {
133     Q_ASSERT(r->resourceType() == QV8ObjectResource::TypeType);
134     QV8TypeResource *resource = static_cast<QV8TypeResource *>(r);
135     QV8Engine *v8engine = resource->engine;
136
137     if (resource->type && resource->type->isSingleton()) {
138         QQmlEngine *e = v8engine->engine();
139         QQmlType::SingletonInstanceInfo *siinfo = resource->type->singletonInstanceInfo();
140         siinfo->init(e); // note: this will also create QJSValue singleton which isn't strictly required.
141         QObject *qobjectSingleton = siinfo->qobjectApi(e);
142         if (qobjectSingleton) {
143             return QVariant::fromValue<QObject*>(qobjectSingleton);
144         }
145     }
146
147     // only QObject Singleton Type can be converted to a variant.
148     return QVariant();
149 }
150
151 v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, 
152                                              const v8::AccessorInfo &info)
153 {
154     QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This());
155
156     if (!resource) 
157         return v8::Undefined();
158
159     QV8Engine *v8engine = resource->engine;
160     QQmlContextData *context = v8engine->callingContext();
161
162     QObject *object = resource->object;
163
164     QHashedV8String propertystring(property);
165
166     if (resource->type) {
167         QQmlType *type = resource->type;
168
169         // singleton types are handled differently to other types.
170         if (type->isSingleton()) {
171             QQmlEngine *e = v8engine->engine();
172             QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo();
173             siinfo->init(e);
174
175             QObject *qobjectSingleton = siinfo->qobjectApi(e);
176             if (qobjectSingleton) {
177                 // check for enum value
178                 if (QV8Engine::startsWithUpper(property)) {
179                     if (resource->mode == IncludeEnums) {
180                         QString name = v8engine->toString(property);
181
182                         // ### Optimize
183                         QByteArray enumName = name.toUtf8();
184                         const QMetaObject *metaObject = qobjectSingleton->metaObject();
185                         for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) {
186                             QMetaEnum e = metaObject->enumerator(ii);
187                             bool ok;
188                             int value = e.keyToValue(enumName.constData(), &ok);
189                             if (ok)
190                                 return v8::Integer::New(value);
191                         }
192                     }
193                 }
194
195                 // check for property.
196                 v8::Handle<v8::Value> rv = v8engine->qobjectWrapper()->getProperty(qobjectSingleton, propertystring, context, QV8QObjectWrapper::IgnoreRevision);
197                 return rv;
198             } else if (!siinfo->scriptApi(e).isUndefined()) {
199                 // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
200                 QJSValuePrivate *apiprivate = QJSValuePrivate::get(siinfo->scriptApi(e));
201                 QScopedPointer<QJSValuePrivate> propertyValue(apiprivate->property(property).give());
202                 return propertyValue->asV8Value(v8engine);
203             }
204
205             // Fall through to return empty handle
206
207         } else {
208
209             if (QV8Engine::startsWithUpper(property)) {
210                 bool ok = false;
211                 int value = type->enumValue(propertystring, &ok);
212                 if (ok)
213                     return v8::Integer::New(value);
214
215                 // Fall through to return empty handle
216
217             } else if (resource->object) {
218                 QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object);
219                 if (ao)
220                     return v8engine->qobjectWrapper()->getProperty(ao, propertystring, context,
221                                                                    QV8QObjectWrapper::IgnoreRevision);
222
223                 // Fall through to return empty handle
224             }
225
226             // Fall through to return empty handle
227         }
228
229         // Fall through to return empty handle
230
231     } else if (resource->typeNamespace) {
232         Q_ASSERT(resource->importNamespace);
233         QQmlTypeNameCache::Result r = resource->typeNamespace->query(propertystring,
234                                                                              resource->importNamespace);
235
236         if (r.isValid()) {
237             QQmlContextData *context = v8engine->callingContext();
238             if (r.type) {
239                 return v8engine->typeWrapper()->newObject(object, r.type, resource->mode);
240             } else if (r.scriptIndex != -1) {
241                 int index = r.scriptIndex;
242                 if (index < context->importedScripts.count())
243                     return context->importedScripts.at(index);
244             } else if (r.importNamespace) {
245                 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace);
246             }
247
248             return v8::Undefined();
249
250         }
251
252         // Fall through to return empty handle
253
254     } else {
255         Q_ASSERT(!"Unreachable");
256     }
257
258     return v8::Handle<v8::Value>();
259 }
260
261 v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property, 
262                                              v8::Local<v8::Value> value,
263                                              const v8::AccessorInfo &info)
264 {
265     QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This());
266
267     if (!resource) 
268         return value;
269
270     QV8Engine *v8engine = resource->engine;
271     QQmlContextData *context = v8engine->callingContext();
272
273     QHashedV8String propertystring(property);
274
275     QQmlType *type = resource->type;
276     if (type && !type->isSingleton() && resource->object) {
277         QObject *object = resource->object;
278         QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object);
279         if (ao) 
280             v8engine->qobjectWrapper()->setProperty(ao, propertystring, context, value,
281                                                     QV8QObjectWrapper::IgnoreRevision);
282     } else if (type && type->isSingleton()) {
283         QQmlEngine *e = v8engine->engine();
284         QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo();
285         siinfo->init(e);
286
287         QObject *qobjectSingleton = siinfo->qobjectApi(e);
288         if (qobjectSingleton) {
289             v8engine->qobjectWrapper()->setProperty(qobjectSingleton, propertystring, context, value,
290                                                     QV8QObjectWrapper::IgnoreRevision);
291         } else if (!siinfo->scriptApi(e).isUndefined()) {
292             QScopedPointer<QJSValuePrivate> setvalp(new QJSValuePrivate(v8engine, value));
293             QJSValuePrivate *apiprivate = QJSValuePrivate::get(siinfo->scriptApi(e));
294             if (apiprivate->propertyFlags(property) & QJSValuePrivate::ReadOnly) {
295                 QString error = QLatin1String("Cannot assign to read-only property \"") +
296                                 v8engine->toString(property) + QLatin1Char('\"');
297                 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
298             } else {
299                 apiprivate->setProperty(property, setvalp.data());
300             }
301         }
302     }
303
304     return value;
305 }
306
307 QT_END_NAMESPACE