From 42aa031a16522bdcabe166881205bfa4607da693 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 3 Aug 2009 16:06:30 +0200 Subject: QScriptContext::calledAsConstructor also works with non-native function Use the opcode to see if it was called with op_construct This could also work with native function, but not when they are called with QScriptValue::call() or QScriptValue::construct() --- src/script/api/qscriptcontext.cpp | 26 +++++++++++++++---- tests/auto/qscriptcontext/tst_qscriptcontext.cpp | 33 ++++++++++++++++++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/script/api/qscriptcontext.cpp b/src/script/api/qscriptcontext.cpp index d68b913..99b8989 100644 --- a/src/script/api/qscriptcontext.cpp +++ b/src/script/api/qscriptcontext.cpp @@ -330,20 +330,36 @@ QScriptValue QScriptContext::argumentsObject() const */ bool QScriptContext::isCalledAsConstructor() const { - const JSC::CallFrame *frame = reinterpret_cast(this); - //look up for the QScriptActivationObject and its calledAsConstructor flag. + JSC::CallFrame *frame = reinterpret_cast(const_cast(this)); + + //For native functions, look up for the QScriptActivationObject and its calledAsConstructor flag. JSC::ScopeChainNode *node = frame->scopeChain(); JSC::ScopeChainIterator it(node); for (it = node->begin(); it != node->end(); ++it) { - if (!(*it)->isVariableObject()) { + if ((*it)->isVariableObject()) { if ((*it)->inherits(&QScript::QScriptActivationObject::info)) { return static_cast(*it)->d_ptr()->calledAsConstructor; } //not a native function - //### we have no way to know if is is or not a constructor - return false; + break; } } + + //Not a native function, try to look up in the bytecode if we where called from op_construct + JSC::Instruction* returnPC = frame->returnPC(); + + if (!returnPC) + return false; + + JSC::CallFrame *callerFrame = reinterpret_cast(parentContext()); + if (!callerFrame) + return false; + + if (returnPC[-JSC::op_construct_length].u.opcode == frame->interpreter()->getOpcode(JSC::op_construct)) { + //We are maybe called from the op_construct opcode which has 6 opperands. + //But we need to check we are not called from op_call with 4 opperands (by checking the argc operand) + return returnPC[-4].u.operand == frame->argumentCount(); + } return false; } diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index 4726815..7a363ac 100644 --- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp +++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp @@ -764,15 +764,21 @@ static QScriptValue storeCalledAsConstructorV2(QScriptContext *ctx, QScriptEngin return eng->undefinedValue(); } +static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngine *eng) +{ + ctx->callee().setProperty("calledAsConstructor", ctx->parentContext()->isCalledAsConstructor()); + return eng->undefinedValue(); +} + void tst_QScriptContext::calledAsConstructor() { QScriptEngine eng; + QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor); { - QScriptValue fun = eng.newFunction(storeCalledAsConstructor); - fun.call(); - QVERIFY(!fun.property("calledAsConstructor").toBool()); - fun.construct(); - QVERIFY(fun.property("calledAsConstructor").toBool()); + fun1.call(); + QVERIFY(!fun1.property("calledAsConstructor").toBool()); + fun1.construct(); + QVERIFY(fun1.property("calledAsConstructor").toBool()); } { QScriptValue fun = eng.newFunction(storeCalledAsConstructorV2, (void*)0); @@ -781,6 +787,23 @@ void tst_QScriptContext::calledAsConstructor() fun.construct(); QVERIFY(fun.property("calledAsConstructor").toBool()); } + { + eng.globalObject().setProperty("fun1", fun1); + eng.evaluate("fun1();"); + QVERIFY(!fun1.property("calledAsConstructor").toBool()); + eng.evaluate("new fun1();"); + QVERIFY(fun1.property("calledAsConstructor").toBool()); + } + { + QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3); + eng.globalObject().setProperty("fun3", fun3); + eng.evaluate("function test() { fun3() }"); + eng.evaluate("test();"); + QVERIFY(!fun3.property("calledAsConstructor").toBool()); + eng.evaluate("new test();"); + QVERIFY(fun3.property("calledAsConstructor").toBool()); + } + } static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *) -- cgit v0.12