aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/v8/qv8contextwrapper.cpp
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2011-05-11 17:20:40 +1000
committerAaron Kennedy <aaron.kennedy@nokia.com>2011-06-06 11:50:48 +1000
commit6b54de600ce74025bc8ada20bea95ad183a6cd8d (patch)
tree6736888525cd8cd8c2d30bb7b87b3249b74839a5 /src/declarative/qml/v8/qv8contextwrapper.cpp
parent6dbd4286eb19e9ac45665046a43342bcdc8b127e (diff)
Initial V8 integration
Diffstat (limited to 'src/declarative/qml/v8/qv8contextwrapper.cpp')
-rw-r--r--src/declarative/qml/v8/qv8contextwrapper.cpp425
1 files changed, 425 insertions, 0 deletions
diff --git a/src/declarative/qml/v8/qv8contextwrapper.cpp b/src/declarative/qml/v8/qv8contextwrapper.cpp
new file mode 100644
index 0000000000..1af4b4c883
--- /dev/null
+++ b/src/declarative/qml/v8/qv8contextwrapper.cpp
@@ -0,0 +1,425 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv8contextwrapper_p.h"
+#include "qv8engine_p.h"
+
+#include <private/qdeclarativeengine_p.h>
+#include <private/qdeclarativecontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static QString internal(QLatin1String("You've stumbled onto an internal implementation detail "
+ "that should never have been exposed."));
+
+class QV8ContextResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(ContextType);
+
+public:
+ QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject);
+ ~QV8ContextResource();
+
+ inline QDeclarativeContextData *getContext() const;
+
+ QDeclarativeGuard<QObject> scopeObject;
+
+ quint32 hasSubContexts:1;
+ quint32 ownsContext:1;
+ quint32 readOnly:1;
+ quint32 dummy:29;
+
+ QObject *secondaryScope;
+
+ // XXX aakenned - this is somewhat of a horrible abuse of external strings :)
+ struct SubContext : public v8::String::ExternalStringResource {
+ SubContext(QDeclarativeContextData *context) : context(context) {}
+ QDeclarativeGuardedContextData context;
+
+ virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); }
+ virtual size_t length() const { return internal.length(); }
+ };
+
+private:
+ QDeclarativeGuardedContextData context;
+};
+
+QV8ContextResource::QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject)
+: QV8ObjectResource(engine), scopeObject(scopeObject), hasSubContexts(false), ownsContext(false),
+ readOnly(true), secondaryScope(0), context(context)
+{
+}
+
+QV8ContextResource::~QV8ContextResource()
+{
+ if (ownsContext && context)
+ context->destroy();
+}
+
+// Returns the context, including resolving a subcontext
+QDeclarativeContextData *QV8ContextResource::getContext() const
+{
+ if (!hasSubContexts)
+ return context;
+
+ v8::Local<v8::Value> callingdata = v8::Context::GetCallingScriptData();
+ if (callingdata.IsEmpty() || !callingdata->IsString())
+ return context;
+
+ v8::Local<v8::String> callingstring = callingdata->ToString();
+ Q_ASSERT(callingstring->IsExternal());
+ Q_ASSERT(callingstring->GetExternalStringResource());
+
+ SubContext *sc = static_cast<SubContext *>(callingstring->GetExternalStringResource());
+ return sc->context;
+}
+
+QV8ContextWrapper::QV8ContextWrapper()
+: m_engine(0)
+{
+}
+
+QV8ContextWrapper::~QV8ContextWrapper()
+{
+}
+
+void QV8ContextWrapper::destroy()
+{
+ m_urlConstructor.Dispose(); m_urlConstructor.Clear();
+ m_constructor.Dispose(); m_constructor.Clear();
+}
+
+void QV8ContextWrapper::init(QV8Engine *engine)
+{
+ m_engine = engine;
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter);
+ m_constructor = v8::Persistent<v8::Function>::New(ft->GetFunction());
+ }
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter);
+ m_urlConstructor = v8::Persistent<v8::Function>::New(ft->GetFunction());
+ }
+}
+
+v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QDeclarativeContextData *ctxt, QObject *scope)
+{
+ // XXX aakenned - NewInstance() is slow for our case
+ v8::Local<v8::Object> rv = m_constructor->NewInstance();
+ QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope);
+ rv->SetExternalResource(r);
+ return rv;
+}
+
+v8::Local<v8::Object> QV8ContextWrapper::urlScope(const QUrl &url)
+{
+ QDeclarativeContextData *context = new QDeclarativeContextData;
+ context->url = url;
+ context->isInternal = true;
+ context->isJSContext = true;
+
+ // XXX aakenned - NewInstance() is slow for our case
+ v8::Local<v8::Object> rv = m_urlConstructor->NewInstance();
+ QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0);
+ r->ownsContext = true;
+ rv->SetExternalResource(r);
+ return rv;
+}
+
+void QV8ContextWrapper::setReadOnly(v8::Handle<v8::Object> qmlglobal, bool readOnly)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
+ Q_ASSERT(resource);
+ resource->readOnly = readOnly;
+}
+
+void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script> script,
+ QDeclarativeContextData *ctxt)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal);
+ Q_ASSERT(resource);
+ resource->hasSubContexts = true;
+ script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt)));
+}
+
+QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle<v8::Object> ctxt, QObject *scope)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(ctxt);
+ if (!resource) return 0;
+
+ QObject *rv = resource->secondaryScope;
+ resource->secondaryScope = scope;
+ return rv;
+}
+
+QDeclarativeContextData *QV8ContextWrapper::callingContext()
+{
+ v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal();
+ if (qmlglobal.IsEmpty()) return 0;
+
+ QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
+ return r?r->getContext():0;
+}
+
+QDeclarativeContextData *QV8ContextWrapper::context(v8::Handle<v8::Value> value)
+{
+ if (!value->IsObject())
+ return 0;
+
+ v8::Handle<v8::Object> qmlglobal = v8::Handle<v8::Object>::Cast(value);
+ QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal);
+ return r?r->getContext():0;
+}
+
+v8::Handle<v8::Value> QV8ContextWrapper::NullGetter(v8::Local<v8::String> property,
+ const v8::AccessorInfo &info)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
+
+ if (!resource)
+ return v8::Undefined(); // XXX Should we throw here?
+
+ QV8Engine *engine = resource->engine;
+
+ QString error = QLatin1String("Can't find variable: ") + engine->toString(property);
+ v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error)));
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property,
+ const v8::AccessorInfo &info)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
+
+ if (!resource)
+ return v8::Undefined(); // XXX Should we throw here?
+
+ // XXX aakenned too agressive
+ QDeclarativeContextData *context = resource->getContext();
+
+ if (!context)
+ return v8::Undefined(); // XXX Should we throw here?
+
+ // Search type (attached property/enum/imported scripts) names
+ // Secondary scope object
+ // while (context) {
+ // Search context properties
+ // Search scope object
+ // Search context object
+ // context = context->parent
+ // }
+
+ QV8Engine *engine = resource->engine;
+ QObject *scopeObject = resource->scopeObject;
+
+ if (context->imports && QV8Engine::startsWithUpper(property)) {
+ // Search for attached properties, enums and imported scripts
+ QDeclarativeTypeNameCache::Data *data = context->imports->data(property);
+
+ if (data) {
+ if (data->importedScriptIndex != -1) {
+ int index = data->importedScriptIndex;
+ if (index < context->importedScripts.count())
+ return context->importedScripts.at(index);
+ else
+ return v8::Undefined();
+ } else if (data->type) {
+ return engine->typeWrapper()->newObject(scopeObject, data->type);
+ } else if (data->typeNamespace) {
+ return engine->typeWrapper()->newObject(scopeObject, data->typeNamespace);
+ }
+ Q_ASSERT(!"Unreachable");
+ }
+
+ // Fall through
+ }
+
+ QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
+ QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
+
+ if (resource->secondaryScope) {
+ v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, property,
+ QV8QObjectWrapper::IgnoreRevision);
+ if (!result.IsEmpty()) return result;
+ }
+
+ while (context) {
+ // Search context properties
+ if (context->propertyNames) {
+ int propertyIdx = context->propertyNames->value(property);
+
+ if (propertyIdx != -1) {
+ typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
+
+ if (propertyIdx < context->idValueCount) {
+
+ if (ep->captureProperties)
+ ep->capturedProperties << CapturedProperty(&context->idValues[propertyIdx].bindings);
+
+ return engine->newQObject(context->idValues[propertyIdx]);
+ } else {
+
+ QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate();
+
+ if (ep->captureProperties)
+ ep->capturedProperties << CapturedProperty(context->asQDeclarativeContext(), -1,
+ propertyIdx + cp->notifyIndex);
+
+ const QVariant &value = cp->propertyValues.at(propertyIdx);
+ if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
+ QDeclarativeListProperty<QObject> prop(context->asQDeclarativeContext(), (void*)propertyIdx,
+ 0,
+ QDeclarativeContextPrivate::context_count,
+ QDeclarativeContextPrivate::context_at);
+ return engine->listWrapper()->newList(prop, qMetaTypeId<QDeclarativeListProperty<QObject> >());
+ } else {
+ return engine->fromVariant(cp->propertyValues.at(propertyIdx));
+ }
+ }
+ }
+ }
+
+ // Search scope object
+ if (scopeObject) {
+ v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, property,
+ QV8QObjectWrapper::CheckRevision);
+ if (!result.IsEmpty()) return result;
+ }
+ scopeObject = 0;
+
+
+ // Search context object
+ if (context->contextObject) {
+ v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, property,
+ QV8QObjectWrapper::CheckRevision);
+ if (!result.IsEmpty()) return result;
+ }
+
+ context = context->parent;
+ }
+
+ QString error = QLatin1String("Can't find variable: ") + engine->toString(property);
+ v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error)));
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> QV8ContextWrapper::NullSetter(v8::Local<v8::String> property,
+ v8::Local<v8::Value>,
+ const v8::AccessorInfo &info)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
+
+ if (!resource)
+ return v8::Undefined(); // XXX Should we throw here?
+
+ QV8Engine *engine = resource->engine;
+
+ if (!resource->readOnly) {
+ return v8::Handle<v8::Value>();
+ } else {
+ QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
+ QLatin1String("\"");
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return v8::Undefined();
+ }
+}
+
+v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo &info)
+{
+ QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This());
+
+ if (!resource)
+ return v8::Undefined(); // XXX Should we throw here?
+
+ // XXX aakenned too agressive
+ QDeclarativeContextData *context = resource->getContext();
+
+ if (!context)
+ return v8::Undefined(); // XXX Should we throw here?
+
+ // See QV8ContextWrapper::Getter for resolution order
+
+ QV8Engine *engine = resource->engine;
+ QObject *scopeObject = resource->scopeObject;
+
+ QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
+
+ // Search scope object
+ if (resource->secondaryScope && qobjectWrapper->setProperty(resource->secondaryScope, property, value,
+ QV8QObjectWrapper::IgnoreRevision))
+ return value;
+
+ while (context) {
+ // Search context properties
+ if (context->propertyNames && -1 != context->propertyNames->value(property))
+ return value;
+
+ // Search scope object
+ if (scopeObject &&
+ qobjectWrapper->setProperty(scopeObject, property, value, QV8QObjectWrapper::CheckRevision))
+ return value;
+ scopeObject = 0;
+
+ // Search context object
+ if (context->contextObject &&
+ qobjectWrapper->setProperty(context->contextObject, property, value, QV8QObjectWrapper::CheckRevision))
+ return value;
+
+ context = context->parent;
+ }
+
+ if (!resource->readOnly) {
+ return v8::Handle<v8::Value>();
+ } else {
+ QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) +
+ QLatin1String("\"");
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return v8::Undefined();
+ }
+}
+
+QT_END_NAMESPACE