diff options
-rw-r--r-- | src/script/api/qscriptcontext.cpp | 7 | ||||
-rw-r--r-- | src/script/api/qscriptcontextinfo.cpp | 20 | ||||
-rw-r--r-- | tests/auto/qscriptcontext/tst_qscriptcontext.cpp | 41 | ||||
-rw-r--r-- | tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp | 1 | ||||
-rw-r--r-- | tests/auto/qscriptengine/tst_qscriptengine.cpp | 32 |
5 files changed, 88 insertions, 13 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<QScriptContext *>(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<QScript::QtPropertyFunction*>(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<JSC::ExecState *>(frame), + returnPC - codeBlock->instructions().begin()); + } + } + } } /*! diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index 8dd2fc7..be0b2b7 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() @@ -218,11 +219,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")); } { @@ -784,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")); } @@ -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); @@ -881,7 +888,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) 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); 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; } |