From b855240b782395f94315f43ea3e7e182299fac48 Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Thu, 16 Feb 2012 14:43:03 +1000 Subject: Rename QDeclarative symbols to QQuick and QQml Symbols beginning with QDeclarative are already exported by the quick1 module. Users can apply the bin/rename-qtdeclarative-symbols.sh script to modify client code using the previous names of the renamed symbols. Task-number: QTBUG-23737 Change-Id: Ifaa482663767634931e8711a8e9bf6e404859e66 Reviewed-by: Martin Jones --- src/qml/qml/v8/qv8contextwrapper.cpp | 455 +++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 src/qml/qml/v8/qv8contextwrapper.cpp (limited to 'src/qml/qml/v8/qv8contextwrapper.cpp') diff --git a/src/qml/qml/v8/qv8contextwrapper.cpp b/src/qml/qml/v8/qv8contextwrapper.cpp new file mode 100644 index 0000000000..246b716aa0 --- /dev/null +++ b/src/qml/qml/v8/qv8contextwrapper.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include +#include + +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, QQmlContextData *context, QObject *scopeObject); + ~QV8ContextResource(); + + inline QQmlContextData *getContext() const; + inline QObject *getScopeObject() const; + + quint32 isSharedContext:1; + quint32 hasSubContexts:1; + quint32 readOnly:1; + quint32 dummy:29; + + QObject *secondaryScope; + + // This is a pretty horrible hack, and an abuse of external strings. When we create a + // sub-context (a context created by a Qt.include() in an external javascript file), + // we pass a specially crafted SubContext external string as the v8::Script::Data() to + // the script, which contains a pointer to the context. We can then access the + // v8::Script::Data() later on to resolve names and URLs against the sub-context instead + // of the main outer context. + struct SubContext : public v8::String::ExternalStringResource { + SubContext(QQmlContextData *context) : context(context) {} + QQmlGuardedContextData context; + + virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); } + virtual size_t length() const { return internal.length(); } + }; + +private: + QQmlGuardedContextData context; + QQmlGuard scopeObject; + +}; + +QV8ContextResource::QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject) +: QV8ObjectResource(engine), isSharedContext(false), hasSubContexts(false), readOnly(true), + secondaryScope(0), context(context), scopeObject(scopeObject) +{ +} + +QV8ContextResource::~QV8ContextResource() +{ + if (context && context->isJSContext) + context->destroy(); +} + +// Returns the scope object +QObject *QV8ContextResource::getScopeObject() const +{ + if (isSharedContext) + return QQmlEnginePrivate::get(engine->engine())->sharedScope; + else + return scopeObject; +} + +// Returns the context, including resolving a subcontext +QQmlContextData *QV8ContextResource::getContext() const +{ + if (isSharedContext) + return QQmlEnginePrivate::get(engine->engine())->sharedContext; + + if (!hasSubContexts) + return context; + + v8::Local callingdata = v8::Context::GetCallingScriptData(); + if (callingdata.IsEmpty() || !callingdata->IsString()) + return context; + + v8::Local callingstring = callingdata->ToString(); + Q_ASSERT(callingstring->IsExternal()); + Q_ASSERT(callingstring->GetExternalStringResource()); + + SubContext *sc = static_cast(callingstring->GetExternalStringResource()); + return sc->context; +} + +QV8ContextWrapper::QV8ContextWrapper() +: m_engine(0) +{ +} + +QV8ContextWrapper::~QV8ContextWrapper() +{ +} + +void QV8ContextWrapper::destroy() +{ + qPersistentDispose(m_sharedContext); + qPersistentDispose(m_urlConstructor); + qPersistentDispose(m_constructor); +} + +void QV8ContextWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + { + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + m_constructor = qPersistentNew(ft->GetFunction()); + } + { + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter); + m_urlConstructor = qPersistentNew(ft->GetFunction()); + } + { + v8::Local sharedContext = m_constructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(engine, 0, 0); + r->isSharedContext = true; + sharedContext->SetExternalResource(r); + m_sharedContext = qPersistentNew(sharedContext); + } +} + +v8::Local QV8ContextWrapper::qmlScope(QQmlContextData *ctxt, QObject *scope) +{ + // XXX NewInstance() should be optimized + v8::Local rv = m_constructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope); + rv->SetExternalResource(r); + return rv; +} + +v8::Local QV8ContextWrapper::urlScope(const QUrl &url) +{ + QQmlContextData *context = new QQmlContextData; + context->url = url; + context->isInternal = true; + context->isJSContext = true; + + // XXX NewInstance() should be optimized + v8::Local rv = m_urlConstructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0); + rv->SetExternalResource(r); + return rv; +} + +void QV8ContextWrapper::setReadOnly(v8::Handle qmlglobal, bool readOnly) +{ + QV8ContextResource *resource = v8_resource_cast(qmlglobal); + Q_ASSERT(resource); + resource->readOnly = readOnly; +} + +void QV8ContextWrapper::addSubContext(v8::Handle qmlglobal, v8::Handle script, + QQmlContextData *ctxt) +{ + QV8ContextResource *resource = v8_resource_cast(qmlglobal); + Q_ASSERT(resource); + resource->hasSubContexts = true; + script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt))); +} + +QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle ctxt, QObject *scope) +{ + QV8ContextResource *resource = v8_resource_cast(ctxt); + if (!resource) return 0; + + QObject *rv = resource->secondaryScope; + resource->secondaryScope = scope; + return rv; +} + +QQmlContextData *QV8ContextWrapper::callingContext() +{ + v8::Local qmlglobal = v8::Context::GetCallingQmlGlobal(); + if (qmlglobal.IsEmpty()) return 0; + + QV8ContextResource *r = v8_resource_cast(qmlglobal); + return r?r->getContext():0; +} + +QQmlContextData *QV8ContextWrapper::context(v8::Handle value) +{ + if (!value->IsObject()) + return 0; + + v8::Handle qmlglobal = v8::Handle::Cast(value); + QV8ContextResource *r = v8_resource_cast(qmlglobal); + return r?r->getContext():0; +} + +v8::Handle QV8ContextWrapper::NullGetter(v8::Local property, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check(info.This()); + + 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 QV8ContextWrapper::Getter(v8::Local property, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check(info.This()); + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return v8::Undefined(); + + if (v8::Context::GetCallingQmlGlobal() != info.This()) + return v8::Handle(); + + // 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->getScopeObject(); + + QHashedV8String propertystring(property); + + if (context->imports && QV8Engine::startsWithUpper(property)) { + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = context->imports->query(propertystring); + + if (r.isValid()) { + if (r.scriptIndex != -1) { + int index = r.scriptIndex; + if (index < context->importedScripts.count()) + return context->importedScripts.at(index); + else + return v8::Undefined(); + } else if (r.type) { + return engine->typeWrapper()->newObject(scopeObject, r.type); + } else if (r.importNamespace) { + return engine->typeWrapper()->newObject(scopeObject, context->imports, r.importNamespace); + } + Q_ASSERT(!"Unreachable"); + } + + // Fall through + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + if (resource->secondaryScope) { + v8::Handle result = qobjectWrapper->getProperty(resource->secondaryScope, propertystring, + QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) return result; + } + + while (context) { + // Search context properties + if (context->propertyNames) { + int propertyIdx = context->propertyNames->value(propertystring); + + if (propertyIdx != -1) { + + if (propertyIdx < context->idValueCount) { + + ep->captureProperty(&context->idValues[propertyIdx].bindings); + return engine->newQObject(context->idValues[propertyIdx]); + } else { + + QQmlContextPrivate *cp = context->asQQmlContextPrivate(); + + ep->captureProperty(context->asQQmlContext(), -1, + propertyIdx + cp->notifyIndex); + + const QVariant &value = cp->propertyValues.at(propertyIdx); + if (value.userType() == qMetaTypeId >()) { + QQmlListProperty prop(context->asQQmlContext(), (void*)propertyIdx, + 0, + QQmlContextPrivate::context_count, + QQmlContextPrivate::context_at); + return engine->listWrapper()->newList(prop, qMetaTypeId >()); + } else { + return engine->fromVariant(cp->propertyValues.at(propertyIdx)); + } + } + } + } + + // Search scope object + if (scopeObject) { + v8::Handle result = qobjectWrapper->getProperty(scopeObject, propertystring, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + scopeObject = 0; + + + // Search context object + if (context->contextObject) { + v8::Handle result = qobjectWrapper->getProperty(context->contextObject, propertystring, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + QString error = QLatin1String("Can't find variable: ") + engine->toString(property); + v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error))); + return v8::Undefined(); +} + +v8::Handle QV8ContextWrapper::NullSetter(v8::Local property, + v8::Local, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check(info.This()); + + QV8Engine *engine = resource->engine; + + if (!resource->readOnly) { + return v8::Handle(); + } else { + QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + + QLatin1String("\""); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle(); + } +} + +v8::Handle QV8ContextWrapper::Setter(v8::Local property, + v8::Local value, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check(info.This()); + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return v8::Undefined(); + + if (v8::Context::GetCallingQmlGlobal() != info.This()) + return v8::Handle(); + + // See QV8ContextWrapper::Getter for resolution order + + QV8Engine *engine = resource->engine; + QObject *scopeObject = resource->getScopeObject(); + + QHashedV8String propertystring(property); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + // Search scope object + if (resource->secondaryScope && + qobjectWrapper->setProperty(resource->secondaryScope, propertystring, value, + QV8QObjectWrapper::IgnoreRevision)) + return value; + + while (context) { + // Search context properties + if (context->propertyNames && -1 != context->propertyNames->value(propertystring)) + return value; + + // Search scope object + if (scopeObject && + qobjectWrapper->setProperty(scopeObject, propertystring, value, QV8QObjectWrapper::CheckRevision)) + return value; + scopeObject = 0; + + // Search context object + if (context->contextObject && + qobjectWrapper->setProperty(context->contextObject, propertystring, value, + QV8QObjectWrapper::CheckRevision)) + return value; + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + if (!resource->readOnly) { + return v8::Handle(); + } 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 -- cgit v1.2.3