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 /src/script | |
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
Diffstat (limited to 'src/script')
-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 |
6 files changed, 133 insertions, 86 deletions
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"); |