From 538153994cacc4613aef1eb8ef77e501be7f5a88 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 29 Jul 2009 09:51:07 +0200 Subject: Enter a scope when enterning a native function. so native function that would call engine->evaluate("var b = 'foo'); would not change the global object. The change in qscriptengine.cpp makes sure that the correct scope is used for the execution of QScriptEngine::evaluate. The changes in qscriptfunction.cpp push a new scope for native function calls. We might want to move that into QScriptContext later Reviewed-by: Kent Hansen --- src/script/api/qscriptengine.cpp | 34 ++++++++----- src/script/bridge/qscriptfunction.cpp | 69 +++++++++++++++++++++++++- tests/auto/qscriptengine/tst_qscriptengine.cpp | 3 ++ 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index 01cf50b..d6f1a83 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -39,6 +39,7 @@ #include "SourceCode.h" #include "FunctionPrototype.h" #include "JSFunction.h" +#include "Parser.h" #include "utils/qscriptdate_p.h" #include "bridge/qscriptfunction_p.h" @@ -2189,27 +2190,36 @@ QScriptValue QScriptEngine::evaluate(const QString &program, const QString &file JSC::UString jscProgram = QScript::qtStringToJSCUString(program); JSC::UString jscFileName = QScript::qtStringToJSCUString(fileName); - JSC::ExecState* exec = d->currentFrame; + JSC::SourceCode source = JSC::makeSource(jscProgram, jscFileName, lineNumber); + exec->clearException(); - JSC::ScopeChain scopeChain = JSC::ScopeChain(exec->scopeChain()); JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject()); - JSC::Completion comp = JSC::evaluate(exec, scopeChain, - JSC::makeSource(jscProgram, jscFileName, lineNumber), - d->thisForContext(exec)); - if ((comp.complType() == JSC::Normal) || (comp.complType() == JSC::ReturnValue)) { - Q_ASSERT(!exec->hadException()); - return d->scriptValueFromJSCValue(comp.value()); + + int errorLine; + JSC::UString errorMessage; + WTF::RefPtr evalNode = exec->globalData().parser->parse(exec, exec->dynamicGlobalObject()->debugger(), source, &errorLine, &errorMessage); + if (!evalNode) { + JSC::JSValue exceptionValue = JSC::Error::create(exec, JSC::SyntaxError, errorMessage, errorLine, source.provider()->asID(), 0); + exec->setException(exceptionValue); + return d->scriptValueFromJSCValue(exceptionValue); } - if (comp.complType() == JSC::Throw) { - exec->setException(comp.value()); - return d->scriptValueFromJSCValue(comp.value()); + JSC::JSValue thisValue = d->thisForContext(exec); + JSC::JSObject* thisObject = (!thisValue || thisValue.isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue.toObject(exec); + JSC::JSValue exceptionValue; + JSC::JSValue result = exec->interpreter()->execute(evalNode.get(), exec, thisObject, exec->scopeChain(), &exceptionValue); + + if (exceptionValue) { + exec->setException(exceptionValue); + return d->scriptValueFromJSCValue(exceptionValue); } - return QScriptValue(); + Q_ASSERT(!exec->hadException()); + return d->scriptValueFromJSCValue(result); } + /*! Returns the current context. diff --git a/src/script/bridge/qscriptfunction.cpp b/src/script/bridge/qscriptfunction.cpp index ee5c9c3..af70523 100644 --- a/src/script/bridge/qscriptfunction.cpp +++ b/src/script/bridge/qscriptfunction.cpp @@ -19,8 +19,8 @@ #include "JSGlobalObject.h" -QT_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace JSC { ASSERT_CLASS_FITS_IN_CELL(QScript::FunctionWrapper); @@ -30,6 +30,37 @@ ASSERT_CLASS_FITS_IN_CELL(QScript::FunctionWithArgWrapper); namespace QScript { + +class QScriptActivation : public JSC::JSVariableObject { +public: + QScriptActivation(JSC::ExecState *callFrame) + : JSC::JSVariableObject(callFrame->globalData().activationStructure, new QScriptActivationData(callFrame->registers())) + {} + virtual ~QScriptActivation() { delete d; } + virtual bool isDynamicScope() const {return true; } +// virtual bool isActivationObject() const { return true; } + + virtual void putWithAttributes(JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes) + { + if (symbolTablePutWithAttributes(propertyName, value, attributes)) + return; + + JSC::PutPropertySlot slot; + JSObject::putWithAttributes(exec, propertyName, value, attributes, true, slot); + } + + +private: + struct QScriptActivationData : public JSVariableObjectData { + QScriptActivationData(JSC::Register* registers) + : JSVariableObjectData(&symbolTable, registers) + { } + JSC::SymbolTable symbolTable; + }; +}; + + + FunctionWrapper::FunctionWrapper(QScriptEngine *engine, int length, const JSC::Identifier &name, QScriptEngine::FunctionSignature function) : JSC::PrototypeFunction(QScriptEnginePrivate::get(engine)->globalExec(), @@ -58,9 +89,18 @@ JSC::JSValue FunctionWrapper::proxyCall(JSC::ExecState *exec, JSC::JSObject *cal JSC::ExecState *previousFrame = eng_p->currentFrame; eng_p->currentFrame = exec; QScriptContext *ctx = eng_p->contextForFrame(exec); + + //We might have nested eval inside our function so we should create another scope + JSC::JSObject* scope = new (exec) QScriptActivation(exec); + exec->setScopeChain(exec->scopeChain()->copy()->push(scope)); + QScriptValue result = self->data->function(ctx, self->data->engine); if (!result.isValid()) result = QScriptValue(QScriptValue::UndefinedValue); + + exec->setScopeChain(exec->scopeChain()->pop()); + exec->scopeChain()->deref(); + eng_p->currentFrame = previousFrame; eng_p->releaseContextForFrame(exec); return eng_p->scriptValueToJSCValue(result); @@ -74,10 +114,19 @@ JSC::JSObject* FunctionWrapper::proxyConstruct(JSC::ExecState *exec, JSC::JSObje JSC::ExecState *previousFrame = eng_p->currentFrame; QScriptContext *ctx = eng_p->contextForFrame(exec); eng_p->currentFrame = exec; + + //We might have nested eval inside our function so we should create another scope + JSC::JSObject* scope = new (exec) QScriptActivation(exec); + exec->setScopeChain(exec->scopeChain()->copy()->push(scope)); + QScriptValue defaultObject = ctx->thisObject(); QScriptValue result = self->data->function(ctx, self->data->engine); if (!result.isObject()) result = defaultObject; + + exec->setScopeChain(exec->scopeChain()->pop()); + exec->scopeChain()->deref(); + eng_p->currentFrame = previousFrame; eng_p->releaseContextForFrame(exec); return JSC::asObject(eng_p->scriptValueToJSCValue(result)); @@ -112,7 +161,16 @@ JSC::JSValue FunctionWithArgWrapper::proxyCall(JSC::ExecState *exec, JSC::JSObje JSC::ExecState *previousFrame = eng_p->currentFrame; QScriptContext *ctx = eng_p->contextForFrame(exec); eng_p->currentFrame = exec; + + //We might have nested eval inside our function so we should create another scope + JSC::JSObject* scope = new (exec) QScriptActivation(exec); + exec->setScopeChain(exec->scopeChain()->copy()->push(scope)); + QScriptValue result = self->data->function(ctx, self->data->engine, self->data->arg); + + exec->setScopeChain(exec->scopeChain()->pop()); + exec->scopeChain()->deref(); + eng_p->currentFrame = previousFrame; eng_p->releaseContextForFrame(exec); return eng_p->scriptValueToJSCValue(result); @@ -126,10 +184,19 @@ JSC::JSObject* FunctionWithArgWrapper::proxyConstruct(JSC::ExecState *exec, JSC: JSC::ExecState *previousFrame = eng_p->currentFrame; QScriptContext *ctx = eng_p->contextForFrame(exec); eng_p->currentFrame = exec; + + //We might have nested eval inside our function so we should create another scope + JSC::JSObject* scope = new (exec) QScriptActivation(exec); + exec->setScopeChain(exec->scopeChain()->copy()->push(scope)); + QScriptValue defaultObject = ctx->thisObject(); QScriptValue result = self->data->function(ctx, self->data->engine, self->data->arg); if (!result.isObject()) result = defaultObject; + + exec->setScopeChain(exec->scopeChain()->pop()); + exec->scopeChain()->deref(); + eng_p->currentFrame = previousFrame; eng_p->releaseContextForFrame(exec); return JSC::asObject(eng_p->scriptValueToJSCValue(result)); diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 5b4d02d..9b0671f 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -1283,6 +1283,9 @@ void tst_QScriptEngine::nestedEvaluate() QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo")); QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo")); QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo")); + QScriptValue bar = eng.evaluate("bar"); + QVERIFY(bar.isError()); + QCOMPARE(bar.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bar")); } void tst_QScriptEngine::uncaughtException() -- cgit v0.12