Implement global handle tracking
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8contextwrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
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 QT_BEGIN_NAMESPACE
49
50 static QString internal(QLatin1String("You've stumbled onto an internal implementation detail "
51                                       "that should never have been exposed."));
52
53 class QV8ContextResource : public QV8ObjectResource
54 {
55     V8_RESOURCE_TYPE(ContextType);
56
57 public:
58     QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject);
59     ~QV8ContextResource();
60
61     inline QDeclarativeContextData *getContext() const;
62
63     QDeclarativeGuard<QObject> scopeObject;
64
65     quint32 hasSubContexts:1;
66     quint32 ownsContext:1;
67     quint32 readOnly:1;
68     quint32 dummy:29;
69
70     QObject *secondaryScope;
71
72     // XXX aakenned - this is somewhat of a horrible abuse of external strings :)
73     struct SubContext : public v8::String::ExternalStringResource {
74         SubContext(QDeclarativeContextData *context) : context(context) {}
75         QDeclarativeGuardedContextData context;
76
77         virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); }
78         virtual size_t length() const { return internal.length(); }
79     };
80
81 private:
82     QDeclarativeGuardedContextData context;
83 };
84
85 QV8ContextResource::QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject)
86 : QV8ObjectResource(engine), scopeObject(scopeObject), hasSubContexts(false), ownsContext(false),
87   readOnly(true), secondaryScope(0), context(context)
88 {
89 }
90
91 QV8ContextResource::~QV8ContextResource()
92 {
93     if (ownsContext && context)
94         context->destroy();
95 }
96
97 // Returns the context, including resolving a subcontext
98 QDeclarativeContextData *QV8ContextResource::getContext() const
99 {
100     if (!hasSubContexts)
101         return context;
102
103     v8::Local<v8::Value> callingdata = v8::Context::GetCallingScriptData();
104     if (callingdata.IsEmpty() || !callingdata->IsString())
105         return context;
106
107     v8::Local<v8::String> callingstring = callingdata->ToString();
108     Q_ASSERT(callingstring->IsExternal());
109     Q_ASSERT(callingstring->GetExternalStringResource());
110
111     SubContext *sc = static_cast<SubContext *>(callingstring->GetExternalStringResource());
112     return sc->context;
113 }
114
115 QV8ContextWrapper::QV8ContextWrapper()
116 : m_engine(0)
117 {
118 }
119
120 QV8ContextWrapper::~QV8ContextWrapper()
121 {
122 }
123
124 void QV8ContextWrapper::destroy()
125 {
126     qPersistentDispose(m_urlConstructor);
127     qPersistentDispose(m_constructor);
128 }
129
130 void QV8ContextWrapper::init(QV8Engine *engine)
131 {
132     m_engine = engine;
133     {
134     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
135     ft->InstanceTemplate()->SetHasExternalResource(true);
136     ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter);
137     m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
138     }
139     {
140     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
141     ft->InstanceTemplate()->SetHasExternalResource(true);
142     ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter);
143     m_urlConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
144     }
145 }
146
147 v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QDeclarativeContextData *ctxt, QObject *scope)
148 {
149     // XXX aakenned - NewInstance() is slow for our case
150     v8::Local<v8::Object> rv = m_constructor->NewInstance(); 
151     QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope);
152     rv->SetExternalResource(r);
153     return rv;
154 }
155
156 v8::Local<v8::Object> QV8ContextWrapper::urlScope(const QUrl &url)
157 {
158     QDeclarativeContextData *context = new QDeclarativeContextData;
159     context->url = url;
160     context->isInternal = true;
161     context->isJSContext = true;
162
163     // XXX aakenned - NewInstance() is slow for our case
164     v8::Local<v8::Object> rv = m_urlConstructor->NewInstance(); 
165     QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0);
166     r->ownsContext = true;
167     rv->SetExternalResource(r);
168     return rv;
169 }
170
171 void QV8ContextWrapper::setReadOnly(v8::Handle<v8::Object> qmlglobal, bool readOnly)
172 {
173     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
174     Q_ASSERT(resource);
175     resource->readOnly = readOnly;
176 }
177
178 void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script> script, 
179                                       QDeclarativeContextData *ctxt)
180 {
181     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
182     Q_ASSERT(resource);
183     resource->hasSubContexts = true;
184     script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt)));
185 }
186
187 QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle<v8::Object> ctxt, QObject *scope)
188 {
189     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(ctxt);
190     if (!resource) return 0;
191
192     QObject *rv = resource->secondaryScope;
193     resource->secondaryScope = scope;
194     return rv;
195 }
196
197 QDeclarativeContextData *QV8ContextWrapper::callingContext()
198 {
199     v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal();
200     if (qmlglobal.IsEmpty()) return 0;
201
202     QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
203     return r?r->getContext():0;
204 }
205
206 QDeclarativeContextData *QV8ContextWrapper::context(v8::Handle<v8::Value> value)
207 {
208     if (!value->IsObject())
209         return 0;
210
211     v8::Handle<v8::Object> qmlglobal = v8::Handle<v8::Object>::Cast(value);
212     QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
213     return r?r->getContext():0;
214 }
215
216 v8::Handle<v8::Value> QV8ContextWrapper::NullGetter(v8::Local<v8::String> property, 
217                                                     const v8::AccessorInfo &info)
218 {
219     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
220
221     if (!resource)
222         return v8::Undefined(); // XXX Should we throw here?
223
224     QV8Engine *engine = resource->engine;
225
226     QString error = QLatin1String("Can't find variable: ") + engine->toString(property);
227     v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error)));
228     return v8::Undefined();
229 }
230
231 v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property, 
232                                                 const v8::AccessorInfo &info)
233 {
234     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
235
236     if (!resource)
237         return v8::Undefined(); // XXX Should we throw here?
238
239     // XXX aakenned too agressive
240     QDeclarativeContextData *context = resource->getContext();
241
242     if (!context)
243         return v8::Undefined(); // XXX Should we throw here?
244
245     // Search type (attached property/enum/imported scripts) names
246     // Secondary scope object
247     // while (context) {
248     //     Search context properties
249     //     Search scope object
250     //     Search context object
251     //     context = context->parent
252     // }
253
254     QV8Engine *engine = resource->engine;
255     QObject *scopeObject = resource->scopeObject;
256
257     if (context->imports && QV8Engine::startsWithUpper(property)) {
258         // Search for attached properties, enums and imported scripts
259         QDeclarativeTypeNameCache::Data *data = context->imports->data(property);
260
261         if (data) {
262             if (data->importedScriptIndex != -1) {
263                 int index = data->importedScriptIndex;
264                 if (index < context->importedScripts.count())
265                     return context->importedScripts.at(index);
266                 else
267                     return v8::Undefined();
268             } else if (data->type) {
269                 return engine->typeWrapper()->newObject(scopeObject, data->type);
270             } else if (data->typeNamespace) {
271                 return engine->typeWrapper()->newObject(scopeObject, data->typeNamespace);
272             }
273             Q_ASSERT(!"Unreachable");
274         }
275
276         // Fall through
277     }
278
279     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
280     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
281
282     if (resource->secondaryScope) {
283         v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, property, 
284                                                                    QV8QObjectWrapper::IgnoreRevision);
285         if (!result.IsEmpty()) return result;
286     }
287
288     while (context) {
289         // Search context properties
290         if (context->propertyNames) {
291             int propertyIdx = context->propertyNames->value(property);
292
293             if (propertyIdx != -1) {
294                 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
295
296                 if (propertyIdx < context->idValueCount) {
297
298                     if (ep->captureProperties)
299                         ep->capturedProperties << CapturedProperty(&context->idValues[propertyIdx].bindings);
300
301                     return engine->newQObject(context->idValues[propertyIdx]);
302                 } else {
303
304                     QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate();
305
306                     if (ep->captureProperties)
307                         ep->capturedProperties << CapturedProperty(context->asQDeclarativeContext(), -1, 
308                                                                    propertyIdx + cp->notifyIndex);
309
310                     const QVariant &value = cp->propertyValues.at(propertyIdx);
311                     if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
312                         QDeclarativeListProperty<QObject> prop(context->asQDeclarativeContext(), (void*)propertyIdx,
313                                                                0,
314                                                                QDeclarativeContextPrivate::context_count,
315                                                                QDeclarativeContextPrivate::context_at);
316                         return engine->listWrapper()->newList(prop, qMetaTypeId<QDeclarativeListProperty<QObject> >());
317                     } else {
318                         return engine->fromVariant(cp->propertyValues.at(propertyIdx));
319                     }
320                 }
321             }
322         }
323
324         // Search scope object
325         if (scopeObject) {
326             v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, property, 
327                                                                        QV8QObjectWrapper::CheckRevision);
328             if (!result.IsEmpty()) return result;
329         }
330         scopeObject = 0;
331
332
333         // Search context object
334         if (context->contextObject) {
335             v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, property,
336                                                                        QV8QObjectWrapper::CheckRevision);
337             if (!result.IsEmpty()) return result;
338         }
339
340         context = context->parent;
341     }
342
343     QString error = QLatin1String("Can't find variable: ") + engine->toString(property);
344     v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error)));
345     return v8::Undefined();
346 }
347
348 v8::Handle<v8::Value> QV8ContextWrapper::NullSetter(v8::Local<v8::String> property, 
349                                                     v8::Local<v8::Value>,
350                                                     const v8::AccessorInfo &info)
351 {
352     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
353
354     if (!resource)
355         return v8::Undefined(); // XXX Should we throw here?
356
357     QV8Engine *engine = resource->engine;
358
359     if (!resource->readOnly) {
360         return v8::Handle<v8::Value>();
361     } else {
362         QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + 
363                         QLatin1String("\"");
364         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
365         return v8::Undefined();
366     }
367 }
368
369 v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property, 
370                                                 v8::Local<v8::Value> value,
371                                                 const v8::AccessorInfo &info)
372 {
373     QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
374
375     if (!resource)
376         return v8::Undefined(); // XXX Should we throw here?
377
378     // XXX aakenned too agressive
379     QDeclarativeContextData *context = resource->getContext();
380
381     if (!context)
382         return v8::Undefined(); // XXX Should we throw here?
383
384     // See QV8ContextWrapper::Getter for resolution order
385
386     QV8Engine *engine = resource->engine;
387     QObject *scopeObject = resource->scopeObject;
388
389     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
390
391     // Search scope object
392     if (resource->secondaryScope && qobjectWrapper->setProperty(resource->secondaryScope, property, value, 
393                                                                 QV8QObjectWrapper::IgnoreRevision))
394         return value;
395
396     while (context) {
397         // Search context properties
398         if (context->propertyNames && -1 != context->propertyNames->value(property))
399             return value;
400
401         // Search scope object
402         if (scopeObject && 
403             qobjectWrapper->setProperty(scopeObject, property, value, QV8QObjectWrapper::CheckRevision))
404             return value;
405         scopeObject = 0;
406
407         // Search context object
408         if (context->contextObject &&
409             qobjectWrapper->setProperty(context->contextObject, property, value, QV8QObjectWrapper::CheckRevision))
410             return value;
411
412         context = context->parent;
413     }
414
415     if (!resource->readOnly) {
416         return v8::Handle<v8::Value>();
417     } else {
418         QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + 
419                         QLatin1String("\"");
420         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
421         return v8::Undefined();
422     }
423 }
424
425 QT_END_NAMESPACE