зеркало из https://github.com/mozilla/pjs.git
Added Function.call/apply. Fixed bug in exception handling clean-up of
activation stack.
This commit is contained in:
Родитель
cfa922c672
Коммит
143454c6bd
|
@ -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 {
|
||||
|
|
|
@ -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[] =
|
||||
|
|
Загрузка…
Ссылка в новой задаче