summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--qmake/generators/win32/msbuild_objectmodel.cpp1
-rw-r--r--qmake/generators/win32/msvc_objectmodel.cpp7
-rw-r--r--qmake/generators/win32/msvc_vcproj.cpp1
-rw-r--r--qmake/generators/win32/msvc_vcxproj.cpp1
-rw-r--r--src/declarative/qml/qdeclarativecontext.cpp12
-rw-r--r--src/declarative/qml/qdeclarativeexpression.cpp8
-rw-r--r--src/declarative/qml/qdeclarativeglobalscriptclass.cpp39
-rw-r--r--src/declarative/qml/qdeclarativeglobalscriptclass_p.h4
-rw-r--r--src/declarative/qml/qdeclarativeinclude.cpp2
-rw-r--r--src/script/api/qscriptengine.cpp12
-rw-r--r--src/script/api/qscriptengine_p.h15
-rw-r--r--src/script/bridge/bridge.pri2
-rw-r--r--src/script/bridge/qscriptdeclarativeclass.cpp36
-rw-r--r--src/script/bridge/qscriptdeclarativeclass_p.h5
-rw-r--r--src/script/bridge/qscriptstaticscopeobject.cpp157
-rw-r--r--src/script/bridge/qscriptstaticscopeobject_p.h103
-rw-r--r--tests/auto/qscriptengine/tst_qscriptengine.cpp242
-rw-r--r--tests/benchmarks/script/qscriptengine/tst_qscriptengine.cpp52
18 files changed, 656 insertions, 43 deletions
diff --git a/qmake/generators/win32/msbuild_objectmodel.cpp b/qmake/generators/win32/msbuild_objectmodel.cpp
index bf874b2..75fc910 100644
--- a/qmake/generators/win32/msbuild_objectmodel.cpp
+++ b/qmake/generators/win32/msbuild_objectmodel.cpp
@@ -385,7 +385,6 @@ VCXCLCompilerTool::VCXCLCompilerTool()
DisableLanguageExtensions(unset),
EnableFiberSafeOptimizations(unset),
EnablePREfast(unset),
- ExceptionHandling("false"),
ExpandAttributedSource(unset),
FloatingPointExceptions(unset),
ForceConformanceInForLoopScope(unset),
diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp
index e23e119..1e060a0 100644
--- a/qmake/generators/win32/msvc_objectmodel.cpp
+++ b/qmake/generators/win32/msvc_objectmodel.cpp
@@ -360,11 +360,8 @@ inline XmlOutput::xml_output xformUsePrecompiledHeaderForNET2005(pchOption whatP
inline XmlOutput::xml_output xformExceptionHandlingNET2005(exceptionHandling eh, DotNET compilerVersion)
{
- if (eh == ehDefault) {
- if (compilerVersion >= NET2005)
- return attrE(_ExceptionHandling, ehNone);
- return attrS(_ExceptionHandling, "false");
- }
+ if (eh == ehDefault)
+ return noxml();
if (compilerVersion >= NET2005)
return attrE(_ExceptionHandling, eh);
diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp
index 35e4896..8686ae8 100644
--- a/qmake/generators/win32/msvc_vcproj.cpp
+++ b/qmake/generators/win32/msvc_vcproj.cpp
@@ -900,6 +900,7 @@ void VcprojGenerator::initCompilerTool()
conf.compiler.AssemblerListingLocation = placement ;
conf.compiler.ProgramDataBaseFileName = ".\\" ;
conf.compiler.ObjectFile = placement ;
+ conf.compiler.ExceptionHandling = ehNone;
// PCH
if (usePCH) {
conf.compiler.UsePrecompiledHeader = pchUseUsingSpecific;
diff --git a/qmake/generators/win32/msvc_vcxproj.cpp b/qmake/generators/win32/msvc_vcxproj.cpp
index 05c1511..f68a435 100644
--- a/qmake/generators/win32/msvc_vcxproj.cpp
+++ b/qmake/generators/win32/msvc_vcxproj.cpp
@@ -274,6 +274,7 @@ void VcxprojGenerator::initCompilerTool()
conf.compiler.AssemblerListingLocation = placement ;
conf.compiler.ProgramDataBaseFileName = ".\\" ;
conf.compiler.ObjectFileName = placement ;
+ conf.compiler.ExceptionHandling = "false";
// PCH
if (usePCH) {
conf.compiler.PrecompiledHeader = "Use";
diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp
index 2221d78..60e9dd3 100644
--- a/src/declarative/qml/qdeclarativecontext.cpp
+++ b/src/declarative/qml/qdeclarativecontext.cpp
@@ -660,10 +660,9 @@ void QDeclarativeContextData::addImportedScript(const QDeclarativeParser::Object
QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
scriptContext->pushScope(enginePriv->contextClass->newUrlContext(url));
- scriptContext->pushScope(enginePriv->globalClass->globalObject());
+ scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject());
- QScriptValue scope = scriptEngine->newObject();
- scriptContext->setActivationObject(scope);
+ QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine);
scriptContext->pushScope(scope);
scriptEngine->evaluate(code, url, 1);
@@ -686,10 +685,9 @@ void QDeclarativeContextData::addImportedScript(const QDeclarativeParser::Object
QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
scriptContext->pushScope(enginePriv->contextClass->newUrlContext(this, 0, url));
- scriptContext->pushScope(enginePriv->globalClass->globalObject());
-
- QScriptValue scope = scriptEngine->newObject();
- scriptContext->setActivationObject(scope);
+ scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject());
+
+ QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine);
scriptContext->pushScope(scope);
scriptEngine->evaluate(code, url, 1);
diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp
index b1aecfa..8ae5f2f 100644
--- a/src/declarative/qml/qdeclarativeexpression.cpp
+++ b/src/declarative/qml/qdeclarativeexpression.cpp
@@ -145,7 +145,7 @@ void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, void *ex
if (!dd->cachedClosures.at(progIdx)) {
QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
scriptContext->pushScope(ep->contextClass->newSharedContext());
- scriptContext->pushScope(ep->globalClass->globalObject());
+ scriptContext->pushScope(ep->globalClass->staticGlobalObject());
dd->cachedClosures[progIdx] = new QScriptValue(scriptEngine->evaluate(data->expression, data->url, data->line));
scriptEngine->popContext();
}
@@ -188,7 +188,7 @@ QScriptValue QDeclarativeExpressionPrivate::evalInObjectScope(QDeclarativeContex
} else {
scriptContext->pushScope(ep->contextClass->newContext(context, object));
}
- scriptContext->pushScope(ep->globalClass->globalObject());
+ scriptContext->pushScope(ep->globalClass->staticGlobalObject());
QScriptValue rv = ep->scriptEngine.evaluate(program, fileName, lineNumber);
ep->scriptEngine.popContext();
return rv;
@@ -206,7 +206,7 @@ QScriptValue QDeclarativeExpressionPrivate::evalInObjectScope(QDeclarativeContex
} else {
scriptContext->pushScope(ep->contextClass->newContext(context, object));
}
- scriptContext->pushScope(ep->globalClass->globalObject());
+ scriptContext->pushScope(ep->globalClass->staticGlobalObject());
QScriptValue rv = ep->scriptEngine.evaluate(program);
ep->scriptEngine.popContext();
return rv;
@@ -369,7 +369,7 @@ QScriptValue QDeclarativeExpressionPrivate::eval(QObject *secondaryScope, bool *
QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
data->expressionContext = ep->contextClass->newContext(data->context(), data->me);
scriptContext->pushScope(data->expressionContext);
- scriptContext->pushScope(ep->globalClass->globalObject());
+ scriptContext->pushScope(ep->globalClass->staticGlobalObject());
if (data->expressionRewritten) {
data->expressionFunction = scriptEngine->evaluate(data->expression,
diff --git a/src/declarative/qml/qdeclarativeglobalscriptclass.cpp b/src/declarative/qml/qdeclarativeglobalscriptclass.cpp
index 6e107fb..39ea101 100644
--- a/src/declarative/qml/qdeclarativeglobalscriptclass.cpp
+++ b/src/declarative/qml/qdeclarativeglobalscriptclass.cpp
@@ -41,10 +41,13 @@
#include "private/qdeclarativeglobalscriptclass_p.h"
+#include <QtCore/qvector.h>
#include <QtScript/qscriptstring.h>
#include <QtScript/qscriptengine.h>
#include <QtScript/qscriptvalueiterator.h>
+#include <private/qscriptdeclarativeclass_p.h>
+
QT_BEGIN_NAMESPACE
/*
@@ -55,23 +58,31 @@ QDeclarativeGlobalScriptClass::QDeclarativeGlobalScriptClass(QScriptEngine *engi
{
QString eval = QLatin1String("eval");
- QScriptValue globalObject = engine->globalObject();
+ QScriptValue originalGlobalObject = engine->globalObject();
- m_globalObject = engine->newObject();
QScriptValue newGlobalObject = engine->newObject();
- QScriptValueIterator iter(globalObject);
-
- while (iter.hasNext()) {
- iter.next();
-
- QString name = iter.name();
-
- if (name != eval)
- m_globalObject.setProperty(iter.scriptName(), iter.value());
- newGlobalObject.setProperty(iter.scriptName(), iter.value());
-
- m_illegalNames.insert(name);
+ {
+ QScriptValueIterator iter(originalGlobalObject);
+ QVector<QString> names;
+ QVector<QScriptValue> values;
+ QVector<QScriptValue::PropertyFlags> flags;
+ while (iter.hasNext()) {
+ iter.next();
+
+ QString name = iter.name();
+
+ if (name != eval) {
+ names.append(name);
+ values.append(iter.value());
+ flags.append(iter.flags() | QScriptValue::Undeletable);
+ }
+ newGlobalObject.setProperty(iter.scriptName(), iter.value());
+
+ m_illegalNames.insert(name);
+ }
+ m_staticGlobalObject = QScriptDeclarativeClass::newStaticScopeObject(
+ engine, names.size(), names.constData(), values.constData(), flags.constData());
}
newGlobalObject.setScriptClass(this);
diff --git a/src/declarative/qml/qdeclarativeglobalscriptclass_p.h b/src/declarative/qml/qdeclarativeglobalscriptclass_p.h
index 7690edd..414bf02 100644
--- a/src/declarative/qml/qdeclarativeglobalscriptclass_p.h
+++ b/src/declarative/qml/qdeclarativeglobalscriptclass_p.h
@@ -75,13 +75,13 @@ public:
void explicitSetProperty(const QString &, const QScriptValue &);
- const QScriptValue &globalObject() const { return m_globalObject; }
+ const QScriptValue &staticGlobalObject() const { return m_staticGlobalObject; }
const QSet<QString> &illegalNames() const { return m_illegalNames; }
private:
QSet<QString> m_illegalNames;
- QScriptValue m_globalObject;
+ QScriptValue m_staticGlobalObject;
};
QT_END_NAMESPACE
diff --git a/src/declarative/qml/qdeclarativeinclude.cpp b/src/declarative/qml/qdeclarativeinclude.cpp
index c29005a..f26b54f 100644
--- a/src/declarative/qml/qdeclarativeinclude.cpp
+++ b/src/declarative/qml/qdeclarativeinclude.cpp
@@ -240,7 +240,7 @@ QScriptValue QDeclarativeInclude::include(QScriptContext *ctxt, QScriptEngine *e
QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(engine);
scriptContext->pushScope(ep->contextClass->newUrlContext(context, 0, urlString));
- scriptContext->pushScope(ep->globalClass->globalObject());
+ scriptContext->pushScope(ep->globalClass->staticGlobalObject());
QScriptValue scope = QScriptDeclarativeClass::scopeChainValue(ctxt, -5);
scriptContext->pushScope(scope);
scriptContext->setActivationObject(scope);
diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp
index e2999c1..655026c 100644
--- a/src/script/api/qscriptengine.cpp
+++ b/src/script/api/qscriptengine.cpp
@@ -64,6 +64,7 @@
#include "bridge/qscriptqobject_p.h"
#include "bridge/qscriptglobalobject_p.h"
#include "bridge/qscriptactivationobject_p.h"
+#include "bridge/qscriptstaticscopeobject_p.h"
#ifndef QT_NO_QOBJECT
#include <QtCore/qcoreapplication.h>
@@ -905,6 +906,7 @@ QScriptEnginePrivate::QScriptEnginePrivate()
JSC::ExecState* exec = globalObject->globalExec();
scriptObjectStructure = QScriptObject::createStructure(globalObject->objectPrototype());
+ staticScopeObjectStructure = QScriptStaticScopeObject::createStructure(JSC::jsNull());
qobjectPrototype = new (exec) QScript::QObjectPrototype(exec, QScript::QObjectPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure());
qobjectWrapperObjectStructure = QScriptObject::createStructure(qobjectPrototype);
@@ -1770,15 +1772,7 @@ void QScriptEnginePrivate::setProperty(JSC::ExecState *exec, JSC::JSValue object
} else if (flags != QScriptValue::KeepExistingFlags) {
if (thisObject->hasOwnProperty(exec, id))
thisObject->deleteProperty(exec, id); // ### hmmm - can't we just update the attributes?
- unsigned attribs = 0;
- if (flags & QScriptValue::ReadOnly)
- attribs |= JSC::ReadOnly;
- if (flags & QScriptValue::SkipInEnumeration)
- attribs |= JSC::DontEnum;
- if (flags & QScriptValue::Undeletable)
- attribs |= JSC::DontDelete;
- attribs |= flags & QScriptValue::UserRange;
- thisObject->putWithAttributes(exec, id, value, attribs);
+ thisObject->putWithAttributes(exec, id, value, propertyFlagsToJSCAttributes(flags));
} else {
JSC::PutPropertySlot slot;
thisObject->put(exec, id, value, slot);
diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h
index 56366e2..1b35704 100644
--- a/src/script/api/qscriptengine_p.h
+++ b/src/script/api/qscriptengine_p.h
@@ -205,6 +205,7 @@ public:
inline QScriptValue scriptValueFromJSCValue(JSC::JSValue value);
inline JSC::JSValue scriptValueToJSCValue(const QScriptValue &value);
+ static inline unsigned propertyFlagsToJSCAttributes(const QScriptValue::PropertyFlags &flags);
static inline JSC::JSValue jscValueFromVariant(JSC::ExecState*, const QVariant &value);
static QVariant jscValueToVariant(JSC::ExecState*, JSC::JSValue value, int targetType);
@@ -346,6 +347,7 @@ public:
JSC::ExecState *currentFrame;
WTF::RefPtr<JSC::Structure> scriptObjectStructure;
+ WTF::RefPtr<JSC::Structure> staticScopeObjectStructure;
QScript::QObjectPrototype *qobjectPrototype;
WTF::RefPtr<JSC::Structure> qobjectWrapperObjectStructure;
@@ -639,6 +641,19 @@ inline JSC::JSValue QScriptEnginePrivate::scriptValueToJSCValue(const QScriptVal
return vv->jscValue;
}
+inline unsigned QScriptEnginePrivate::propertyFlagsToJSCAttributes(const QScriptValue::PropertyFlags &flags)
+{
+ unsigned attribs = 0;
+ if (flags & QScriptValue::ReadOnly)
+ attribs |= JSC::ReadOnly;
+ if (flags & QScriptValue::SkipInEnumeration)
+ attribs |= JSC::DontEnum;
+ if (flags & QScriptValue::Undeletable)
+ attribs |= JSC::DontDelete;
+ attribs |= flags & QScriptValue::UserRange;
+ return attribs;
+}
+
inline QScriptValuePrivate::~QScriptValuePrivate()
{
if (engine)
diff --git a/src/script/bridge/bridge.pri b/src/script/bridge/bridge.pri
index 09e2dfb..ab0a322 100644
--- a/src/script/bridge/bridge.pri
+++ b/src/script/bridge/bridge.pri
@@ -6,6 +6,7 @@ SOURCES += \
$$PWD/qscriptqobject.cpp \
$$PWD/qscriptglobalobject.cpp \
$$PWD/qscriptactivationobject.cpp \
+ $$PWD/qscriptstaticscopeobject.cpp \
$$PWD/qscriptdeclarativeobject.cpp \
$$PWD/qscriptdeclarativeclass.cpp
@@ -17,5 +18,6 @@ HEADERS += \
$$PWD/qscriptqobject_p.h \
$$PWD/qscriptglobalobject_p.h \
$$PWD/qscriptactivationobject_p.h \
+ $$PWD/qscriptstaticscopeobject_p.h \
$$PWD/qscriptdeclarativeobject_p.h \
$$PWD/qscriptdeclarativeclass_p.h
diff --git a/src/script/bridge/qscriptdeclarativeclass.cpp b/src/script/bridge/qscriptdeclarativeclass.cpp
index 1093448..8080b9f 100644
--- a/src/script/bridge/qscriptdeclarativeclass.cpp
+++ b/src/script/bridge/qscriptdeclarativeclass.cpp
@@ -24,6 +24,7 @@
#include "qscriptdeclarativeclass_p.h"
#include "qscriptdeclarativeobject_p.h"
#include "qscriptobject_p.h"
+#include "qscriptstaticscopeobject_p.h"
#include <QtScript/qscriptstring.h>
#include <QtScript/qscriptengine.h>
#include <QtScript/qscriptengineagent.h>
@@ -549,4 +550,39 @@ QScriptContext *QScriptDeclarativeClass::context() const
return d_ptr->context;
}
+/*!
+ Creates a scope object with a fixed set of undeletable properties.
+*/
+QScriptValue QScriptDeclarativeClass::newStaticScopeObject(
+ QScriptEngine *engine, int propertyCount, const QString *names,
+ const QScriptValue *values, const QScriptValue::PropertyFlags *flags)
+{
+ QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(engine);
+ QScript::APIShim shim(eng_p);
+ JSC::ExecState *exec = eng_p->currentFrame;
+ QScriptStaticScopeObject::PropertyInfo *props = new QScriptStaticScopeObject::PropertyInfo[propertyCount];
+ for (int i = 0; i < propertyCount; ++i) {
+ unsigned attribs = QScriptEnginePrivate::propertyFlagsToJSCAttributes(flags[i]);
+ Q_ASSERT_X(attribs & JSC::DontDelete, Q_FUNC_INFO, "All properties must be undeletable");
+ JSC::Identifier id = JSC::Identifier(exec, names[i]);
+ JSC::JSValue jsval = eng_p->scriptValueToJSCValue(values[i]);
+ props[i] = QScriptStaticScopeObject::PropertyInfo(id, jsval, attribs);
+ }
+ QScriptValue result = eng_p->scriptValueFromJSCValue(new (exec)QScriptStaticScopeObject(eng_p->staticScopeObjectStructure,
+ propertyCount, props));
+ delete[] props;
+ return result;
+}
+
+/*!
+ Creates a static scope object that's initially empty, but to which new
+ properties can be added.
+*/
+QScriptValue QScriptDeclarativeClass::newStaticScopeObject(QScriptEngine *engine)
+{
+ QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(engine);
+ QScript::APIShim shim(eng_p);
+ return eng_p->scriptValueFromJSCValue(new (eng_p->currentFrame)QScriptStaticScopeObject(eng_p->staticScopeObjectStructure));
+}
+
QT_END_NAMESPACE
diff --git a/src/script/bridge/qscriptdeclarativeclass_p.h b/src/script/bridge/qscriptdeclarativeclass_p.h
index 714a67c..420b133 100644
--- a/src/script/bridge/qscriptdeclarativeclass_p.h
+++ b/src/script/bridge/qscriptdeclarativeclass_p.h
@@ -92,6 +92,11 @@ public:
static QScriptValue scopeChainValue(QScriptContext *, int index);
static QScriptContext *pushCleanContext(QScriptEngine *);
+ static QScriptValue newStaticScopeObject(
+ QScriptEngine *, int propertyCount, const QString *names,
+ const QScriptValue *values, const QScriptValue::PropertyFlags *flags);
+ static QScriptValue newStaticScopeObject(QScriptEngine *);
+
class Q_SCRIPT_EXPORT PersistentIdentifier
{
public:
diff --git a/src/script/bridge/qscriptstaticscopeobject.cpp b/src/script/bridge/qscriptstaticscopeobject.cpp
new file mode 100644
index 0000000..44548a4
--- /dev/null
+++ b/src/script/bridge/qscriptstaticscopeobject.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtScript module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL-ONLY$
+** 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "config.h"
+#include "qscriptstaticscopeobject_p.h"
+
+namespace JSC
+{
+ ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScriptStaticScopeObject));
+}
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QScriptStaticScopeObject
+ \internal
+
+ Represents a static scope object.
+
+ This class allows the VM to determine at JS script compile time whether
+ the object has a given property or not. If the object has the property,
+ a fast, index-based read/write operation will be used. If the object
+ doesn't have the property, the compiler knows it can safely skip this
+ object when dynamically resolving the property. Either way, this can
+ greatly improve performance.
+
+ \sa QScriptContext::pushScope()
+*/
+
+const JSC::ClassInfo QScriptStaticScopeObject::info = { "QScriptStaticScopeObject", 0, 0, 0 };
+
+/*!
+ Creates a static scope object with a fixed set of undeletable properties.
+
+ It's not possible to add new properties to the object after construction.
+*/
+QScriptStaticScopeObject::QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure,
+ int propertyCount, const PropertyInfo* props)
+ : JSC::JSVariableObject(structure, new Data(/*canGrow=*/false))
+{
+ int index = growRegisterArray(propertyCount);
+ for (int i = 0; i < propertyCount; ++i, --index) {
+ const PropertyInfo& prop = props[i];
+ JSC::SymbolTableEntry entry(index, prop.attributes);
+ symbolTable().add(prop.identifier.ustring().rep(), entry);
+ registerAt(index) = prop.value;
+ }
+}
+
+/*!
+ Creates an empty static scope object.
+
+ Properties can be added to the object after construction, either by
+ calling QScriptValue::setProperty(), or by pushing the object on the
+ scope chain; variable declarations ("var" statements) and function
+ declarations in JavaScript will create properties on the scope object.
+
+ Note that once the scope object has been used in a closure and the
+ resulting function has been compiled, it's no longer safe to add
+ properties to the scope object (because the VM will bypass this
+ object the next time the function is executed).
+*/
+QScriptStaticScopeObject::QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure)
+ : JSC::JSVariableObject(structure, new Data(/*canGrow=*/true))
+{
+}
+
+QScriptStaticScopeObject::~QScriptStaticScopeObject()
+{
+ delete d;
+}
+
+bool QScriptStaticScopeObject::getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot& slot)
+{
+ return symbolTableGet(propertyName, slot);
+}
+
+bool QScriptStaticScopeObject::getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor)
+{
+ return symbolTableGet(propertyName, descriptor);
+}
+
+void QScriptStaticScopeObject::putWithAttributes(JSC::ExecState* exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes)
+{
+ if (symbolTablePutWithAttributes(propertyName, value, attributes))
+ return;
+ Q_ASSERT(d_ptr()->canGrow);
+ addSymbolTableProperty(propertyName, value, attributes);
+}
+
+void QScriptStaticScopeObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot&)
+{
+ if (symbolTablePut(propertyName, value))
+ return;
+ Q_ASSERT(d_ptr()->canGrow);
+ addSymbolTableProperty(propertyName, value, /*attributes=*/0);
+}
+
+bool QScriptStaticScopeObject::deleteProperty(JSC::ExecState*, const JSC::Identifier&)
+{
+ return false;
+}
+
+void QScriptStaticScopeObject::markChildren(JSC::MarkStack& markStack)
+{
+ JSC::Register* registerArray = d_ptr()->registerArray.get();
+ if (!registerArray)
+ return;
+ markStack.appendValues(reinterpret_cast<JSC::JSValue*>(registerArray), d_ptr()->registerArraySize);
+}
+
+void QScriptStaticScopeObject::addSymbolTableProperty(const JSC::Identifier& name, JSC::JSValue value, unsigned attributes)
+{
+ int index = growRegisterArray(1);
+ JSC::SymbolTableEntry newEntry(index, attributes | JSC::DontDelete);
+ symbolTable().add(name.ustring().rep(), newEntry);
+ registerAt(index) = value;
+}
+
+/*!
+ Grows the register array by \a count elements, and returns the offset of
+ the newly added elements (note that the register file grows downwards,
+ starting at index -1).
+*/
+int QScriptStaticScopeObject::growRegisterArray(int count)
+{
+ size_t oldSize = d_ptr()->registerArraySize;
+ size_t newSize = oldSize + count;
+ JSC::Register* registerArray = new JSC::Register[newSize];
+ if (d_ptr()->registerArray)
+ memcpy(registerArray + count, d_ptr()->registerArray.get(), oldSize * sizeof(JSC::Register));
+ setRegisters(registerArray + newSize, registerArray);
+ d_ptr()->registerArraySize = newSize;
+ return -oldSize - 1;
+}
+
+QT_END_NAMESPACE
diff --git a/src/script/bridge/qscriptstaticscopeobject_p.h b/src/script/bridge/qscriptstaticscopeobject_p.h
new file mode 100644
index 0000000..0a0e7ef
--- /dev/null
+++ b/src/script/bridge/qscriptstaticscopeobject_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtScript module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL-ONLY$
+** 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCRIPTSTATICSCOPEOBJECT_P_H
+#define QSCRIPTSTATICSCOPEOBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobjectdefs.h>
+
+#include "JSVariableObject.h"
+
+QT_BEGIN_NAMESPACE
+
+class QScriptStaticScopeObject : public JSC::JSVariableObject {
+public:
+ struct PropertyInfo {
+ PropertyInfo(const JSC::Identifier& i, JSC::JSValue v, unsigned a)
+ : identifier(i), value(v), attributes(a)
+ { }
+ PropertyInfo() {}
+
+ JSC::Identifier identifier;
+ JSC::JSValue value;
+ unsigned attributes;
+ };
+
+ QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure,
+ int propertyCount, const PropertyInfo*);
+ QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure);
+ virtual ~QScriptStaticScopeObject();
+
+ virtual bool isDynamicScope() const { return false; }
+
+ virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&);
+ virtual bool getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor&);
+
+ virtual void putWithAttributes(JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes);
+ virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot&);
+
+ virtual bool deleteProperty(JSC::ExecState*, const JSC::Identifier& propertyName);
+
+ virtual void markChildren(JSC::MarkStack&);
+
+ virtual const JSC::ClassInfo* classInfo() const { return &info; }
+ static const JSC::ClassInfo info;
+
+ static WTF::PassRefPtr<JSC::Structure> createStructure(JSC::JSValue proto) {
+ return JSC::Structure::create(proto, JSC::TypeInfo(JSC::ObjectType, StructureFlags));
+ }
+
+protected:
+ static const unsigned StructureFlags = JSC::OverridesGetOwnPropertySlot | JSC::NeedsThisConversion | JSC::OverridesMarkChildren | JSC::OverridesGetPropertyNames | JSC::JSVariableObject::StructureFlags;
+
+ struct Data : public JSVariableObjectData {
+ Data(bool canGrow_)
+ : JSVariableObjectData(&symbolTable, /*registers=*/0),
+ canGrow(canGrow_), registerArraySize(0)
+ { }
+ bool canGrow;
+ int registerArraySize;
+ JSC::SymbolTable symbolTable;
+ };
+
+ Data* d_ptr() const { return static_cast<Data*>(JSVariableObject::d); }
+
+private:
+ void addSymbolTableProperty(const JSC::Identifier&, JSC::JSValue, unsigned attributes);
+ int growRegisterArray(int);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp
index 5e59950..6885adf 100644
--- a/tests/auto/qscriptengine/tst_qscriptengine.cpp
+++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp
@@ -51,6 +51,8 @@
#include <QtCore/qnumeric.h>
#include <stdlib.h>
+#include <QtScript/private/qscriptdeclarativeclass_p.h>
+
Q_DECLARE_METATYPE(QList<int>)
Q_DECLARE_METATYPE(QObjectList)
Q_DECLARE_METATYPE(QScriptProgram)
@@ -169,6 +171,8 @@ private slots:
void qRegExpInport_data();
void qRegExpInport();
void reentrency();
+ void newFixedStaticScopeObject();
+ void newGrowingStaticScopeObject();
};
tst_QScriptEngine::tst_QScriptEngine()
@@ -4955,5 +4959,243 @@ void tst_QScriptEngine::reentrency()
QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9);
}
+void tst_QScriptEngine::newFixedStaticScopeObject()
+{
+ QScriptEngine eng;
+ static const int propertyCount = 4;
+ QString names[] = { "foo", "bar", "baz", "Math" };
+ QScriptValue values[] = { 123, "ciao", true, false };
+ QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable,
+ QScriptValue::ReadOnly | QScriptValue::Undeletable,
+ QScriptValue::SkipInEnumeration | QScriptValue::Undeletable,
+ QScriptValue::Undeletable };
+ QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags);
+
+ // Query property.
+ for (int i = 0; i < propertyCount; ++i) {
+ for (int x = 0; x < 2; ++x) {
+ if (x) {
+ // Properties can't be deleted.
+ scope.setProperty(names[i], QScriptValue());
+ }
+ QVERIFY(scope.property(names[i]).equals(values[i]));
+ QCOMPARE(scope.propertyFlags(names[i]), flags[i]);
+ }
+ }
+
+ // Property that doesn't exist.
+ QVERIFY(!scope.property("noSuchProperty").isValid());
+ QCOMPARE(scope.propertyFlags("noSuchProperty"), QScriptValue::PropertyFlags());
+
+ // Write to writable property.
+ {
+ QScriptValue oldValue = scope.property("foo");
+ QVERIFY(oldValue.isNumber());
+ QScriptValue newValue = oldValue.toNumber() * 2;
+ scope.setProperty("foo", newValue);
+ QVERIFY(scope.property("foo").equals(newValue));
+ scope.setProperty("foo", oldValue);
+ QVERIFY(scope.property("foo").equals(oldValue));
+ }
+
+ // Write to read-only property.
+ scope.setProperty("bar", 456);
+ QVERIFY(scope.property("bar").equals("ciao"));
+
+ // Iterate.
+ {
+ QScriptValueIterator it(scope);
+ QSet<QString> iteratedNames;
+ while (it.hasNext()) {
+ it.next();
+ iteratedNames.insert(it.name());
+ }
+ for (int i = 0; i < propertyCount; ++i)
+ QVERIFY(iteratedNames.contains(names[i]));
+ }
+
+ // Push it on the scope chain of a new context.
+ QScriptContext *ctx = eng.pushContext();
+ ctx->pushScope(scope);
+ QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
+ QVERIFY(ctx->activationObject().equals(scope));
+
+ // Read property from JS.
+ for (int i = 0; i < propertyCount; ++i) {
+ for (int x = 0; x < 2; ++x) {
+ if (x) {
+ // Property can't be deleted from JS.
+ QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i]));
+ QVERIFY(ret.equals(false));
+ }
+ QVERIFY(eng.evaluate(names[i]).equals(values[i]));
+ }
+ }
+
+ // Property that doesn't exist.
+ QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: Can't find variable: noSuchProperty"));
+
+ // Write property from JS.
+ {
+ QScriptValue oldValue = eng.evaluate("foo");
+ QVERIFY(oldValue.isNumber());
+ QScriptValue newValue = oldValue.toNumber() * 2;
+ QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
+ scope.setProperty("foo", oldValue);
+ QVERIFY(eng.evaluate("foo").equals(oldValue));
+ }
+
+ // Write to read-only property.
+ QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
+
+ // Create a closure and return properties from there.
+ {
+ QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()");
+ QVERIFY(props.isArray());
+ // "foo" and "bar" come from scope object.
+ QVERIFY(props.property(0).equals(scope.property("foo")));
+ QVERIFY(props.property(1).equals(scope.property("bar")));
+ // "baz" shadows property in scope object.
+ QVERIFY(props.property(2).equals("shadow"));
+ // "Math" comes from scope object, and shadows Global Object's "Math".
+ QVERIFY(props.property(3).equals(scope.property("Math")));
+ QVERIFY(!props.property(3).equals(eng.globalObject().property("Math")));
+ // "Array" comes from Global Object.
+ QVERIFY(props.property(4).equals(eng.globalObject().property("Array")));
+ }
+
+ // As with normal JS, assigning to an undefined variable will create
+ // the property on the Global Object, not the inner scope.
+ QVERIFY(!eng.globalObject().property("newProperty").isValid());
+ QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined());
+ QVERIFY(!scope.property("newProperty").isValid());
+ QVERIFY(eng.globalObject().property("newProperty").isNumber());
+
+ // Nested static scope.
+ {
+ static const int propertyCount2 = 2;
+ QString names2[] = { "foo", "hum" };
+ QScriptValue values2[] = { 321, "hello" };
+ QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable,
+ QScriptValue::ReadOnly | QScriptValue::Undeletable };
+ QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2);
+ ctx->pushScope(scope2);
+
+ // "foo" shadows scope.foo.
+ QVERIFY(eng.evaluate("foo").equals(scope2.property("foo")));
+ QVERIFY(!eng.evaluate("foo").equals(scope.property("foo")));
+ // "hum" comes from scope2.
+ QVERIFY(eng.evaluate("hum").equals(scope2.property("hum")));
+ // "Array" comes from Global Object.
+ QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array")));
+
+ ctx->popScope();
+ }
+
+ QScriptValue fun = eng.evaluate("(function() { return foo; })");
+ QVERIFY(fun.isFunction());
+ eng.popContext();
+ // Function's scope chain persists after popContext().
+ QVERIFY(fun.call().equals(scope.property("foo")));
+}
+
+void tst_QScriptEngine::newGrowingStaticScopeObject()
+{
+ QScriptEngine eng;
+ QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng);
+
+ // Initially empty.
+ QVERIFY(!QScriptValueIterator(scope).hasNext());
+ QVERIFY(!scope.property("foo").isValid());
+
+ // Add a static property.
+ scope.setProperty("foo", 123);
+ QVERIFY(scope.property("foo").equals(123));
+ QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
+
+ // Modify existing property.
+ scope.setProperty("foo", 456);
+ QVERIFY(scope.property("foo").equals(456));
+
+ // Add a read-only property.
+ scope.setProperty("bar", "ciao", QScriptValue::ReadOnly);
+ QVERIFY(scope.property("bar").equals("ciao"));
+ QCOMPARE(scope.propertyFlags("bar"), QScriptValue::ReadOnly | QScriptValue::Undeletable);
+
+ // Attempt to modify read-only property.
+ scope.setProperty("bar", "hello");
+ QVERIFY(scope.property("bar").equals("ciao"));
+
+ // Properties can't be deleted.
+ scope.setProperty("foo", QScriptValue());
+ QVERIFY(scope.property("foo").equals(456));
+ scope.setProperty("bar", QScriptValue());
+ QVERIFY(scope.property("bar").equals("ciao"));
+
+ // Iterate.
+ {
+ QScriptValueIterator it(scope);
+ QSet<QString> iteratedNames;
+ while (it.hasNext()) {
+ it.next();
+ iteratedNames.insert(it.name());
+ }
+ QCOMPARE(iteratedNames.size(), 2);
+ QVERIFY(iteratedNames.contains("foo"));
+ QVERIFY(iteratedNames.contains("bar"));
+ }
+
+ // Push it on the scope chain of a new context.
+ QScriptContext *ctx = eng.pushContext();
+ ctx->pushScope(scope);
+ QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
+ QVERIFY(ctx->activationObject().equals(scope));
+
+ // Read property from JS.
+ QVERIFY(eng.evaluate("foo").equals(scope.property("foo")));
+ QVERIFY(eng.evaluate("bar").equals(scope.property("bar")));
+
+ // Write property from JS.
+ {
+ QScriptValue oldValue = eng.evaluate("foo");
+ QVERIFY(oldValue.isNumber());
+ QScriptValue newValue = oldValue.toNumber() * 2;
+ QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
+ scope.setProperty("foo", oldValue);
+ QVERIFY(eng.evaluate("foo").equals(oldValue));
+ }
+
+ // Write to read-only property.
+ QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
+
+ // Shadow property.
+ QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math")));
+ scope.setProperty("Math", "fake Math");
+ QVERIFY(eng.evaluate("Math").equals(scope.property("Math")));
+
+ // Variable declarations will create properties on the scope.
+ eng.evaluate("var baz = 456");
+ QVERIFY(scope.property("baz").equals(456));
+
+ // Function declarations will create properties on the scope.
+ eng.evaluate("function fun() { return baz; }");
+ QVERIFY(scope.property("fun").isFunction());
+ QVERIFY(scope.property("fun").call().equals(scope.property("baz")));
+
+ // Demonstrate the limitation of a growable static scope: Once a function that
+ // uses the scope has been compiled, it won't pick up properties that are added
+ // to the scope later.
+ {
+ QScriptValue fun = eng.evaluate("(function() { return futureProperty; })");
+ QVERIFY(fun.isFunction());
+ QCOMPARE(fun.call().toString(), QString::fromLatin1("ReferenceError: Can't find variable: futureProperty"));
+ scope.setProperty("futureProperty", "added after the function was compiled");
+ // If scope were dynamic, this would return the new property.
+ QCOMPARE(fun.call().toString(), QString::fromLatin1("ReferenceError: Can't find variable: futureProperty"));
+ }
+
+ eng.popContext();
+}
+
QTEST_MAIN(tst_QScriptEngine)
#include "tst_qscriptengine.moc"
diff --git a/tests/benchmarks/script/qscriptengine/tst_qscriptengine.cpp b/tests/benchmarks/script/qscriptengine/tst_qscriptengine.cpp
index 35e2f28..4610046 100644
--- a/tests/benchmarks/script/qscriptengine/tst_qscriptengine.cpp
+++ b/tests/benchmarks/script/qscriptengine/tst_qscriptengine.cpp
@@ -42,6 +42,8 @@
#include <qtest.h>
#include <QtScript>
+#include <QtScript/private/qscriptdeclarativeclass_p.h>
+
//TESTED_FILES=
class tst_QScriptEngine : public QObject
@@ -74,6 +76,8 @@ private slots:
void nativeCall();
void translation_data();
void translation();
+ void readScopeProperty_data();
+ void readScopeProperty();
};
tst_QScriptEngine::tst_QScriptEngine()
@@ -288,5 +292,53 @@ void tst_QScriptEngine::translation()
}
}
+void tst_QScriptEngine::readScopeProperty_data()
+{
+ QTest::addColumn<bool>("staticScope");
+ QTest::addColumn<bool>("nestedScope");
+ QTest::newRow("single dynamic scope") << false << false;
+ QTest::newRow("single static scope") << true << false;
+ QTest::newRow("double dynamic scope") << false << true;
+ QTest::newRow("double static scope") << true << true;
+}
+
+void tst_QScriptEngine::readScopeProperty()
+{
+ QFETCH(bool, staticScope);
+ QFETCH(bool, nestedScope);
+
+ QScriptEngine engine;
+ QScriptContext *ctx = engine.pushContext();
+
+ QScriptValue scope;
+ if (staticScope)
+ scope = QScriptDeclarativeClass::newStaticScopeObject(&engine);
+ else
+ scope = engine.newObject();
+ scope.setProperty("foo", 123);
+ ctx->pushScope(scope);
+
+ if (nestedScope) {
+ QScriptValue scope2;
+ if (staticScope)
+ scope2 = QScriptDeclarativeClass::newStaticScopeObject(&engine);
+ else
+ scope2 = engine.newObject();
+ scope2.setProperty("bar", 456); // ensure a miss in inner scope
+ ctx->pushScope(scope2);
+ }
+
+ QScriptValue fun = engine.evaluate("(function() {\n"
+ " for (var i = 0; i < 10000; ++i) {\n"
+ " foo; foo; foo; foo; foo; foo; foo; foo;\n"
+ " }\n"
+ "})");
+ engine.popContext();
+ QVERIFY(fun.isFunction());
+ QBENCHMARK {
+ fun.call();
+ }
+}
+
QTEST_MAIN(tst_QScriptEngine)
#include "tst_qscriptengine.moc"