From 2fabd4ccadccebf724775327472ebd712bc7d2f2 Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Tue, 6 Nov 2001 19:45:04 +0000 Subject: [PATCH] Added Function.call/apply. Fixed bug in exception handling clean-up of activation stack. git-svn-id: svn://10.0.0.236/trunk@107472 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js2/src/js2execution.cpp | 95 ++++++++++++++++++++------------ mozilla/js2/src/js2runtime.cpp | 72 +++++++++++++++++++++++- 2 files changed, 131 insertions(+), 36 deletions(-) diff --git a/mozilla/js2/src/js2execution.cpp b/mozilla/js2/src/js2execution.cpp index 0ad7a4d8007..21592bb723b 100644 --- a/mozilla/js2/src/js2execution.cpp +++ b/mozilla/js2/src/js2execution.cpp @@ -188,8 +188,10 @@ JSValue Context::invokeFunction(JSFunction *target, const JSValue& thisValue, JS JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeChain *scopeChain, const JSValue& thisValue, JSValue *argv, uint32 /*argc*/) { - mActivationStack.push(new Activation(mLocals, mStack, mStackTop, mScopeChain, - mArgumentBase, mThis, NULL, mCurModule)); // use NULL pc value to force interpret loop to exit + Activation *prev = new Activation(mLocals, mStack, mStackTop, mScopeChain, + mArgumentBase, mThis, NULL, mCurModule); // use NULL pc value to force interpret loop to exit + uint32 activationHeight = mActivationStack.size(); + mActivationStack.push(prev); mThis = thisValue; if (scopeChain) mScopeChain = scopeChain; @@ -215,14 +217,18 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha try { result = interpret(pc, endPC); } - catch (Exception &x) { - Activation *prev = mActivationStack.top(); - mActivationStack.pop(); - + catch (Exception &jsx) { + while (mActivationStack.size() != activationHeight) + mActivationStack.pop(); + // the following (delete's) are a bit iffy - depends on whether // a closure capturing the contents has come along... // if (mThis.isObject()) // mScopeChain->popScope(); + JSValue x; + if (jsx.hasKind(Exception::userException)) + x = popValue(); + delete[] mStack; delete[] mLocals; if (scopeChain == NULL) @@ -230,7 +236,11 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha mCurModule = prev->mModule; mStack = prev->mStack; - mStackTop = prev->mStackTop; + mStackTop = 0; // we're processing an exception, no need to preserve the stack + + if (jsx.hasKind(Exception::userException)) + pushValue(x); + if (mCurModule) mStackMax = mCurModule->mStackDepth; mLocals = prev->mLocals; @@ -238,9 +248,9 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha mThis = prev->mThis; mScopeChain = prev->mScopeChain; delete prev; - throw x; + throw jsx; } - Activation *prev = mActivationStack.top(); + ASSERT(prev == mActivationStack.top()); mActivationStack.pop(); // the following (delete's) are a bit iffy - depends on whether @@ -1013,6 +1023,18 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) const String *name = index.toString(this).string; obj->getProperty(this, *name, CURRENT_ATTR); } + // if the result is a method of some kind, bind + // the base object to it + JSValue result = topValue(); + if (result.isFunction()) { + popValue(); + if (result.function->isConstructor()) + // A constructor has to be called with a NULL 'this' in order to prompt it + // to construct the instance object. + pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, NULL)))); + else + pushValue(JSValue((JSFunction *)(new JSBoundFunction(result.function, obj)))); + } } break; case SetElementOp: @@ -1627,6 +1649,34 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) Activation *curAct = (mActivationStack.size() > 0) ? mActivationStack.top() : NULL; JSValue x; + if (curAct != hndlr->mActivation) { + ASSERT(mActivationStack.size() > 0); + Activation *prev;// = mActivationStack.top(); + do { + prev = curAct; + if (prev->mPC == NULL) { + // Yikes! the exception is getting thrown across a re-invocation + // of the interpreter loop. + throw jsx; + } + mActivationStack.pop(); + curAct = mActivationStack.top(); + } while (hndlr->mActivation != curAct); + if (jsx.hasKind(Exception::userException)) // snatch the exception before the stack gets clobbered + x = popValue(); + mCurModule = prev->mModule; + endPC = mCurModule->mCodeBase + mCurModule->mLength; + mLocals = prev->mLocals; + mStack = prev->mStack; + mStackMax = mCurModule->mStackDepth; + mArgumentBase = prev->mArgumentBase; + mThis = prev->mThis; + } + else { + if (jsx.hasKind(Exception::userException)) + x = popValue(); + } + // make sure there's a JS object for the catch clause to work with if (!jsx.hasKind(Exception::userException)) { JSValue argv[1]; @@ -1649,32 +1699,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC) break; } } - else { - x = popValue(); - } - if (curAct != hndlr->mActivation) { - ASSERT(mActivationStack.size() > 0); - Activation *prev;// = mActivationStack.top(); - do { - prev = curAct; - if (prev->mPC == NULL) { - // Yikes! the exception is getting thrown across a re-invocation - // of the interpreter loop. - throw jsx; - } - mActivationStack.pop(); - curAct = mActivationStack.top(); - } while (hndlr->mActivation != curAct); - mCurModule = prev->mModule; - endPC = mCurModule->mCodeBase + mCurModule->mLength; - mLocals = prev->mLocals; - mStack = prev->mStack; - mStackMax = mCurModule->mStackDepth; - mArgumentBase = prev->mArgumentBase; - mThis = prev->mThis; - } - resizeStack(hndlr->mStackSize); pc = hndlr->mPC; pushValue(x); @@ -2059,7 +2084,7 @@ static JSValue objectSpittingImage(Context * /*cx*/, const JSValue& /*thisValue* JSType *t1 = r1.getType(); JSType *t2 = r2.getType(); - if (t1 != t2) { + if ((t1 != t2) || (r1.isObject() != r2.isObject())) { return kFalseValue; } else { diff --git a/mozilla/js2/src/js2runtime.cpp b/mozilla/js2/src/js2runtime.cpp index e1d7fa7065a..c54dbd9f043 100644 --- a/mozilla/js2/src/js2runtime.cpp +++ b/mozilla/js2/src/js2runtime.cpp @@ -1788,7 +1788,7 @@ static JSValue Object_Constructor(Context *cx, const JSValue& thisValue, JSValue return thatValue; } -static JSValue Object_toString(Context *, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) +static JSValue Object_toString(Context * /* cx */, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) { if (thisValue.isObject()) return JSValue(new String(widenCString("[object ") + *thisValue.object->getType()->mClassName + widenCString("]"))); @@ -1804,6 +1804,11 @@ static JSValue Object_toString(Context *, const JSValue& thisValue, JSValue * /* } } +static JSValue Object_valueOf(Context * /* cx */, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/) +{ + return thisValue; +} + struct IteratorDongle { JSObject *obj; PropertyIterator it; @@ -1976,6 +1981,68 @@ static JSValue Function_hasInstance(Context *cx, const JSValue& thisValue, JSVal return kFalseValue; } +static JSValue Function_call(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) +{ + if (!thisValue.isFunction()) + cx->reportError(Exception::typeError, "Non-callable object for Function.call"); + + JSValue thisArg; + if (argc == 0) + thisArg = JSValue(cx->getGlobalObject()); + else { + if (argv[0].isUndefined() || argv[0].isNull()) + thisArg = JSValue(cx->getGlobalObject()); + else + thisArg = JSValue(argv[0].toObject(cx)); + --argc; + ++argv; + } + return cx->invokeFunction(thisValue.function, thisArg, argv, argc); +} + +static JSValue Function_apply(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) +{ + if (!thisValue.isFunction()) + cx->reportError(Exception::typeError, "Non-callable object for Function.call"); + + ContextStackReplacement csr(cx); + + JSValue thisArg; + if (argc == 0) + thisArg = JSValue(cx->getGlobalObject()); + else { + if (argv[0].isUndefined() || argv[0].isNull()) + thisArg = JSValue(cx->getGlobalObject()); + else + thisArg = JSValue(argv[0].toObject(cx)); + } + if (argc <= 1) { + argv = NULL; + argc = 0; + } + else { + if (argv[1].getType() != Array_Type) + cx->reportError(Exception::typeError, "Function.apply must have Array type argument list"); + + ASSERT(argv[1].isObject()); + JSObject *argsObj = argv[1].object; + argsObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); + JSValue result = cx->popValue(); + argc = (uint32)(result.toUInt32(cx).f64); + + argv = new JSValue[argc]; + for (uint32 i = 0; i < argc; i++) { + const String *id = numberToString(i); + argsObj->getProperty(cx, *id, CURRENT_ATTR); + argv[i] = cx->popValue(); + delete id; + } + } + + return cx->invokeFunction(thisValue.function, thisArg, argv, argc); +} + + static JSValue Number_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { JSValue v = thisValue; @@ -2609,6 +2676,7 @@ void Context::initBuiltins() { "forin", Object_Type, 0, Object_forin }, { "next", Object_Type, 0, Object_next }, { "done", Object_Type, 0, Object_done }, + { "valueOf", Object_Type, 0, Object_valueOf }, { NULL } }; ProtoFunDef functionProtos[] = @@ -2616,6 +2684,8 @@ void Context::initBuiltins() { "toString", String_Type, 0, Function_toString }, { "toSource", String_Type, 0, Function_toString }, { "hasInstance", Boolean_Type, 1, Function_hasInstance }, + { "call", Object_Type, 1, Function_call }, + { "apply", Object_Type, 2, Function_apply }, { NULL } }; ProtoFunDef numberProtos[] =