1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qv8contextwrapper_p.h"
43 #include "qv8engine_p.h"
45 #include <private/qqmlengine_p.h>
46 #include <private/qqmlcontext_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, QQmlContextData *context, QObject *scopeObject);
59 ~QV8ContextResource();
61 inline QQmlContextData *getContext() const;
62 inline QObject *getScopeObject() const;
64 quint32 isSharedContext:1;
65 quint32 hasSubContexts:1;
69 // This is a pretty horrible hack, and an abuse of external strings. When we create a
70 // sub-context (a context created by a Qt.include() in an external javascript file),
71 // we pass a specially crafted SubContext external string as the v8::Script::Data() to
72 // the script, which contains a pointer to the context. We can then access the
73 // v8::Script::Data() later on to resolve names and URLs against the sub-context instead
74 // of the main outer context.
75 struct SubContext : public v8::String::ExternalStringResource {
76 SubContext(QQmlContextData *context) : context(context) {}
77 QQmlGuardedContextData context;
79 virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); }
80 virtual size_t length() const { return internal.length(); }
84 QQmlGuardedContextData context;
85 QQmlGuard<QObject> scopeObject;
89 QV8ContextResource::QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject)
90 : QV8ObjectResource(engine), isSharedContext(false), hasSubContexts(false), readOnly(true),
91 context(context), scopeObject(scopeObject)
95 QV8ContextResource::~QV8ContextResource()
97 if (context && context->isJSContext)
101 // Returns the scope object
102 QObject *QV8ContextResource::getScopeObject() const
105 return QQmlEnginePrivate::get(engine->engine())->sharedScope;
110 // Returns the context, including resolving a subcontext
111 QQmlContextData *QV8ContextResource::getContext() const
114 return QQmlEnginePrivate::get(engine->engine())->sharedContext;
119 v8::Local<v8::Value> callingdata = v8::Context::GetCallingScriptData();
120 if (callingdata.IsEmpty() || !callingdata->IsString())
123 v8::Local<v8::String> callingstring = callingdata->ToString();
124 Q_ASSERT(callingstring->IsExternal());
125 Q_ASSERT(callingstring->GetExternalStringResource());
127 SubContext *sc = static_cast<SubContext *>(callingstring->GetExternalStringResource());
131 QV8ContextWrapper::QV8ContextWrapper()
136 QV8ContextWrapper::~QV8ContextWrapper()
140 void QV8ContextWrapper::destroy()
142 qPersistentDispose(m_sharedContext);
143 qPersistentDispose(m_urlConstructor);
144 qPersistentDispose(m_constructor);
147 void QV8ContextWrapper::init(QV8Engine *engine)
151 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
152 ft->InstanceTemplate()->SetHasExternalResource(true);
153 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter);
154 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
157 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
158 ft->InstanceTemplate()->SetHasExternalResource(true);
159 ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter);
160 m_urlConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
163 v8::Local<v8::Object> sharedContext = m_constructor->NewInstance();
164 QV8ContextResource *r = new QV8ContextResource(engine, 0, 0);
165 r->isSharedContext = true;
166 sharedContext->SetExternalResource(r);
167 m_sharedContext = qPersistentNew<v8::Object>(sharedContext);
171 v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QQmlContextData *ctxt, QObject *scope)
173 // XXX NewInstance() should be optimized
174 v8::Local<v8::Object> rv = m_constructor->NewInstance();
175 QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope);
176 rv->SetExternalResource(r);
180 v8::Local<v8::Object> QV8ContextWrapper::urlScope(const QUrl &url)
182 QQmlContextData *context = new QQmlContextData;
184 context->isInternal = true;
185 context->isJSContext = true;
187 // XXX NewInstance() should be optimized
188 v8::Local<v8::Object> rv = m_urlConstructor->NewInstance();
189 QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0);
190 rv->SetExternalResource(r);
194 void QV8ContextWrapper::setReadOnly(v8::Handle<v8::Object> qmlglobal, bool readOnly)
196 QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
198 resource->readOnly = readOnly;
201 void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script> script,
202 QQmlContextData *ctxt)
204 QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
206 resource->hasSubContexts = true;
207 script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt)));
210 QQmlContextData *QV8ContextWrapper::callingContext()
212 v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal();
213 if (qmlglobal.IsEmpty()) return 0;
215 QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
216 return r?r->getContext():0;
219 QQmlContextData *QV8ContextWrapper::context(v8::Handle<v8::Value> value)
221 if (!value->IsObject())
224 v8::Handle<v8::Object> qmlglobal = v8::Handle<v8::Object>::Cast(value);
225 QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
226 return r?r->getContext():0;
229 v8::Handle<v8::Value> QV8ContextWrapper::NullGetter(v8::Local<v8::String>,
230 const v8::AccessorInfo &)
232 // V8 will throw a ReferenceError if appropriate ("typeof" should not throw)
233 return v8::Handle<v8::Value>();
236 v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property,
237 const v8::AccessorInfo &info)
239 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
241 // Its possible we could delay the calculation of the "actual" context (in the case
242 // of sub contexts) until it is definately needed.
243 QQmlContextData *context = resource->getContext();
244 QQmlContextData *expressionContext = context;
247 return v8::Undefined();
249 if (v8::Context::GetCallingQmlGlobal() != info.This())
250 return v8::Handle<v8::Value>();
252 // Search type (attached property/enum/imported scripts) names
254 // Search context properties
255 // Search scope object
256 // Search context object
257 // context = context->parent
260 QV8Engine *engine = resource->engine;
262 QObject *scopeObject = resource->getScopeObject();
264 QHashedV8String propertystring(property);
266 if (context->imports && QV8Engine::startsWithUpper(property)) {
267 // Search for attached properties, enums and imported scripts
268 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
271 if (r.scriptIndex != -1) {
272 int index = r.scriptIndex;
273 if (index < context->importedScripts.count())
274 return context->importedScripts.at(index);
276 return v8::Undefined();
278 return engine->typeWrapper()->newObject(scopeObject, r.type);
279 } else if (r.importNamespace) {
280 return engine->typeWrapper()->newObject(scopeObject, context->imports, r.importNamespace);
282 Q_ASSERT(!"Unreachable");
288 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
289 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
292 // Search context properties
293 if (context->propertyNames) {
294 int propertyIdx = context->propertyNames->value(propertystring);
296 if (propertyIdx != -1) {
298 if (propertyIdx < context->idValueCount) {
300 ep->captureProperty(&context->idValues[propertyIdx].bindings);
301 return engine->newQObject(context->idValues[propertyIdx]);
304 QQmlContextPrivate *cp = context->asQQmlContextPrivate();
306 ep->captureProperty(context->asQQmlContext(), -1,
307 propertyIdx + cp->notifyIndex);
309 const QVariant &value = cp->propertyValues.at(propertyIdx);
310 if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
311 QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx),
313 QQmlContextPrivate::context_count,
314 QQmlContextPrivate::context_at);
315 return engine->listWrapper()->newList(prop, qMetaTypeId<QQmlListProperty<QObject> >());
317 return engine->fromVariant(cp->propertyValues.at(propertyIdx));
323 // Search scope object
325 v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, propertystring,
326 context, QV8QObjectWrapper::CheckRevision);
327 if (!result.IsEmpty()) return result;
332 // Search context object
333 if (context->contextObject) {
334 v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, propertystring,
335 context, QV8QObjectWrapper::CheckRevision);
336 if (!result.IsEmpty()) return result;
339 context = context->parent;
342 expressionContext->unresolvedNames = true;
344 // V8 will throw a ReferenceError if appropriate ("typeof" should not throw)
345 return v8::Handle<v8::Value>();
348 v8::Handle<v8::Value> QV8ContextWrapper::NullSetter(v8::Local<v8::String> property,
349 v8::Local<v8::Value>,
350 const v8::AccessorInfo &info)
352 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
354 QV8Engine *engine = resource->engine;
356 if (!resource->readOnly) {
357 return v8::Handle<v8::Value>();
359 QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
361 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
362 return v8::Handle<v8::Value>();
366 v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property,
367 v8::Local<v8::Value> value,
368 const v8::AccessorInfo &info)
370 QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This());
372 // Its possible we could delay the calculation of the "actual" context (in the case
373 // of sub contexts) until it is definately needed.
374 QQmlContextData *context = resource->getContext();
375 QQmlContextData *expressionContext = context;
378 return v8::Undefined();
380 if (v8::Context::GetCallingQmlGlobal() != info.This())
381 return v8::Handle<v8::Value>();
383 // See QV8ContextWrapper::Getter for resolution order
385 QV8Engine *engine = resource->engine;
386 QObject *scopeObject = resource->getScopeObject();
388 QHashedV8String propertystring(property);
390 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
393 // Search context properties
394 if (context->propertyNames && -1 != context->propertyNames->value(propertystring))
397 // Search scope object
399 qobjectWrapper->setProperty(scopeObject, propertystring, context, value, QV8QObjectWrapper::CheckRevision))
403 // Search context object
404 if (context->contextObject &&
405 qobjectWrapper->setProperty(context->contextObject, propertystring, context, value,
406 QV8QObjectWrapper::CheckRevision))
409 context = context->parent;
412 expressionContext->unresolvedNames = true;
414 if (!resource->readOnly) {
415 return v8::Handle<v8::Value>();
417 QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
419 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
420 return v8::Undefined();