1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qv8contextwrapper_p.h"
43 #include "qv8engine_p.h"
45 #include <private/qdeclarativeengine_p.h>
46 #include <private/qdeclarativecontext_p.h>
50 static QString internal(QLatin1String("You've stumbled onto an internal implementation detail "
51 "that should never have been exposed."));
53 class QV8ContextResource : public QV8ObjectResource
55 V8_RESOURCE_TYPE(ContextType);
58 QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject);
59 ~QV8ContextResource();
61 inline QDeclarativeContextData *getContext() const;
62 inline QObject *getScopeObject() const;
64 quint32 isSharedContext:1;
65 quint32 hasSubContexts:1;
69 QObject *secondaryScope;
71 // This is a pretty horrible hack, and an abuse of external strings. When we create a
72 // sub-context (a context created by a Qt.include() in an external javascript file),
73 // we pass a specially crafted SubContext external string as the v8::Script::Data() to
74 // the script, which contains a pointer to the context. We can then access the
75 // v8::Script::Data() later on to resolve names and URLs against the sub-context instead
76 // of the main outer context.
77 struct SubContext : public v8::String::ExternalStringResource {
78 SubContext(QDeclarativeContextData *context) : context(context) {}
79 QDeclarativeGuardedContextData context;
81 virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); }
82 virtual size_t length() const { return internal.length(); }
86 QDeclarativeGuardedContextData context;
87 QDeclarativeGuard<QObject> scopeObject;
91 QV8ContextResource::QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject)
92 : QV8ObjectResource(engine), isSharedContext(false), hasSubContexts(false), readOnly(true),
93 secondaryScope(0), context(context), scopeObject(scopeObject)
97 QV8ContextResource::~QV8ContextResource()
99 if (context && context->isJSContext)
103 // Returns the scope object
104 QObject *QV8ContextResource::getScopeObject() const
107 return QDeclarativeEnginePrivate::get(engine->engine())->sharedScope;
112 // Returns the context, including resolving a subcontext
113 QDeclarativeContextData *QV8ContextResource::getContext() const
116 return QDeclarativeEnginePrivate::get(engine->engine())->sharedContext;
121 v8::Local<v8::Value> callingdata = v8::Context::GetCallingScriptData();
122 if (callingdata.IsEmpty() || !callingdata->IsString())
125 v8::Local<v8::String> callingstring = callingdata->ToString();
126 Q_ASSERT(callingstring->IsExternal());
127 Q_ASSERT(callingstring->GetExternalStringResource());
129 SubContext *sc = static_cast<SubContext *>(callingstring->GetExternalStringResource());
133 QV8ContextWrapper::QV8ContextWrapper()
138 QV8ContextWrapper::~QV8ContextWrapper()
142 void QV8ContextWrapper::destroy()
144 qPersistentDispose(m_sharedContext);
145 qPersistentDispose(m_urlConstructor);
146 qPersistentDispose(m_constructor);
149 void QV8ContextWrapper::init(QV8Engine *engine)
153 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
154 ft->InstanceTemplate()->SetHasExternalResource(true);
155 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter);
156 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
159 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
160 ft->InstanceTemplate()->SetHasExternalResource(true);
161 ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter);
162 m_urlConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
165 v8::Local<v8::Object> sharedContext = m_constructor->NewInstance();
166 QV8ContextResource *r = new QV8ContextResource(engine, 0, 0);
167 r->isSharedContext = true;
168 sharedContext->SetExternalResource(r);
169 m_sharedContext = qPersistentNew<v8::Object>(sharedContext);
173 v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QDeclarativeContextData *ctxt, QObject *scope)
175 // XXX NewInstance() should be optimized
176 v8::Local<v8::Object> rv = m_constructor->NewInstance();
177 QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope);
178 rv->SetExternalResource(r);
182 v8::Local<v8::Object> QV8ContextWrapper::urlScope(const QUrl &url)
184 QDeclarativeContextData *context = new QDeclarativeContextData;
186 context->isInternal = true;
187 context->isJSContext = true;
189 // XXX NewInstance() should be optimized
190 v8::Local<v8::Object> rv = m_urlConstructor->NewInstance();
191 QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0);
192 rv->SetExternalResource(r);
196 void QV8ContextWrapper::setReadOnly(v8::Handle<v8::Object> qmlglobal, bool readOnly)
198 QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
200 resource->readOnly = readOnly;
203 void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script> script,
204 QDeclarativeContextData *ctxt)
206 QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
208 resource->hasSubContexts = true;
209 script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt)));
212 QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle<v8::Object> ctxt, QObject *scope)
214 QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(ctxt);
215 if (!resource) return 0;
217 QObject *rv = resource->secondaryScope;
218 resource->secondaryScope = scope;
222 QDeclarativeContextData *QV8ContextWrapper::callingContext()
224 v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal();
225 if (qmlglobal.IsEmpty()) return 0;
227 QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
228 return r?r->getContext():0;
231 QDeclarativeContextData *QV8ContextWrapper::context(v8::Handle<v8::Value> value)
233 if (!value->IsObject())
236 v8::Handle<v8::Object> qmlglobal = v8::Handle<v8::Object>::Cast(value);
237 QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
238 return r?r->getContext():0;
241 v8::Handle<v8::Value> QV8ContextWrapper::NullGetter(v8::Local<v8::String> property,
242 const v8::AccessorInfo &info)
244 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
246 QV8Engine *engine = resource->engine;
248 QString error = QLatin1String("Can't find variable: ") + engine->toString(property);
249 v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error)));
250 return v8::Undefined();
253 v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property,
254 const v8::AccessorInfo &info)
256 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
258 // Its possible we could delay the calculation of the "actual" context (in the case
259 // of sub contexts) until it is definately needed.
260 QDeclarativeContextData *context = resource->getContext();
261 QDeclarativeContextData *expressionContext = context;
264 return v8::Undefined();
266 if (v8::Context::GetCallingQmlGlobal() != info.This())
267 return v8::Handle<v8::Value>();
269 // Search type (attached property/enum/imported scripts) names
270 // Secondary scope object
272 // Search context properties
273 // Search scope object
274 // Search context object
275 // context = context->parent
278 QV8Engine *engine = resource->engine;
280 QObject *scopeObject = resource->getScopeObject();
282 QHashedV8String propertystring(property);
284 if (context->imports && QV8Engine::startsWithUpper(property)) {
285 // Search for attached properties, enums and imported scripts
286 QDeclarativeTypeNameCache::Result r = context->imports->query(propertystring);
289 if (r.scriptIndex != -1) {
290 int index = r.scriptIndex;
291 if (index < context->importedScripts.count())
292 return context->importedScripts.at(index);
294 return v8::Undefined();
296 return engine->typeWrapper()->newObject(scopeObject, r.type);
297 } else if (r.importNamespace) {
298 return engine->typeWrapper()->newObject(scopeObject, context->imports, r.importNamespace);
300 Q_ASSERT(!"Unreachable");
306 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
307 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
309 if (resource->secondaryScope) {
310 v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, propertystring,
311 QV8QObjectWrapper::IgnoreRevision);
312 if (!result.IsEmpty()) return result;
316 // Search context properties
317 if (context->propertyNames) {
318 int propertyIdx = context->propertyNames->value(propertystring);
320 if (propertyIdx != -1) {
322 if (propertyIdx < context->idValueCount) {
324 ep->captureProperty(&context->idValues[propertyIdx].bindings);
325 return engine->newQObject(context->idValues[propertyIdx]);
328 QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate();
330 ep->captureProperty(context->asQDeclarativeContext(), -1,
331 propertyIdx + cp->notifyIndex);
333 const QVariant &value = cp->propertyValues.at(propertyIdx);
334 if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
335 QDeclarativeListProperty<QObject> prop(context->asQDeclarativeContext(), (void*)propertyIdx,
337 QDeclarativeContextPrivate::context_count,
338 QDeclarativeContextPrivate::context_at);
339 return engine->listWrapper()->newList(prop, qMetaTypeId<QDeclarativeListProperty<QObject> >());
341 return engine->fromVariant(cp->propertyValues.at(propertyIdx));
347 // Search scope object
349 v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, propertystring,
350 QV8QObjectWrapper::CheckRevision);
351 if (!result.IsEmpty()) return result;
356 // Search context object
357 if (context->contextObject) {
358 v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, propertystring,
359 QV8QObjectWrapper::CheckRevision);
360 if (!result.IsEmpty()) return result;
363 context = context->parent;
366 expressionContext->unresolvedNames = true;
368 QString error = QLatin1String("Can't find variable: ") + engine->toString(property);
369 v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error)));
370 return v8::Undefined();
373 v8::Handle<v8::Value> QV8ContextWrapper::NullSetter(v8::Local<v8::String> property,
374 v8::Local<v8::Value>,
375 const v8::AccessorInfo &info)
377 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
379 QV8Engine *engine = resource->engine;
381 if (!resource->readOnly) {
382 return v8::Handle<v8::Value>();
384 QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
386 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
387 return v8::Handle<v8::Value>();
391 v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property,
392 v8::Local<v8::Value> value,
393 const v8::AccessorInfo &info)
395 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
397 // Its possible we could delay the calculation of the "actual" context (in the case
398 // of sub contexts) until it is definately needed.
399 QDeclarativeContextData *context = resource->getContext();
400 QDeclarativeContextData *expressionContext = context;
403 return v8::Undefined();
405 if (v8::Context::GetCallingQmlGlobal() != info.This())
406 return v8::Handle<v8::Value>();
408 // See QV8ContextWrapper::Getter for resolution order
410 QV8Engine *engine = resource->engine;
411 QObject *scopeObject = resource->getScopeObject();
413 QHashedV8String propertystring(property);
415 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
417 // Search scope object
418 if (resource->secondaryScope &&
419 qobjectWrapper->setProperty(resource->secondaryScope, propertystring, value,
420 QV8QObjectWrapper::IgnoreRevision))
424 // Search context properties
425 if (context->propertyNames && -1 != context->propertyNames->value(propertystring))
428 // Search scope object
430 qobjectWrapper->setProperty(scopeObject, propertystring, value, QV8QObjectWrapper::CheckRevision))
434 // Search context object
435 if (context->contextObject &&
436 qobjectWrapper->setProperty(context->contextObject, propertystring, value,
437 QV8QObjectWrapper::CheckRevision))
440 context = context->parent;
443 expressionContext->unresolvedNames = true;
445 if (!resource->readOnly) {
446 return v8::Handle<v8::Value>();
448 QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
450 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
451 return v8::Undefined();