f1c095678b1c1defa8527d4a126c779e1b6191f9
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8typewrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8contextwrapper_p.h"
43 #include "qv8engine_p.h"
44
45 #include <private/qdeclarativeengine_p.h>
46 #include <private/qdeclarativecontext_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     QDeclarativeGuard<QObject> object;
64
65     QDeclarativeType *type;
66     QDeclarativeTypeNameCache *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, QDeclarativeType *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, QDeclarativeTypeNameCache *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->typeNamespace) {
138         if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) {
139             if (moduleApi->scriptCallback) {
140                 moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine());
141                 moduleApi->scriptCallback = 0;
142                 moduleApi->qobjectCallback = 0;
143             } else if (moduleApi->qobjectCallback) {
144                 moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine());
145                 moduleApi->scriptCallback = 0;
146                 moduleApi->qobjectCallback = 0;
147             }
148
149             if (moduleApi->qobjectApi) {
150                 return QVariant::fromValue<QObject*>(moduleApi->qobjectApi);
151             }
152         }
153     }
154
155     // only QObject Module API can be converted to a variant.
156     return QVariant();
157 }
158
159 v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, 
160                                              const v8::AccessorInfo &info)
161 {
162     QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This());
163
164     if (!resource) 
165         return v8::Undefined();
166
167     QV8Engine *v8engine = resource->engine;
168     QObject *object = resource->object;
169
170     QHashedV8String propertystring(property);
171
172     if (resource->type) {
173         QDeclarativeType *type = resource->type;
174
175         if (QV8Engine::startsWithUpper(property)) {
176             int value = type->enumValue(propertystring);
177             if (-1 != value)
178                 return v8::Integer::New(value);
179
180             // Fall through to return empty handle
181
182         } else if (resource->object) {
183             QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object);
184             if (ao) 
185                 return v8engine->qobjectWrapper()->getProperty(ao, propertystring, 
186                                                                QV8QObjectWrapper::IgnoreRevision);
187
188             // Fall through to return empty handle
189         }
190
191         // Fall through to return empty handle
192
193     } else if (resource->typeNamespace) {
194         Q_ASSERT(resource->importNamespace);
195         QDeclarativeTypeNameCache::Result r = resource->typeNamespace->query(propertystring,
196                                                                              resource->importNamespace);
197
198         if (r.isValid()) {
199             if (r.type) {
200                 return v8engine->typeWrapper()->newObject(object, r.type, resource->mode);
201             } else if (r.scriptIndex != -1) {
202                 int index = r.scriptIndex;
203                 QDeclarativeContextData *context = v8engine->callingContext();
204                 if (index < context->importedScripts.count())
205                     return context->importedScripts.at(index);
206             }
207
208             return v8::Undefined();
209         } else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) {
210
211             if (moduleApi->scriptCallback) {
212                 moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine());
213                 moduleApi->scriptCallback = 0;
214                 moduleApi->qobjectCallback = 0;
215             } else if (moduleApi->qobjectCallback) {
216                 moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine());
217                 moduleApi->scriptCallback = 0;
218                 moduleApi->qobjectCallback = 0;
219             }
220
221             if (moduleApi->qobjectApi) {
222                 // check for enum value
223                 if (QV8Engine::startsWithUpper(property)) {
224                     if (resource->mode == IncludeEnums) {
225                         QString name = v8engine->toString(property);
226
227                         // ### Optimize
228                         QByteArray enumName = name.toUtf8();
229                         const QMetaObject *metaObject = moduleApi->qobjectApi->metaObject();
230                         for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) {
231                             QMetaEnum e = metaObject->enumerator(ii);
232                             bool ok;
233                             int value = e.keyToValue(enumName.constData(), &ok);
234                             if (ok)
235                                 return v8::Integer::New(value);
236                         }
237                     }
238                 }
239
240                 // check for property.
241                 v8::Handle<v8::Value> rv = v8engine->qobjectWrapper()->getProperty(moduleApi->qobjectApi, propertystring, QV8QObjectWrapper::IgnoreRevision);
242                 return rv;
243             } else if (moduleApi->scriptApi.isValid()) {
244                 // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
245                 QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi);
246                 QScopedPointer<QJSValuePrivate> propertyValue(apiprivate->property(property).give());
247                 return propertyValue->asV8Value(v8engine);
248             } else {
249                 return v8::Handle<v8::Value>();
250             }
251         }
252
253         // Fall through to return empty handle
254
255     } else {
256         Q_ASSERT(!"Unreachable");
257     }
258
259     return v8::Handle<v8::Value>();
260 }
261
262 v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property, 
263                                              v8::Local<v8::Value> value,
264                                              const v8::AccessorInfo &info)
265 {
266     QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This());
267
268     if (!resource) 
269         return value;
270
271     QV8Engine *v8engine = resource->engine;
272
273     QHashedV8String propertystring(property);
274
275     if (resource->type && resource->object) {
276         QDeclarativeType *type = resource->type;
277         QObject *object = resource->object;
278         QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object);
279         if (ao) 
280             v8engine->qobjectWrapper()->setProperty(ao, propertystring, value, 
281                                                     QV8QObjectWrapper::IgnoreRevision);
282     } else if (resource->typeNamespace) {
283         if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) {
284             if (moduleApi->scriptCallback) {
285                 moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine());
286                 moduleApi->scriptCallback = 0;
287                 moduleApi->qobjectCallback = 0;
288             } else if (moduleApi->qobjectCallback) {
289                 moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine());
290                 moduleApi->scriptCallback = 0;
291                 moduleApi->qobjectCallback = 0;
292             }
293
294             if (moduleApi->qobjectApi) {
295                 v8engine->qobjectWrapper()->setProperty(moduleApi->qobjectApi, propertystring, value, 
296                                                         QV8QObjectWrapper::IgnoreRevision);
297             } else if (moduleApi->scriptApi.isValid()) {
298                 QScopedPointer<QJSValuePrivate> setvalp(new QJSValuePrivate(v8engine, value));
299                 QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi);
300                 if (apiprivate->propertyFlags(property) & QJSValue::ReadOnly) {
301                     QString error = QLatin1String("Cannot assign to read-only property \"") +
302                                     v8engine->toString(property) + QLatin1Char('\"');
303                     v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
304                 } else {
305                     apiprivate->setProperty(property, setvalp.data());
306                 }
307             }
308         }
309     }
310
311     return value;
312 }
313
314 QT_END_NAMESPACE