From 9e002a6e98f9dac8bd2a5787ca92fa857ec8323f Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 10 Aug 2009 21:05:28 +0200 Subject: Test the same code as in the documentation for closures --- tests/auto/qscriptengine/tst_qscriptengine.cpp | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 7fc50d4..ff3a21d 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -3934,6 +3934,38 @@ void tst_QScriptEngine::nativeFunctionScopes() QCOMPARE(ret.toInt32(), 123); } } + + //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain + { + QScriptEngine eng; + eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n" + "var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptEngine eng; + eng.globalObject().setProperty("counter", eng.newFunction(counter)); + eng.evaluate("var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptEngine eng; + eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid)); + eng.evaluate("var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } } static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } -- cgit v0.12 From 71caeca273d4b76d9ca9b490bf444443d6875c54 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 10 Aug 2009 21:38:06 +0200 Subject: more tests for the QScriptContext::argumentsObject --- tests/auto/qscriptcontext/tst_qscriptcontext.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index 8dd2fc7..ca062ca 100644 --- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp +++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp @@ -218,11 +218,17 @@ void tst_QScriptContext::arguments() { QScriptValue result = eng.evaluate(prefix+"get_argumentsObject(123)"); + eng.evaluate("function nestedArg(x,y,z) { var w = get_argumentsObject('ABC' , x+y+z); return w; }"); + QScriptValue result2 = eng.evaluate("nestedArg(1, 'a', 2)"); QCOMPARE(result.isArray(), false); QVERIFY(result.isObject()); QCOMPARE(result.property("length").toUInt32(), quint32(1)); QCOMPARE(result.property("0").isNumber(), true); QCOMPARE(result.property("0").toNumber(), 123.0); + QVERIFY(result2.isObject()); + QCOMPARE(result2.property("length").toUInt32(), quint32(2)); + QCOMPARE(result2.property("0").toString(), QString::fromLatin1("ABC")); + QCOMPARE(result2.property("1").toString(), QString::fromLatin1("1a2")); } { @@ -842,7 +848,7 @@ void tst_QScriptContext::calledAsConstructor() } -static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *) +static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng) { #define VERIFY(statement) \ do {\ @@ -854,6 +860,10 @@ static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEn VERIFY(obj.isObject()); VERIFY(obj.property(0).toUInt32() == 123); VERIFY(obj.property(1).toString() == QString::fromLatin1("456")); + + obj.setProperty(0, "abc"); + VERIFY(eng->evaluate("arguments[0]").toString() == QString::fromLatin1("abc") ); + return QString::fromLatin1("success"); #undef VERIFY } @@ -870,9 +880,6 @@ void tst_QScriptContext::argumentsObjectInNative() QVERIFY(!eng.hasUncaughtException()); QCOMPARE(result.toString(), QString::fromLatin1("success")); } - - - { QScriptEngine eng; QScriptValue fun = eng.newFunction(argumentsObjectInNative_test1); -- cgit v0.12 From 62281ca6f9e74a5c8b66204d74ceec0215ecab92 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 10 Aug 2009 22:10:51 +0200 Subject: Test the activationObject for js functions --- tests/auto/qscriptcontext/tst_qscriptcontext.cpp | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index ca062ca..a278d2c 100644 --- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp +++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp @@ -76,6 +76,7 @@ private slots: void toString(); void calledAsConstructor(); void argumentsObjectInNative(); + void jsActivationObject(); }; tst_QScriptContext::tst_QScriptContext() @@ -888,7 +889,31 @@ void tst_QScriptContext::argumentsObjectInNative() QVERIFY(!eng.hasUncaughtException()); QCOMPARE(result.toString(), QString::fromLatin1("success")); } +} + +static QScriptValue get_jsActivationObject(QScriptContext *ctx, QScriptEngine *) +{ + return ctx->parentContext()->parentContext()->activationObject(); +} +void tst_QScriptContext::jsActivationObject() +{ + QScriptEngine eng; + eng.globalObject().setProperty("get_jsActivationObject", eng.newFunction(get_jsActivationObject)); + eng.evaluate("function f1() { var w = get_jsActivationObject('arg1'); return w; }"); + eng.evaluate("function f2(x,y,z) { var v1 = 42;\n" + // "function foo() {};\n" //this would avoid JSC to optimize + "var v2 = f1(); return v2; }"); + eng.evaluate("function f3() { var v1 = 'nothing'; return f2(1,2,3); }"); + QScriptValue result1 = eng.evaluate("f2('hello', 'useless', 'world')"); + QScriptValue result2 = eng.evaluate("f3()"); + QVERIFY(result1.isObject()); + QEXPECT_FAIL("", "JSC optimize away the activation object", Abort); + QCOMPARE(result1.property("v1").toInt32() , 42); + QCOMPARE(result1.property("arguments").property(1).toString() , QString::fromLatin1("useless")); + QVERIFY(result2.isObject()); + QCOMPARE(result2.property("v1").toInt32() , 42); + QCOMPARE(result2.property("arguments").property(1).toString() , QString::fromLatin1("2")); } QTEST_MAIN(tst_QScriptContext) -- cgit v0.12 From 81d8292e486bce768d9af27e3510520a769c4fa8 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 11 Aug 2009 09:35:50 +0200 Subject: Fix line number and arguments in QScriptContext::toString For arguments, we always need to skip the implicit 'this' argument For line number, we walk thought the stack frames from the top to find the one above the requested one, which contains the returnPC we need. Also fixed a crash because QScriptContext::parentContext would have returned a pointer with flags inside. Reviewed-by: Kent Hansen --- src/script/api/qscriptcontext.cpp | 7 ++----- src/script/api/qscriptcontextinfo.cpp | 20 ++++++++++++++++++-- tests/auto/qscriptcontext/tst_qscriptcontext.cpp | 1 - .../qscriptcontextinfo/tst_qscriptcontextinfo.cpp | 1 - 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/script/api/qscriptcontext.cpp b/src/script/api/qscriptcontext.cpp index 669002d..f4b8af6 100644 --- a/src/script/api/qscriptcontext.cpp +++ b/src/script/api/qscriptcontext.cpp @@ -273,8 +273,7 @@ QScriptValue QScriptContext::argument(int index) const if (index >= argumentCount()) return QScriptValue(QScriptValue::UndefinedValue); JSC::Register* thisRegister = frame->registers() - JSC::RegisterFile::CallFrameHeaderSize - frame->argumentCount(); - if (frame->codeBlock() == 0) - ++index; // ### off-by-one issue with native functions + ++index; //skip the 'this' object return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(thisRegister[index].jsValue()); } @@ -370,9 +369,7 @@ bool QScriptContext::isCalledAsConstructor() const QScriptContext *QScriptContext::parentContext() const { const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); - JSC::CallFrame *callerFrame = frame->callerFrame(); - if (callerFrame == (JSC::CallFrame*)(1)) // ### CallFrame::noCaller() is private - return 0; + JSC::CallFrame *callerFrame = frame->callerFrame()->removeHostCallFrameFlag(); return reinterpret_cast(callerFrame); } diff --git a/src/script/api/qscriptcontextinfo.cpp b/src/script/api/qscriptcontextinfo.cpp index 52776e2..20b77f0 100644 --- a/src/script/api/qscriptcontextinfo.cpp +++ b/src/script/api/qscriptcontextinfo.cpp @@ -174,8 +174,6 @@ QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *conte for (size_t i = 0; i < body->parameterCount(); ++i) parameterNames.append(QScript::qtStringFromJSCUString(params[i].ustring())); // ### get the function name from the AST - // ### don't know the PC, since it's not stored in the frame - // lineNumber = codeBlock->expressionRangeForBytecodeOffset(...); } else if (callee && callee->isObject(&QScript::QtFunction::info)) { functionType = QScriptContextInfo::QtFunction; // ### the slot can be overloaded -- need to get the particular overload from the context @@ -192,6 +190,24 @@ QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *conte functionType = QScriptContextInfo::QtPropertyFunction; functionMetaIndex = static_cast(callee)->propertyIndex(); } + + // get the lineNumber: + QScriptContext *rewindContext = context->engine()->currentContext(); + if (rewindContext == context) { + //ignore native function + } else { + // rewind the stack from the top in order to find the frame from the caller where the returnPC is stored + while (rewindContext && rewindContext->parentContext() != context) + rewindContext = rewindContext->parentContext(); + if (rewindContext) { + JSC::Instruction *returnPC = QScriptEnginePrivate::frameForContext(rewindContext)->returnPC(); + JSC::CodeBlock *codeBlock = frame->codeBlock(); + if (returnPC && codeBlock) { + lineNumber = codeBlock->lineNumberForBytecodeOffset(const_cast(frame), + returnPC - codeBlock->instructions().begin()); + } + } + } } /*! diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index a278d2c..be0b2b7 100644 --- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp +++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp @@ -791,7 +791,6 @@ void tst_QScriptContext::toString() " return parentContextToString();\n" "}; foo(1, 2, 3)", "script.qs"); QVERIFY(ret.isString()); - QEXPECT_FAIL("", "", Continue); QCOMPARE(ret.toString(), QString::fromLatin1("foo (first=1, second=2, third=3) at script.qs:2")); } diff --git a/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp b/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp index 0877717..921cbd8 100644 --- a/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp +++ b/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp @@ -185,7 +185,6 @@ void tst_QScriptContextInfo::scriptFunction() QCOMPARE(info.functionType(), QScriptContextInfo::ScriptFunction); QVERIFY(info.scriptId() != -1); QCOMPARE(info.fileName(), fileName); - QEXPECT_FAIL("", "lineNumber doesn't work", Continue); QCOMPARE(info.lineNumber(), lineNumber + 1); QEXPECT_FAIL("", "columnNumber doesn't work", Continue); QCOMPARE(info.columnNumber(), 2); -- cgit v0.12