diff options
author | Olivier Goffart <ogoffart@trolltech.com> | 2009-08-11 11:53:04 (GMT) |
---|---|---|
committer | Olivier Goffart <ogoffart@trolltech.com> | 2009-08-12 13:15:23 (GMT) |
commit | 45e2a19b4b75fe94a78161f26862bf3c6f727d74 (patch) | |
tree | a6e698eee514ad28815519f3b2ba5ac592941de3 | |
parent | e806a4e887b6584f1115ced3cb489bb0e9a2de36 (diff) | |
download | Qt-45e2a19b4b75fe94a78161f26862bf3c6f727d74.zip Qt-45e2a19b4b75fe94a78161f26862bf3c6f727d74.tar.gz Qt-45e2a19b4b75fe94a78161f26862bf3c6f727d74.tar.bz2 |
Refactor the way the JS stack are created for native function
The original JavaScriptCore doesn't create stack frame or scope for
native function.
JSC has been patched to support that.
This commit revert our patches to JSC, and implement create the stack
frame from QScript
Reviewed-by: Kent Hansen
-rw-r--r-- | src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp | 14 | ||||
-rw-r--r-- | src/3rdparty/webkit/JavaScriptCore/runtime/CallData.cpp | 27 | ||||
-rw-r--r-- | src/3rdparty/webkit/JavaScriptCore/runtime/ConstructData.cpp | 39 | ||||
-rw-r--r-- | src/3rdparty/webkit/JavaScriptCore/runtime/PropertySlot.cpp | 23 | ||||
-rw-r--r-- | src/script/api/qscriptengine.cpp | 100 | ||||
-rw-r--r-- | src/script/api/qscriptengine_p.h | 22 | ||||
-rw-r--r-- | src/script/api/qscriptvalue.cpp | 1 | ||||
-rw-r--r-- | src/script/bridge/qscriptclassobject.cpp | 24 | ||||
-rw-r--r-- | src/script/bridge/qscriptfunction.cpp | 61 | ||||
-rw-r--r-- | src/script/bridge/qscriptqobject.cpp | 11 | ||||
-rw-r--r-- | tests/auto/qscriptengine/tst_qscriptengine.cpp | 11 |
11 files changed, 142 insertions, 191 deletions
diff --git a/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp b/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp index fe663ae..c78466e 100644 --- a/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp +++ b/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp @@ -3507,20 +3507,6 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi ScopeChainNode* scopeChain = callFrame->scopeChain(); - Structure* structure; - JSValue prototype = callFrame->r(proto).jsValue(); - if (prototype.isObject()) - structure = asObject(prototype)->inheritorID(); - else - structure = scopeChain->globalObject()->emptyObjectStructure(); -#ifdef QT_BUILD_SCRIPT_LIB - // ### world-class hack - QScriptObject* newObject = new (globalData) QScriptObject(structure); -#else - JSObject* newObject = new (globalData) JSObject(structure); -#endif - callFrame[thisRegister] = JSValue(newObject); // "this" value - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); newCallFrame->init(0, vPC + 7, scopeChain, callFrame, dst, argCount, asObject(v)); diff --git a/src/3rdparty/webkit/JavaScriptCore/runtime/CallData.cpp b/src/3rdparty/webkit/JavaScriptCore/runtime/CallData.cpp index b33c8ba..c89ebf8 100644 --- a/src/3rdparty/webkit/JavaScriptCore/runtime/CallData.cpp +++ b/src/3rdparty/webkit/JavaScriptCore/runtime/CallData.cpp @@ -25,10 +25,6 @@ #include "config.h" #include "CallData.h" -#ifdef QT_BUILD_SCRIPT_LIB -#include "ExceptionHelpers.h" -#include "Interpreter.h" -#endif #include "JSFunction.h" #include "JSGlobalObject.h" @@ -59,29 +55,8 @@ JSValue JSC::NativeFuncWrapper::operator() (ExecState* exec, JSObject* jsobj, JS JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) { - if (callType == CallTypeHost) { -#ifdef QT_BUILD_SCRIPT_LIB - ScopeChainNode* scopeChain = exec->scopeChain(); - Interpreter *interp = exec->interpreter(); - Register *oldEnd = interp->registerFile().end(); - int argc = 1 + args.size(); // implicit "this" parameter - if (!interp->registerFile().grow(oldEnd + argc + RegisterFile::CallFrameHeaderSize)) - return createStackOverflowError(exec); - CallFrame* newCallFrame = CallFrame::create(oldEnd); - newCallFrame[0] = thisValue; - size_t dst = 0; - ArgList::const_iterator it; - for (it = args.begin(); it != args.end(); ++it) - newCallFrame[++dst] = *it; - newCallFrame += argc + RegisterFile::CallFrameHeaderSize; - newCallFrame->init(0, /*vPC=*/0, scopeChain, exec, 0, argc, asObject(functionObject)); - JSValue result = callData.native.function(newCallFrame, asObject(functionObject), thisValue, args); - interp->registerFile().shrink(oldEnd); - return result; -#else + if (callType == CallTypeHost) return callData.native.function(exec, asObject(functionObject), thisValue, args); -#endif - } ASSERT(callType == CallTypeJS); // FIXME: Can this be done more efficiently using the callData? return asFunction(functionObject)->call(exec, thisValue, args); diff --git a/src/3rdparty/webkit/JavaScriptCore/runtime/ConstructData.cpp b/src/3rdparty/webkit/JavaScriptCore/runtime/ConstructData.cpp index 5fe792f..d4eb307 100644 --- a/src/3rdparty/webkit/JavaScriptCore/runtime/ConstructData.cpp +++ b/src/3rdparty/webkit/JavaScriptCore/runtime/ConstructData.cpp @@ -25,12 +25,6 @@ #include "config.h" #include "ConstructData.h" -#ifdef QT_BUILD_SCRIPT_LIB -#include "ExceptionHelpers.h" -#include "Interpreter.h" -#include "JSGlobalObject.h" -#include "bridge/qscriptobject_p.h" -#endif #include "JSFunction.h" @@ -60,39 +54,8 @@ JSObject* JSC::NativeConstrWrapper::operator() (ExecState* exec, JSObject* jsobj JSObject* construct(ExecState* exec, JSValue callee, ConstructType constructType, const ConstructData& constructData, const ArgList& args) { - if (constructType == ConstructTypeHost) { -#ifdef QT_BUILD_SCRIPT_LIB - Structure* structure; - JSValue prototype = callee.get(exec, exec->propertyNames().prototype); - if (prototype.isObject()) - structure = asObject(prototype)->inheritorID(); - else - structure = exec->lexicalGlobalObject()->emptyObjectStructure(); - JSObject* thisObj = new (exec) QScriptObject(structure); - - ScopeChainNode* scopeChain = exec->scopeChain(); - Interpreter *interp = exec->interpreter(); - Register *oldEnd = interp->registerFile().end(); - int argc = 1 + args.size(); // implicit "this" parameter - if (!interp->registerFile().grow(oldEnd + argc + RegisterFile::CallFrameHeaderSize)) - return asObject(createStackOverflowError(exec)); - CallFrame* newCallFrame = CallFrame::create(oldEnd); - size_t dst = 0; - newCallFrame[0] = JSValue(thisObj); - ArgList::const_iterator it; - for (it = args.begin(); it != args.end(); ++it) - newCallFrame[++dst] = *it; - newCallFrame += argc + RegisterFile::CallFrameHeaderSize; - newCallFrame->init(0, /*vPC=*/0, scopeChain, exec, 0, argc, asObject(callee)); - JSObject *result = constructData.native.function(newCallFrame, asObject(callee), args); - if (!result) - result = thisObj; - interp->registerFile().shrink(oldEnd); - return result; -#else + if (constructType == ConstructTypeHost) return constructData.native.function(exec, asObject(callee), args); -#endif - } ASSERT(constructType == ConstructTypeJS); // FIXME: Can this be done more efficiently using the constructData? return asFunction(callee)->construct(exec, args); diff --git a/src/3rdparty/webkit/JavaScriptCore/runtime/PropertySlot.cpp b/src/3rdparty/webkit/JavaScriptCore/runtime/PropertySlot.cpp index 08f50b4..36fa5d8 100644 --- a/src/3rdparty/webkit/JavaScriptCore/runtime/PropertySlot.cpp +++ b/src/3rdparty/webkit/JavaScriptCore/runtime/PropertySlot.cpp @@ -19,10 +19,6 @@ */ #include "config.h" -#ifdef QT_BUILD_SCRIPT_LIB -#include "ExceptionHelpers.h" -#include "Interpreter.h" -#endif #include "PropertySlot.h" #include "JSFunction.h" @@ -39,25 +35,8 @@ JSValue PropertySlot::functionGetter(ExecState* exec, const Identifier&, const P CallData callData; CallType callType = slot.m_data.getterFunc->getCallData(callData); - if (callType == CallTypeHost) { -#ifdef QT_BUILD_SCRIPT_LIB - ScopeChainNode* scopeChain = exec->scopeChain(); - Interpreter *interp = exec->interpreter(); - Register *oldEnd = interp->registerFile().end(); - int argc = 1; // implicit "this" parameter - if (!interp->registerFile().grow(oldEnd + argc + RegisterFile::CallFrameHeaderSize)) - return createStackOverflowError(exec); - JSC::CallFrame* newCallFrame = JSC::CallFrame::create(oldEnd); - newCallFrame[0] = slot.slotBase(); // this - newCallFrame += argc + JSC::RegisterFile::CallFrameHeaderSize; - newCallFrame->init(0, /*vPC=*/0, scopeChain, exec, 0, argc, slot.m_data.getterFunc); - JSValue result = callData.native.function(newCallFrame, slot.m_data.getterFunc, slot.slotBase(), exec->emptyList()); - interp->registerFile().shrink(oldEnd); - return result; -#else + if (callType == CallTypeHost) return callData.native.function(exec, slot.m_data.getterFunc, slot.slotBase(), exec->emptyList()); -#endif - } ASSERT(callType == CallTypeJS); // FIXME: Can this be done more efficiently using the callData? return static_cast<JSFunction*>(slot.m_data.getterFunc)->call(exec, slot.slotBase(), exec->emptyList()); diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index f59cd99..a0ca6b0 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -797,24 +797,6 @@ static QScriptValue __setupPackage__(QScriptContext *ctx, QScriptEngine *eng) } #endif -QScriptPushScopeHelper::QScriptPushScopeHelper(JSC::CallFrame *exec, bool calledAsConstructor) -{ - engine = scriptEngineFromExec(exec); - previousFrame = engine->currentFrame; - engine->currentFrame = exec; - QScriptActivationObject *scope = new (exec) QScriptActivationObject(exec); - scope->d_ptr()->calledAsConstructor = calledAsConstructor; - exec->setScopeChain(exec->scopeChain()->copy()->push(scope)); -} - -QScriptPushScopeHelper::~QScriptPushScopeHelper() -{ - JSC::CallFrame *exec = engine->currentFrame; - exec->setScopeChain(exec->scopeChain()->pop()); - exec->scopeChain()->deref(); - engine->currentFrame = previousFrame; -} - } // namespace QScript QScriptEnginePrivate::QScriptEnginePrivate() : idGenerator(1) @@ -2269,26 +2251,55 @@ QScriptContext *QScriptEngine::currentContext() const QScriptContext *QScriptEngine::pushContext() { Q_D(QScriptEngine); - const int argCount = 1; // for 'this' - JSC::RegisterFile ®isterFile = d->currentFrame->interpreter()->registerFile(); - JSC::Register *const newEnd = registerFile.end() + JSC::RegisterFile::CallFrameHeaderSize + argCount; - if (!registerFile.grow(newEnd)) - return 0; - JSC::CallFrame* previousFrame = d->currentFrame; - JSC::JSObject* scope = new (d->currentFrame) QScript::QScriptActivationObject(d->currentFrame); + JSC::CallFrame* newFrame = d->pushContext(d->currentFrame, d->currentFrame->globalData().dynamicGlobalObject, + JSC::ArgList(), /*callee = */0); - d->currentFrame = JSC::CallFrame::create(newEnd); - d->currentFrame->init(0, 0, previousFrame->scopeChain()->copy()->push(scope), - previousFrame, 0, argCount, 0); - QScriptContext *ctx = d->contextForFrame(d->currentFrame); - ctx->setThisObject(globalObject()); - if (agent()) agent()->contextPush(); - return ctx; + + return d->contextForFrame(newFrame); } +/*! \internal + push a context for a native function. + JSC native function doesn't have different stackframe or context. so we need to create one. + + use popContext right after to go back to the previous context the context if no stack overflow has hapenned + + exec is the current top frame. + + return the new top frame. (might be the same as exec if a new stackframe was not needed) or 0 if stack overflow +*/ +JSC::CallFrame *QScriptEnginePrivate::pushContext(JSC::CallFrame *exec, const JSC::JSValue &thisObject, + const JSC::ArgList& args, JSC::JSObject *callee, bool calledAsConstructor) +{ + JSC::CallFrame *newCallFrame = exec; + if (callee == 0 || !(exec->callee() == callee && exec->returnPC() != 0)) { + //We need to check if the Interpreter might have already created a frame for function called from JS. + JSC::Interpreter *interp = exec->interpreter(); + JSC::Register *oldEnd = interp->registerFile().end(); + int argc = args.size() + 1; //add "this" + JSC::Register *newEnd = oldEnd + argc + JSC::RegisterFile::CallFrameHeaderSize; + if (!interp->registerFile().grow(newEnd)) + return 0; //### Stack overflow + newCallFrame = JSC::CallFrame::create(oldEnd); + newCallFrame[0] = thisObject; + int dst = 0; + JSC::ArgList::const_iterator it; + for (it = args.begin(); it != args.end(); ++it) + newCallFrame[++dst] = *it; + newCallFrame += argc + JSC::RegisterFile::CallFrameHeaderSize; + newCallFrame->init(0, /*vPC=*/0, exec->scopeChain(), exec, 0, argc, callee); + } + currentFrame = newCallFrame; + QScript::QScriptActivationObject *scope = new (newCallFrame) QScript::QScriptActivationObject(newCallFrame); + scope->d_ptr()->calledAsConstructor = calledAsConstructor; + newCallFrame->setScopeChain(newCallFrame->scopeChain()->copy()->push(scope)); + return newCallFrame; +} + + /*! Pops the current execution context and restores the previous one. This function must be used in conjunction with pushContext(). @@ -2305,11 +2316,26 @@ void QScriptEngine::popContext() qWarning("QScriptEngine::popContext() doesn't match with pushContext()"); return; } - JSC::RegisterFile ®isterFile = d->currentFrame->interpreter()->registerFile(); - JSC::Register *const newEnd = d->currentFrame->registers() - JSC::RegisterFile::CallFrameHeaderSize - d->currentFrame->argumentCount(); - d->currentFrame->scopeChain()->pop()->deref(); - d->currentFrame = d->currentFrame->callerFrame(); - registerFile.shrink(newEnd); + + d->popContext(); +} + +/*! \internal + counter part of QScriptEnginePrivate::pushContext + */ +void QScriptEnginePrivate::popContext() +{ + if (currentFrame->returnPC() == 0) { //normal case + JSC::RegisterFile ®isterFile = currentFrame->interpreter()->registerFile(); + JSC::Register *const newEnd = currentFrame->registers() - JSC::RegisterFile::CallFrameHeaderSize - currentFrame->argumentCount(); + currentFrame->scopeChain()->pop()->deref(); + currentFrame = currentFrame->callerFrame(); + registerFile.shrink(newEnd); + } else { //the stack frame was created by the Interpreter, we don't need to rewind it. + currentFrame->setScopeChain(currentFrame->scopeChain()->pop()); + currentFrame->scopeChain()->deref(); + } + } /*! diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h index 1506e7e..572f361 100644 --- a/src/script/api/qscriptengine_p.h +++ b/src/script/api/qscriptengine_p.h @@ -157,6 +157,10 @@ public: JSC::JSValue toUsableValue(JSC::JSValue value); static JSC::JSValue thisForContext(JSC::ExecState *frame); + JSC::CallFrame *pushContext(JSC::CallFrame *exec, const JSC::JSValue &thisObject, const JSC::ArgList& args, + JSC::JSObject *callee, bool calledAsConstructor = false); + void popContext(); + void mark(); bool isCollecting() const; void collectGarbage(); @@ -246,24 +250,6 @@ public: #endif }; -namespace QScript -{ -/*! \internal - Helper class to create QScriptActivationObject. - To be used on the stack -*/ -class QScriptPushScopeHelper -{ - public: - QScriptPushScopeHelper(JSC::CallFrame *newFrame, bool calledAsConstructor = false); - ~QScriptPushScopeHelper(); - private: - QScriptEnginePrivate *engine; - JSC::CallFrame *previousFrame; -}; -} // namespace QScript - - QT_END_NAMESPACE #endif diff --git a/src/script/api/qscriptvalue.cpp b/src/script/api/qscriptvalue.cpp index 9bf4ff2..aa0e8cb 100644 --- a/src/script/api/qscriptvalue.cpp +++ b/src/script/api/qscriptvalue.cpp @@ -889,7 +889,6 @@ void QScriptValue::setPrototype(const QScriptValue &prototype) } nextPrototypeValue = nextPrototype->prototype(); } - JSC::asObject(d->jscValue)->setPrototype(other); } diff --git a/src/script/bridge/qscriptclassobject.cpp b/src/script/bridge/qscriptclassobject.cpp index 50547e81..3a41dae 100644 --- a/src/script/bridge/qscriptclassobject.cpp +++ b/src/script/bridge/qscriptclassobject.cpp @@ -214,14 +214,16 @@ JSC::JSValue JSC_HOST_CALL ClassObjectDelegate::call(JSC::ExecState *exec, JSC:: if (!delegate || (delegate->type() != QScriptObjectDelegate::ClassObject)) return JSC::throwError(exec, JSC::TypeError, "callee is not a ClassObject object"); - //We might have nested eval inside our function so we should create another scope - QScriptPushScopeHelper scope(exec, true); - QScriptClass *scriptClass = static_cast<ClassObjectDelegate*>(delegate)->scriptClass(); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); - QScriptContext *ctx = eng_p->contextForFrame(exec); + + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisValue, args, callee); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); QScriptValue scriptObject = eng_p->scriptValueFromJSCValue(obj); QVariant result = scriptClass->extension(QScriptClass::Callable, qVariantFromValue(ctx)); + eng_p->popContext(); + eng_p->currentFrame = oldFrame; return eng_p->jscValueFromVariant(result); } @@ -241,11 +243,19 @@ JSC::JSObject* ClassObjectDelegate::construct(JSC::ExecState *exec, JSC::JSObjec QScriptObjectDelegate *delegate = obj->delegate(); QScriptClass *scriptClass = static_cast<ClassObjectDelegate*>(delegate)->scriptClass(); - //We might have nested eval inside our function so we should create another scope - QScriptPushScopeHelper scope(exec, true); + JSC::Structure* structure; + JSC::JSValue prototype = JSC::asObject(callee)->get(exec, exec->propertyNames().prototype); + if (prototype.isObject()) + structure = JSC::asObject(prototype)->inheritorID(); + else + structure = exec->lexicalGlobalObject()->emptyObjectStructure(); + JSC::JSObject* thisObject = new (exec) QScriptObject(structure); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); - QScriptContext *ctx = eng_p->contextForFrame(exec); + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee, true); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + QScriptValue defaultObject = ctx->thisObject(); QScriptValue result = qvariant_cast<QScriptValue>(scriptClass->extension(QScriptClass::Callable, qVariantFromValue(ctx))); if (!result.isObject()) diff --git a/src/script/bridge/qscriptfunction.cpp b/src/script/bridge/qscriptfunction.cpp index 2124128..f3880b4 100644 --- a/src/script/bridge/qscriptfunction.cpp +++ b/src/script/bridge/qscriptfunction.cpp @@ -46,12 +46,12 @@ #include "private/qscriptcontext_p.h" #include "private/qscriptvalue_p.h" #include "qscriptactivationobject_p.h" +#include "qscriptobject_p.h" #include "JSGlobalObject.h" #include "DebuggerCallFrame.h" #include "Debugger.h" - namespace JSC { ASSERT_CLASS_FITS_IN_CELL(QScript::FunctionWrapper); @@ -91,29 +91,38 @@ JSC::JSValue FunctionWrapper::proxyCall(JSC::ExecState *exec, JSC::JSObject *cal { FunctionWrapper *self = static_cast<FunctionWrapper*>(callee); QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); - QScriptContext *ctx = eng_p->contextForFrame(exec); - //We might have nested eval inside our function so we should create another scope - QScriptPushScopeHelper scope(exec); + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p)); if (!result.isValid()) result = QScriptValue(QScriptValue::UndefinedValue); + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + return eng_p->scriptValueToJSCValue(result); } JSC::JSObject* FunctionWrapper::proxyConstruct(JSC::ExecState *exec, JSC::JSObject *callee, const JSC::ArgList &args) { + JSC::Structure* structure; + JSC::JSValue prototype = JSC::asObject(callee)->get(exec, exec->propertyNames().prototype); + if (prototype.isObject()) + structure = JSC::asObject(prototype)->inheritorID(); + else + structure = exec->lexicalGlobalObject()->emptyObjectStructure(); + JSC::JSObject* thisObject = new (exec) QScriptObject(structure); + FunctionWrapper *self = static_cast<FunctionWrapper*>(callee); QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); - QScriptContext *ctx = eng_p->contextForFrame(exec); - - //We might have nested eval inside our function so we should create another scope - QScriptPushScopeHelper scope(exec, true); - QScriptValue defaultObject = ctx->thisObject(); + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee, true); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p)); #ifdef QT_BUILD_SCRIPT_LIB @@ -121,7 +130,10 @@ JSC::JSObject* FunctionWrapper::proxyConstruct(JSC::ExecState *exec, JSC::JSObje debugger->functionExit(QScriptValuePrivate::get(result)->jscValue, -1); #endif if (!result.isObject()) - result = defaultObject; + result = ctx->thisObject(); + + eng_p->popContext(); + eng_p->currentFrame = oldFrame; return JSC::asObject(eng_p->scriptValueToJSCValue(result)); } @@ -152,10 +164,14 @@ JSC::JSValue FunctionWithArgWrapper::proxyCall(JSC::ExecState *exec, JSC::JSObje FunctionWithArgWrapper *self = static_cast<FunctionWithArgWrapper*>(callee); QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); - //We might have nested eval inside our function so we should create another scope - QScriptPushScopeHelper scope(exec); + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p), self->data->arg); - QScriptValue result = self->data->function(eng_p->contextForFrame(exec), QScriptEnginePrivate::get(eng_p), self->data->arg); + eng_p->popContext(); + eng_p->currentFrame = oldFrame; return eng_p->scriptValueToJSCValue(result); } @@ -163,18 +179,27 @@ JSC::JSValue FunctionWithArgWrapper::proxyCall(JSC::ExecState *exec, JSC::JSObje JSC::JSObject* FunctionWithArgWrapper::proxyConstruct(JSC::ExecState *exec, JSC::JSObject *callee, const JSC::ArgList &args) { + JSC::Structure* structure; + JSC::JSValue prototype = JSC::asObject(callee)->get(exec, exec->propertyNames().prototype); + if (prototype.isObject()) + structure = JSC::asObject(prototype)->inheritorID(); + else + structure = exec->lexicalGlobalObject()->emptyObjectStructure(); + JSC::JSObject* thisObject = new (exec) QScriptObject(structure); + FunctionWithArgWrapper *self = static_cast<FunctionWithArgWrapper*>(callee); QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); - QScriptContext *ctx = eng_p->contextForFrame(exec); - //We might have nested eval inside our function so we should create another scope - QScriptPushScopeHelper scope(exec, true); + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee, true); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); - QScriptValue defaultObject = ctx->thisObject(); QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p) , self->data->arg); if (!result.isObject()) - result = defaultObject; + result = ctx->thisObject(); + eng_p->popContext(); + eng_p->currentFrame = oldFrame; return JSC::asObject(eng_p->scriptValueToJSCValue(result)); } diff --git a/src/script/bridge/qscriptqobject.cpp b/src/script/bridge/qscriptqobject.cpp index 69ae205..a15e632 100644 --- a/src/script/bridge/qscriptqobject.cpp +++ b/src/script/bridge/qscriptqobject.cpp @@ -1837,8 +1837,9 @@ JSC::JSValue JSC_HOST_CALL QMetaObjectWrapperObject::call( return throwError(exec, JSC::TypeError, "callee is not a QMetaObject"); QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(callee); JSC::ExecState *previousFrame = eng_p->currentFrame; - eng_p->currentFrame = exec; - JSC::JSValue result = self->execute(exec, args, /*calledAsConstructor=*/false); + eng_p->pushContext(exec, thisValue, args, callee); + JSC::JSValue result = self->execute(eng_p->currentFrame, args, /*calledAsConstructor=*/false); + eng_p->popContext(); eng_p->currentFrame = previousFrame; return result; } @@ -1848,8 +1849,9 @@ JSC::JSObject* QMetaObjectWrapperObject::construct(JSC::ExecState *exec, JSC::JS QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(callee); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); JSC::ExecState *previousFrame = eng_p->currentFrame; - eng_p->currentFrame = exec; - JSC::JSValue result = self->execute(exec, args, /*calledAsConstructor=*/true); + eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); + JSC::JSValue result = self->execute(eng_p->currentFrame, args, /*calledAsConstructor=*/true); + eng_p->popContext(); eng_p->currentFrame = previousFrame; if (!result || !result.isObject()) return 0; @@ -1863,7 +1865,6 @@ JSC::JSValue QMetaObjectWrapperObject::execute(JSC::ExecState *exec, if (data->ctor) { QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); QScriptContext *ctx = eng_p->contextForFrame(exec); - QScriptPushScopeHelper scope(exec, calledAsConstructor); JSC::CallData callData; JSC::CallType callType = data->ctor.getCallData(callData); Q_ASSERT_X(callType == JSC::CallTypeHost, Q_FUNC_INFO, "script constructors not supported"); diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 5947814..f15ebdf 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -1872,26 +1872,27 @@ static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng) void tst_QScriptEngine::infiniteRecursion() { - QSKIP("Can cause C stack overflow (task 241294)", SkipAll); - + const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded."); QScriptEngine eng; { QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();"); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("Error: call stack overflow")); + QCOMPARE(ret.toString(), stackOverflowError); } +#if 0 //The native C++ stack overflow before the JS stack { QScriptValue fun = eng.newFunction(recurse); QScriptValue ret = fun.call(); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("Error: call stack overflow")); + QCOMPARE(ret.toString(), stackOverflowError); } { QScriptValue fun = eng.newFunction(recurse2); QScriptValue ret = fun.construct(); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("Error: call stack overflow")); + QCOMPARE(ret.toString(), stackOverflowError); } +#endif } struct Bar { |