bug=420399 r=brendan a1.9=blocking1.9 eliminating the pc stack in the interpreter

This commit is contained in:
igor@mir2.org 2008-03-04 15:30:58 -08:00
parent fb447201e2
commit 03f433c720
5 changed files with 336 additions and 278 deletions

View File

@ -843,91 +843,116 @@ js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
#if JS_HAS_NO_SUCH_METHOD #if JS_HAS_NO_SUCH_METHOD
static JSBool JSClass js_NoSuchMethodClass = {
NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags) "NoSuchMethod",
JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
JSObject*
js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
{ {
JSStackFrame *fp; JSObject *proto;
JSObject *thisp, *argsobj;
JSAtom *atom; proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
jsval roots[3]; NULL, NULL, NULL);
if (!proto)
return NULL;
OBJ_SET_PROTO(cx, proto, NULL);
return proto;
}
/*
* When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
* the base object, we search for the __noSuchMethod__ method in the base.
* If it exists, we store the method and the property's id into an object of
* NoSuchMethod class and store this object into the callee's stack slot.
* Later, js_Invoke will recognise such an object and transfer control to
* NoSuchMethod that invokes the method like:
*
* this.__noSuchMethod__(id, args)
*
* where id is the name of the method that this invocation attempted to
* call by name, and args is an Array containing this invocation's actual
* parameters.
*/
JSBool
js_OnUnknownMethod(JSContext *cx, jsval *vp)
{
JSObject *obj;
JSTempValueRooter tvr; JSTempValueRooter tvr;
jsid id; jsid id;
JSBool ok; JSBool ok;
jsbytecode *pc;
/* JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
* NB: vp[1] aka |this| may be null still, requiring obj = JSVAL_TO_OBJECT(vp[1]);
* js_ComputeGlobalThis. JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
*/
JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0]));
JS_ASSERT(JSVAL_IS_OBJECT(vp[1]));
fp = cx->fp;
/* From here on, control must flow through label out: to return. */
memset(roots, 0, sizeof roots);
JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
thisp = JSVAL_TO_OBJECT(vp[1]);
if (!thisp) {
thisp = js_ComputeGlobalThis(cx, JS_FALSE, vp + 2);
if (!thisp) {
ok = JS_FALSE;
goto out;
}
fp->thisp = thisp;
}
/* From here on, control must flow through label out:. */
id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
if (OBJECT_IS_XML(cx, thisp)) { if (OBJECT_IS_XML(cx, obj)) {
JSXMLObjectOps *ops; JSXMLObjectOps *ops;
ops = (JSXMLObjectOps *) thisp->map->ops; ops = (JSXMLObjectOps *) obj->map->ops;
thisp = ops->getMethod(cx, thisp, id, &roots[2]); obj = ops->getMethod(cx, obj, id, &tvr.u.value);
if (!thisp) { if (!obj) {
ok = JS_FALSE; ok = JS_FALSE;
goto out; goto out;
} }
vp[1] = OBJECT_TO_JSVAL(thisp); vp[1] = OBJECT_TO_JSVAL(obj);
} else } else
#endif #endif
{ {
ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]); ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
if (!ok) if (!ok)
goto out; goto out;
} }
if (JSVAL_IS_PRIMITIVE(roots[2])) if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
goto not_function; vp[0] = tvr.u.value;
} else {
pc = (jsbytecode *) vp[-(intN)fp->script->depth]; obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL);
switch ((JSOp) *pc) { if (!obj) {
case JSOP_NAME:
case JSOP_GETPROP:
case JSOP_CALLPROP:
GET_ATOM_FROM_BYTECODE(fp->script, pc, 0, atom);
roots[0] = ATOM_KEY(atom);
argsobj = js_NewArrayObject(cx, argc, vp + 2);
if (!argsobj) {
ok = JS_FALSE; ok = JS_FALSE;
goto out; goto out;
} }
roots[1] = OBJECT_TO_JSVAL(argsobj); STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, tvr.u.value);
ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL, STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE + 1, vp[0]);
2, roots, &vp[0]); vp[0] = OBJECT_TO_JSVAL(obj);
break;
default:
goto not_function;
} }
ok = JS_TRUE;
out: out:
JS_POP_TEMP_ROOT(cx, &tvr); JS_POP_TEMP_ROOT(cx, &tvr);
return ok; return ok;
}
not_function: static JSBool
js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
ok = JS_FALSE; {
goto out; JSObject *obj, *thisp, *argsobj;
jsval fval, idval, args[2];
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
obj = JSVAL_TO_OBJECT(vp[0]);
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
fval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
idval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE + 1);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
thisp = JSVAL_TO_OBJECT(vp[1]);
args[0] = idval;
argsobj = js_NewArrayObject(cx, argc, vp + 2);
if (!argsobj)
return JS_FALSE;
args[1] = OBJECT_TO_JSVAL(argsobj);
return js_InternalInvoke(cx, thisp, fval, flags | JSINVOKE_INTERNAL,
2, args, &vp[0]);
} }
#endif /* JS_HAS_NO_SUCH_METHOD */ #endif /* JS_HAS_NO_SUCH_METHOD */
@ -987,31 +1012,20 @@ js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
mark = JS_ARENA_MARK(&cx->stackPool); mark = JS_ARENA_MARK(&cx->stackPool);
v = *vp; v = *vp;
/* if (JSVAL_IS_PRIMITIVE(v))
* A callee must be an object reference, unless its 'this' parameter
* implements the __noSuchMethod__ method, in which case that method will
* be called like so:
*
* this.__noSuchMethod__(id, args)
*
* where id is the name of the method that this invocation attempted to
* call by name, and args is an Array containing this invocation's actual
* parameters.
*/
if (JSVAL_IS_PRIMITIVE(v)) {
#if JS_HAS_NO_SUCH_METHOD
if (cx->fp && cx->fp->script && !(flags & JSINVOKE_INTERNAL)) {
ok = NoSuchMethod(cx, argc, vp, flags);
goto out2;
}
#endif
goto bad; goto bad;
}
funobj = JSVAL_TO_OBJECT(v); funobj = JSVAL_TO_OBJECT(v);
parent = OBJ_GET_PARENT(cx, funobj); parent = OBJ_GET_PARENT(cx, funobj);
clasp = OBJ_GET_CLASS(cx, funobj); clasp = OBJ_GET_CLASS(cx, funobj);
if (clasp != &js_FunctionClass) { if (clasp != &js_FunctionClass) {
#if JS_HAS_NO_SUCH_METHOD
if (clasp == &js_NoSuchMethodClass) {
ok = NoSuchMethod(cx, argc, vp, flags);
goto out2;
}
#endif
/* Function is inlined, all other classes use object ops. */ /* Function is inlined, all other classes use object ops. */
ops = funobj->map->ops; ops = funobj->map->ops;
@ -2153,8 +2167,6 @@ js_DumpOpMeters()
* to a function that may invoke the interpreter. RESTORE_SP must be called * to a function that may invoke the interpreter. RESTORE_SP must be called
* only after return from js_Invoke, because only js_Invoke changes fp->sp. * only after return from js_Invoke, because only js_Invoke changes fp->sp.
*/ */
#define PUSH(v) (*sp++ = (v))
#define POP() (*--sp)
#define SAVE_SP(fp) \ #define SAVE_SP(fp) \
(JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \
(fp)->sp = sp) (fp)->sp = sp)
@ -2171,22 +2183,17 @@ js_DumpOpMeters()
#define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc) #define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc)
#define ASSERT_SAVED_SP_AND_PC(fp) JS_ASSERT((fp)->sp == sp && (fp)->pc == pc); #define ASSERT_SAVED_SP_AND_PC(fp) JS_ASSERT((fp)->sp == sp && (fp)->pc == pc);
/* #define PUSH(v) (*sp++ = (v))
* Push the generating bytecode's pc onto the parallel pc stack that runs #define PUSH_OPND(v) PUSH(v)
* depth slots below the operands. #define STORE_OPND(n,v) (sp[n] = (v))
* #define POP() (*--sp)
* NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See
* js_Interpret for these local variables' declarations and uses.
*/
#define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v))
#define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v))
#define POP_OPND() POP() #define POP_OPND() POP()
#define FETCH_OPND(n) (sp[n]) #define FETCH_OPND(n) (sp[n])
/* /*
* Push the jsdouble d using sp, depth, and pc from the lexical environment. * Push the jsdouble d using sp from the lexical environment. Try to convert d
* Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a
* for it and push a reference. * reference.
*/ */
#define STORE_NUMBER(cx, n, d) \ #define STORE_NUMBER(cx, n, d) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
@ -2416,7 +2423,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
JSVersion currentVersion, originalVersion; JSVersion currentVersion, originalVersion;
JSBool ok, cond; JSBool ok, cond;
JSTrapHandler interruptHandler; JSTrapHandler interruptHandler;
jsint depth, len; jsint len;
jsval *sp; jsval *sp;
void *mark; void *mark;
jsbytecode *endpc, *pc2; jsbytecode *endpc, *pc2;
@ -2589,28 +2596,26 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
/* From this point the control must flow through the label exit. /* From this point the control must flow through the label exit.
* *
* Allocate operand and pc stack slots for the script's worst-case depth, * Allocate operand stack slots for the script's worst-case depth, unless
* unless we're resuming a generator. * we're resuming a generator.
*/ */
depth = (jsint) script->depth;
if (JS_LIKELY(!fp->spbase)) { if (JS_LIKELY(!fp->spbase)) {
ASSERT_NOT_THROWING(cx); ASSERT_NOT_THROWING(cx);
JS_ASSERT(!fp->sp); JS_ASSERT(!fp->sp);
sp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); sp = js_AllocRawStack(cx, script->depth, &mark);
if (!sp) { if (!sp) {
mark = NULL; mark = NULL;
ok = JS_FALSE; ok = JS_FALSE;
goto exit; goto exit;
} }
JS_ASSERT(mark); JS_ASSERT(mark);
sp += depth; /* skip pc stack slots */
fp->spbase = sp; fp->spbase = sp;
SAVE_SP(fp); SAVE_SP(fp);
} else { } else {
JS_ASSERT(fp->flags & JSFRAME_GENERATOR); JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
mark = NULL; mark = NULL;
RESTORE_SP(fp); RESTORE_SP(fp);
JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); JS_ASSERT((size_t) (sp - fp->spbase) <= script->depth);
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0); JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp); JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp);
@ -2765,10 +2770,6 @@ interrupt:
END_CASE(JSOP_POPN) END_CASE(JSOP_POPN)
BEGIN_CASE(JSOP_SWAP) BEGIN_CASE(JSOP_SWAP)
vp = sp - depth; /* swap generating pc's for the decompiler */
ltmp = vp[-1];
vp[-1] = vp[-2];
sp[-2] = ltmp;
rtmp = sp[-1]; rtmp = sp[-1];
sp[-1] = sp[-2]; sp[-1] = sp[-2];
sp[-2] = rtmp; sp[-2] = rtmp;
@ -2894,13 +2895,9 @@ interrupt:
/* Restore the calling script's interpreter registers. */ /* Restore the calling script's interpreter registers. */
script = fp->script; script = fp->script;
depth = (jsint) script->depth;
atoms = script->atomMap.vector; atoms = script->atomMap.vector;
pc = fp->pc; pc = fp->pc;
/* Store the generating pc for the return value. */
vp[-depth] = (jsval)pc;
/* Resume execution in the calling frame. */ /* Resume execution in the calling frame. */
inlineCallCount--; inlineCallCount--;
if (JS_LIKELY(ok)) { if (JS_LIKELY(ok)) {
@ -3135,7 +3132,7 @@ interrupt:
case JSOP_FORLOCAL: case JSOP_FORLOCAL:
slot = GET_UINT16(pc); slot = GET_UINT16(pc);
JS_ASSERT(slot < (uintN)depth); JS_ASSERT(slot < script->depth);
vp = &fp->spbase[slot]; vp = &fp->spbase[slot];
GC_POKE(cx, *vp); GC_POKE(cx, *vp);
*vp = rval; *vp = rval;
@ -3191,20 +3188,14 @@ interrupt:
BEGIN_CASE(JSOP_DUP) BEGIN_CASE(JSOP_DUP)
JS_ASSERT(sp > fp->spbase); JS_ASSERT(sp > fp->spbase);
vp = sp - 1; /* address top of stack */ rval = FETCH_OPND(-1);
rval = *vp;
vp -= depth; /* address generating pc */
vp[1] = *vp;
PUSH(rval); PUSH(rval);
END_CASE(JSOP_DUP) END_CASE(JSOP_DUP)
BEGIN_CASE(JSOP_DUP2) BEGIN_CASE(JSOP_DUP2)
JS_ASSERT(sp - 2 >= fp->spbase); JS_ASSERT(sp - 2 >= fp->spbase);
vp = sp - 1; /* address top of stack */ lval = FETCH_OPND(-2);
lval = vp[-1]; rval = FETCH_OPND(-1);
rval = *vp;
vp -= depth; /* address generating pc */
vp[1] = vp[2] = *vp;
PUSH(lval); PUSH(lval);
PUSH(rval); PUSH(rval);
END_CASE(JSOP_DUP2) END_CASE(JSOP_DUP2)
@ -3548,7 +3539,6 @@ interrupt:
END_CASE(JSOP_STRICTNE) END_CASE(JSOP_STRICTNE)
BEGIN_CASE(JSOP_CASE) BEGIN_CASE(JSOP_CASE)
pc2 = (jsbytecode *) sp[-2-depth];
STRICT_EQUALITY_OP(==); STRICT_EQUALITY_OP(==);
(void) POP(); (void) POP();
if (cond) { if (cond) {
@ -3556,12 +3546,10 @@ interrupt:
CHECK_BRANCH(len); CHECK_BRANCH(len);
DO_NEXT_OP(len); DO_NEXT_OP(len);
} }
sp[-depth] = (jsval)pc2;
PUSH(lval); PUSH(lval);
END_CASE(JSOP_CASE) END_CASE(JSOP_CASE)
BEGIN_CASE(JSOP_CASEX) BEGIN_CASE(JSOP_CASEX)
pc2 = (jsbytecode *) sp[-2-depth];
STRICT_EQUALITY_OP(==); STRICT_EQUALITY_OP(==);
(void) POP(); (void) POP();
if (cond) { if (cond) {
@ -3569,7 +3557,6 @@ interrupt:
CHECK_BRANCH(len); CHECK_BRANCH(len);
DO_NEXT_OP(len); DO_NEXT_OP(len);
} }
sp[-depth] = (jsval)pc2;
PUSH(lval); PUSH(lval);
END_CASE(JSOP_CASEX) END_CASE(JSOP_CASEX)
@ -3779,7 +3766,6 @@ interrupt:
goto error; goto error;
sp[-1] = rval; sp[-1] = rval;
} }
sp[-1-depth] = (jsval)pc;
END_CASE(JSOP_POS) END_CASE(JSOP_POS)
BEGIN_CASE(JSOP_NEW) BEGIN_CASE(JSOP_NEW)
@ -3792,7 +3778,6 @@ interrupt:
if (!js_InvokeConstructor(cx, vp, argc)) if (!js_InvokeConstructor(cx, vp, argc))
goto error; goto error;
sp = vp + 1; sp = vp + 1;
vp[-depth] = (jsval)pc;
LOAD_INTERRUPT_HANDLER(cx); LOAD_INTERRUPT_HANDLER(cx);
END_CASE(JSOP_NEW) END_CASE(JSOP_NEW)
@ -3837,8 +3822,7 @@ interrupt:
END_CASE(JSOP_TYPEOF) END_CASE(JSOP_TYPEOF)
BEGIN_CASE(JSOP_VOID) BEGIN_CASE(JSOP_VOID)
(void) POP_OPND(); STORE_OPND(-1, JSVAL_VOID);
PUSH_OPND(JSVAL_VOID);
END_CASE(JSOP_VOID) END_CASE(JSOP_VOID)
BEGIN_CASE(JSOP_INCELEM) BEGIN_CASE(JSOP_INCELEM)
@ -4110,7 +4094,7 @@ interrupt:
BEGIN_CASE(JSOP_GETLOCALPROP) BEGIN_CASE(JSOP_GETLOCALPROP)
i = UINT16_LEN; i = UINT16_LEN;
slot = GET_UINT16(pc); slot = GET_UINT16(pc);
JS_ASSERT(slot < (uintN)depth); JS_ASSERT(slot < script->depth);
PUSH_OPND(fp->spbase[slot]); PUSH_OPND(fp->spbase[slot]);
len = JSOP_GETLOCALPROP_LENGTH; len = JSOP_GETLOCALPROP_LENGTH;
goto do_getprop_body; goto do_getprop_body;
@ -4243,7 +4227,7 @@ interrupt:
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
STORE_OPND(-1, rval); STORE_OPND(-1, rval);
PUSH_OPND(lval); PUSH_OPND(lval);
goto end_callname; goto end_callprop;
} }
} else { } else {
entry = NULL; entry = NULL;
@ -4284,7 +4268,7 @@ interrupt:
STORE_OPND(-2, rval); STORE_OPND(-2, rval);
} }
end_callname: end_callprop:
/* Wrap primitive lval in object clothing if necessary. */ /* Wrap primitive lval in object clothing if necessary. */
if (JSVAL_IS_PRIMITIVE(lval)) { if (JSVAL_IS_PRIMITIVE(lval)) {
/* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
@ -4296,6 +4280,15 @@ interrupt:
goto error; goto error;
} }
} }
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(rval == JSVAL_VOID)) {
LOAD_ATOM(0);
sp[-2] = ATOM_KEY(atom);
SAVE_SP_AND_PC(fp);
if (!js_OnUnknownMethod(cx, sp - 2))
goto error;
}
#endif
} }
END_CASE(JSOP_CALLPROP) END_CASE(JSOP_CALLPROP)
@ -4522,10 +4515,10 @@ interrupt:
if (JSVAL_IS_INT(rval)) { if (JSVAL_IS_INT(rval)) {
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
jsuint length; jsuint length;
length = ARRAY_DENSE_LENGTH(obj); length = ARRAY_DENSE_LENGTH(obj);
i = JSVAL_TO_INT(rval); i = JSVAL_TO_INT(rval);
if ((jsuint)i < length && if ((jsuint)i < length &&
i < obj->fslots[JSSLOT_ARRAY_LENGTH]) { i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {
rval = obj->dslots[i]; rval = obj->dslots[i];
if (rval != JSVAL_HOLE) if (rval != JSVAL_HOLE)
@ -4551,8 +4544,19 @@ interrupt:
* CALLPROP does. See bug 362910. * CALLPROP does. See bug 362910.
*/ */
ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval)); ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval));
STORE_OPND(-2, rval); #if JS_HAS_NO_SUCH_METHOD
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); if (JS_UNLIKELY(rval == JSVAL_VOID)) {
sp[-2] = sp[-1];
sp[-1] = OBJECT_TO_JSVAL(obj);
SAVE_SP_AND_PC(fp);
if (!js_OnUnknownMethod(cx, sp - 2))
goto error;
} else
#endif
{
STORE_OPND(-2, rval);
STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
}
END_CASE(JSOP_CALLELEM) END_CASE(JSOP_CALLELEM)
BEGIN_CASE(JSOP_SETELEM) BEGIN_CASE(JSOP_SETELEM)
@ -4618,9 +4622,8 @@ interrupt:
sizeof(jsval)); sizeof(jsval));
nvars = fun->u.i.nvars; nvars = fun->u.i.nvars;
script = fun->u.i.script; script = fun->u.i.script;
depth = (jsint) script->depth;
atoms = script->atomMap.vector; atoms = script->atomMap.vector;
nslots = nframeslots + nvars + 2 * depth; nslots = nframeslots + nvars + script->depth;
/* Allocate missing expected args adjacent to actuals. */ /* Allocate missing expected args adjacent to actuals. */
missing = (fun->nargs > argc) ? fun->nargs - argc : 0; missing = (fun->nargs > argc) ? fun->nargs - argc : 0;
@ -4710,7 +4713,6 @@ interrupt:
sp = newsp; sp = newsp;
while (nvars--) while (nvars--)
PUSH(JSVAL_VOID); PUSH(JSVAL_VOID);
sp += depth;
newifp->frame.spbase = sp; newifp->frame.spbase = sp;
SAVE_SP(&newifp->frame); SAVE_SP(&newifp->frame);
@ -4763,7 +4765,6 @@ interrupt:
RESTORE_SP(fp); RESTORE_SP(fp);
JS_ASSERT(fp->pc == pc); JS_ASSERT(fp->pc == pc);
script = fp->script; script = fp->script;
depth = (jsint) script->depth;
atoms = script->atomMap.vector; atoms = script->atomMap.vector;
js_FreeRawStack(cx, newmark); js_FreeRawStack(cx, newmark);
goto error; goto error;
@ -4791,15 +4792,10 @@ interrupt:
* this frame's operand stack, take the slow path. * this frame's operand stack, take the slow path.
*/ */
nargs = fun->u.n.minargs - argc; nargs = fun->u.n.minargs - argc;
if (sp + nargs > fp->spbase + depth) if (sp + nargs > fp->spbase + script->depth)
goto do_invoke; goto do_invoke;
do { do {
/* PUSH(JSVAL_VOID);
* Use PUSH_OPND to set the proper pc values for
* the extra arguments. The decompiler relies on
* this.
*/
PUSH_OPND(JSVAL_VOID);
} while (--nargs != 0); } while (--nargs != 0);
SAVE_SP(fp); SAVE_SP(fp);
} }
@ -4819,7 +4815,6 @@ interrupt:
if (!ok) if (!ok)
goto error; goto error;
sp = vp + 1; sp = vp + 1;
vp[-depth] = (jsval)pc;
goto end_call; goto end_call;
} }
} }
@ -4836,7 +4831,6 @@ interrupt:
} }
#endif #endif
sp = vp + 1; sp = vp + 1;
vp[-depth] = (jsval)pc;
LOAD_INTERRUPT_HANDLER(cx); LOAD_INTERRUPT_HANDLER(cx);
if (!ok) if (!ok)
goto error; goto error;
@ -4876,7 +4870,6 @@ interrupt:
vp = sp - argc - 2; vp = sp - argc - 2;
ok = js_Invoke(cx, argc, vp, 0); ok = js_Invoke(cx, argc, vp, 0);
sp = vp + 1; sp = vp + 1;
vp[-depth] = (jsval)pc;
LOAD_INTERRUPT_HANDLER(cx); LOAD_INTERRUPT_HANDLER(cx);
if (!ok) if (!ok)
goto error; goto error;
@ -6218,7 +6211,7 @@ interrupt:
*/ */
JS_ASSERT(sp - fp->spbase >= 2); JS_ASSERT(sp - fp->spbase >= 2);
slot = GET_UINT16(pc); slot = GET_UINT16(pc);
JS_ASSERT(slot + 1 < (uintN)depth); JS_ASSERT(slot + 1 < script->depth);
fp->spbase[slot] = POP_OPND(); fp->spbase[slot] = POP_OPND();
END_CASE(JSOP_SETLOCALPOP) END_CASE(JSOP_SETLOCALPOP)
@ -6528,7 +6521,7 @@ interrupt:
LOAD_OBJECT(0); LOAD_OBJECT(0);
JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
vp = sp + OBJ_BLOCK_COUNT(cx, obj); vp = sp + OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(vp <= fp->spbase + depth); JS_ASSERT(vp <= fp->spbase + script->depth);
while (sp < vp) { while (sp < vp) {
STORE_OPND(0, JSVAL_VOID); STORE_OPND(0, JSVAL_VOID);
sp++; sp++;
@ -6565,7 +6558,7 @@ interrupt:
? fp->blockChain ? fp->blockChain
: fp->scopeChain); : fp->scopeChain);
JS_ASSERT(fp->spbase <= blocksp && blocksp <= fp->spbase + depth); JS_ASSERT((size_t) (blocksp - fp->spbase) <= script->depth);
#endif #endif
if (fp->blockChain) { if (fp->blockChain) {
JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass); JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
@ -6599,7 +6592,7 @@ interrupt:
BEGIN_CASE(JSOP_GETLOCAL) BEGIN_CASE(JSOP_GETLOCAL)
BEGIN_CASE(JSOP_CALLLOCAL) BEGIN_CASE(JSOP_CALLLOCAL)
slot = GET_UINT16(pc); slot = GET_UINT16(pc);
JS_ASSERT(slot < (uintN)depth); JS_ASSERT(slot < script->depth);
PUSH_OPND(fp->spbase[slot]); PUSH_OPND(fp->spbase[slot]);
if (op == JSOP_CALLLOCAL) if (op == JSOP_CALLLOCAL)
PUSH_OPND(JSVAL_NULL); PUSH_OPND(JSVAL_NULL);
@ -6607,7 +6600,7 @@ interrupt:
BEGIN_CASE(JSOP_SETLOCAL) BEGIN_CASE(JSOP_SETLOCAL)
slot = GET_UINT16(pc); slot = GET_UINT16(pc);
JS_ASSERT(slot < (uintN)depth); JS_ASSERT(slot < script->depth);
vp = &fp->spbase[slot]; vp = &fp->spbase[slot];
GC_POKE(cx, *vp); GC_POKE(cx, *vp);
*vp = FETCH_OPND(-1); *vp = FETCH_OPND(-1);
@ -6616,7 +6609,7 @@ interrupt:
/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
#define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \ #define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \
slot = GET_UINT16(pc); \ slot = GET_UINT16(pc); \
JS_ASSERT(slot < (uintN)depth); \ JS_ASSERT(slot < script->depth); \
vp = fp->spbase + slot; \ vp = fp->spbase + slot; \
rval = *vp; \ rval = *vp; \
if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
@ -6690,7 +6683,7 @@ interrupt:
BEGIN_CASE(JSOP_ARRAYPUSH) BEGIN_CASE(JSOP_ARRAYPUSH)
slot = GET_UINT16(pc); slot = GET_UINT16(pc);
JS_ASSERT(slot < (uintN)depth); JS_ASSERT(slot < script->depth);
lval = fp->spbase[slot]; lval = fp->spbase[slot];
obj = JSVAL_TO_OBJECT(lval); obj = JSVAL_TO_OBJECT(lval);
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);

View File

@ -60,6 +60,9 @@ JS_BEGIN_EXTERN_C
* with well-known slots, if possible. * with well-known slots, if possible.
*/ */
struct JSStackFrame { struct JSStackFrame {
jsval *sp; /* stack pointer */
jsbytecode *pc; /* program counter */
jsval *spbase; /* operand stack base */
JSObject *callobj; /* lazily created Call object */ JSObject *callobj; /* lazily created Call object */
JSObject *argsobj; /* lazily created arguments object */ JSObject *argsobj; /* lazily created arguments object */
JSObject *varobj; /* variables object, where vars go */ JSObject *varobj; /* variables object, where vars go */
@ -75,9 +78,6 @@ struct JSStackFrame {
JSStackFrame *down; /* previous frame */ JSStackFrame *down; /* previous frame */
void *annotation; /* used by Java security */ void *annotation; /* used by Java security */
JSObject *scopeChain; /* scope chain */ JSObject *scopeChain; /* scope chain */
jsbytecode *pc; /* program counter */
jsval *sp; /* stack pointer */
jsval *spbase; /* operand stack base */
uintN sharpDepth; /* array/object initializer depth */ uintN sharpDepth; /* array/object initializer depth */
JSObject *sharpArray; /* scope for #n= initializer vars */ JSObject *sharpArray; /* scope for #n= initializer vars */
uint32 flags; /* frame flags -- see below */ uint32 flags; /* frame flags -- see below */
@ -492,6 +492,9 @@ js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp);
extern JSBool extern JSBool
js_ImportProperty(JSContext *cx, JSObject *obj, jsid id); js_ImportProperty(JSContext *cx, JSObject *obj, jsid id);
extern JSBool
js_OnUnknownMethod(JSContext *cx, jsval *vp);
/* /*
* JS_OPMETER helper functions. * JS_OPMETER helper functions.
*/ */

View File

@ -4763,21 +4763,34 @@ js_DecompileFunction(JSPrinter *jp)
return JS_TRUE; return JS_TRUE;
} }
#undef LOCAL_ASSERT_RV /*
* Find the depth of the operand stack when the interpreter reaches the given
* pc in script. When pcstack is not null, it must be an array with space for
* at least script->depth elements. On return the array will contain pointers
* to opcodes that populated the interpreter's current operand stack.
*
* This function cannot raise an exception or error. However, due to a risk of
* potential bugs when modeling the stack, the function returns -1 if it
* detects an inconsistency in the model. Such an inconsistency triggers an
* assert in a debug build.
*/
static intN
ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
jsbytecode **pcstack);
char * char *
js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
JSString *fallback) JSString *fallback)
{ {
JSStackFrame *fp, *down; JSStackFrame *fp;
jsbytecode *pc, *begin, *end; jsbytecode *pc, *begin, *end;
jsval *sp, *spbase, *base, *limit;
intN depth, pcdepth;
JSScript *script; JSScript *script;
JSOp op; JSOp op;
const JSCodeSpec *cs; const JSCodeSpec *cs;
jssrcnote *sn; jssrcnote *sn;
ptrdiff_t len, oplen; ptrdiff_t len;
intN pcdepth;
jsval *sp;
JSPrinter *jp; JSPrinter *jp;
char *name; char *name;
@ -4790,113 +4803,73 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
if (!fp) if (!fp)
goto do_fallback; goto do_fallback;
/* Try to find sp's generating pc depth slots under it on the stack. */
pc = fp->pc; pc = fp->pc;
sp = fp->sp; if (!pc)
spbase = fp->spbase; goto do_fallback;
if ((uintN)(sp - spbase) > fp->script->depth) { script = fp->script;
/* if (pc < script->main || script->code + script->length <= pc) {
* Preparing to make an internal invocation, using an argv stack JS_NOT_REACHED("bug");
* segment pushed just above fp's operand stack space. Such an argv
* stack has no generating pc "basement", so we must fall back.
*/
goto do_fallback; goto do_fallback;
} }
if (spindex == JSDVG_SEARCH_STACK) { if (spindex != JSDVG_IGNORE_STACK) {
if (!pc) { jsbytecode **pcstack;
/*
* Current frame is native: look under it for a scripted call /*
* in which a decompilable bytecode string that generated the * Prepare computing pcstack containing pointers to opcodes that
* value as an actual argument might exist. * populated interpreter's stack with its current content.
*/ */
JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun))); pcstack = (jsbytecode **)
down = fp->down; JS_malloc(cx, script->depth * sizeof *pcstack);
if (!down) if (!pcstack)
goto do_fallback; return NULL;
script = down->script; pcdepth = ReconstructPCStack(cx, script, fp->pc, pcstack);
spbase = down->spbase; if (pcdepth < 0)
base = fp->argv; goto release_pcstack;
limit = base + fp->argc;
if (spindex != JSDVG_SEARCH_STACK) {
JS_ASSERT(spindex < 0);
pcdepth += spindex;
if (pcdepth < 0)
goto release_pcstack;
pc = pcstack[pcdepth];
} else { } else {
/* /*
* This should be a script activation, either a top-level * We search from fp->sp to base to find the most recently
* script or a scripted function. But be paranoid about calls * calculated value matching v under assumption that it is
* to js_DecompileValueGenerator from code that hasn't fully * it that caused exception, see bug 328664.
* initialized a (default-all-zeroes) frame.
*/ */
script = fp->script; JS_ASSERT((size_t) (fp->sp - fp->spbase) <= fp->script->depth);
spbase = base = fp->spbase; sp = fp->sp;
limit = fp->sp; do {
} if (sp == fp->spbase) {
pcdepth = -1;
goto release_pcstack;
}
} while (*--sp != v);
/* if (sp >= fp->spbase + pcdepth) {
* Pure paranoia about default-zeroed frames being active while /*
* js_DecompileValueGenerator is called. It can't hurt much now; * This happens when the value comes from a temporary slot
* error reporting performance is not an issue. * that the interpreter uses for GC roots. Assume that it is
*/ * fp->pc that caused the exception.
if (!script || !base || !limit) */
goto do_fallback; pc = fp->pc;
} else {
/* pc = pcstack[sp - fp->spbase];
* Try to find operand-generating pc depth slots below sp.
*
* In the native case, we know the arguments have generating pc's
* under them, on account of fp->down->script being non-null: all
* compiled scripts get depth slots for generating pc's allocated
* upon activation, at the top of js_Interpret.
*
* In the script or scripted function case, the same reasoning
* applies to fp rather than to fp->down.
*
* We search from limit to base to find the most recently calculated
* value matching v under assumption that it is it that caused
* exception, see bug 328664.
*/
for (sp = limit;;) {
if (sp <= base)
goto do_fallback;
--sp;
if (*sp == v) {
depth = (intN)script->depth;
sp -= depth;
pc = (jsbytecode *) *sp;
break;
} }
} }
} else {
/*
* At this point, pc may or may not be null, i.e., we could be in
* a script activation, or we could be in a native frame that was
* called by another native function. Check pc and script.
*/
if (!pc)
goto do_fallback;
script = fp->script;
if (!script)
goto do_fallback;
if (spindex != JSDVG_IGNORE_STACK) { release_pcstack:
JS_ASSERT(spindex < 0); JS_free(cx, pcstack);
depth = (intN)script->depth; if (pcdepth < 0)
#if !JS_HAS_NO_SUCH_METHOD goto do_fallback;
JS_ASSERT(-depth <= spindex);
#endif
sp = fp->sp + spindex;
if ((jsuword) (sp - fp->spbase) < (jsuword) depth)
pc = (jsbytecode *) *(sp - depth);
}
} }
/* /*
* Again, be paranoid, this time about possibly loading an invalid pc * We know the address of the opcode that triggered the diagnostic. Find
* from fp->sp[spindex - script->depth)]. * the decompilation limits for the opcode and its stack depth.
*/ */
if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
pc = fp->pc;
if (!pc)
goto do_fallback;
}
op = (JSOp) *pc; op = (JSOp) *pc;
if (op == JSOP_TRAP) if (op == JSOP_TRAP)
op = JS_GetTrapOpcode(cx, script, pc); op = JS_GetTrapOpcode(cx, script, pc);
@ -4952,16 +4925,57 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
if (len <= 0) if (len <= 0)
goto do_fallback; goto do_fallback;
pcdepth = ReconstructPCStack(cx, script, begin, NULL);
if (pcdepth < 0)
goto do_fallback;
name = NULL;
jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", fp->fun, 0, JS_FALSE);
if (jp) {
jp->dvgfence = end;
if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) {
name = (jp->sprinter.base) ? jp->sprinter.base : (char *) "";
name = JS_strdup(cx, name);
}
js_DestroyPrinter(jp);
}
return name;
do_fallback:
if (!fallback) {
fallback = js_ValueToSource(cx, v);
if (!fallback)
return NULL;
}
return js_DeflateString(cx, JSSTRING_CHARS(fallback),
JSSTRING_LENGTH(fallback));
}
static intN
ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
jsbytecode **pcstack)
{
intN pcdepth, nuses, ndefs;
jsbytecode *begin;
JSOp op;
const JSCodeSpec *cs;
ptrdiff_t oplen;
jssrcnote *sn;
uint32 type;
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
/* /*
* Walk forward from script->main and compute starting stack depth. * Walk forward from script->main and compute the stack depth and stack of
* operand-generating opcode PCs in pcstack.
*
* FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
* FIXME: Optimize to use last empty-stack sequence point. * FIXME: Optimize to use last empty-stack sequence point.
*/ */
LOCAL_ASSERT(script->main <= pc && pc < script->code + script->length);
pcdepth = 0; pcdepth = 0;
begin = pc;
for (pc = script->main; pc < begin; pc += oplen) { for (pc = script->main; pc < begin; pc += oplen) {
uint32 type;
intN nuses, ndefs;
op = (JSOp) *pc; op = (JSOp) *pc;
if (op == JSOP_TRAP) if (op == JSOP_TRAP)
op = JS_GetTrapOpcode(cx, script, pc); op = JS_GetTrapOpcode(cx, script, pc);
@ -4970,6 +4984,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
if (op == JSOP_POPN) { if (op == JSOP_POPN) {
pcdepth -= GET_UINT16(pc); pcdepth -= GET_UINT16(pc);
LOCAL_ASSERT(pcdepth >= 0);
continue; continue;
} }
@ -5002,6 +5017,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
* since we have moved beyond the IFEQ now. * since we have moved beyond the IFEQ now.
*/ */
--pcdepth; --pcdepth;
LOCAL_ASSERT(pcdepth >= 0);
} }
} }
@ -5068,7 +5084,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
nuses = GET_UINT16(pc); nuses = GET_UINT16(pc);
} }
pcdepth -= nuses; pcdepth -= nuses;
JS_ASSERT(pcdepth >= 0); LOCAL_ASSERT(pcdepth >= 0);
ndefs = cs->ndefs; ndefs = cs->ndefs;
if (op == JSOP_FINALLY) { if (op == JSOP_FINALLY) {
@ -5083,27 +5099,65 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth); JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth);
ndefs = OBJ_BLOCK_COUNT(cx, obj); ndefs = OBJ_BLOCK_COUNT(cx, obj);
} }
LOCAL_ASSERT(pcdepth + ndefs <= script->depth);
if (pcstack) {
intN i;
jsbytecode *pc2;
/*
* Fill the slots that the opcode defines withs its pc unless it
* just reshuffle the stack. In the latter case we want to
* preserve the opcode that generated the original value.
*/
switch (op) {
default:
for (i = 0; i != ndefs; ++i)
pcstack[pcdepth + i] = pc;
break;
case JSOP_CASE:
case JSOP_CASEX:
/* Keep the switch value. */
JS_ASSERT(ndefs == 1);
break;
case JSOP_DUP:
JS_ASSERT(ndefs == 2);
pcstack[pcdepth + 1] = pcstack[pcdepth];
break;
case JSOP_DUP2:
JS_ASSERT(ndefs == 4);
pcstack[pcdepth + 2] = pcstack[pcdepth];
pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
break;
case JSOP_SWAP:
JS_ASSERT(ndefs == 2);
pc2 = pcstack[pcdepth];
pcstack[pcdepth] = pcstack[pcdepth + 1];
pcstack[pcdepth + 1] = pc2;
break;
case JSOP_LEAVEBLOCKEXPR:
/*
* The decompiler wants [leaveblockexpr], not [enterblock], to
* be left on pcstack after a simulated let expression.
*/
JS_ASSERT(ndefs == 0);
LOCAL_ASSERT(pcdepth >= 1);
LOCAL_ASSERT(*pcstack[pcdepth - 1] == JSOP_ENTERBLOCK);
pcstack[pcdepth - 1] = pc;
break;
}
}
pcdepth += ndefs; pcdepth += ndefs;
} }
LOCAL_ASSERT(pc == begin);
return pcdepth;
name = NULL; #undef LOCAL_ASSERT
jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", fp->fun, 0, JS_FALSE);
if (jp) {
jp->dvgfence = end;
if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) {
name = (jp->sprinter.base) ? jp->sprinter.base : (char *) "";
name = JS_strdup(cx, name);
}
js_DestroyPrinter(jp);
}
return name;
do_fallback:
if (!fallback) {
fallback = js_ValueToSource(cx, v);
if (!fallback)
return NULL;
}
return js_DeflateString(cx, JSSTRING_CHARS(fallback),
JSSTRING_LENGTH(fallback));
} }
#undef LOCAL_ASSERT_RV

View File

@ -272,7 +272,7 @@ OPDEF(JSOP_UNUSED117, 117,"unused117", NULL, 1, 0, 0, 0, JOF_BYTE)
* lval if false; and DEFAULT is POP lval and GOTO. * lval if false; and DEFAULT is POP lval and GOTO.
*/ */
OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD) OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD)
OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 0, JOF_JUMP) OPDEF(JSOP_CASE, 119,"case", NULL, 3, 2, 1, 0, JOF_JUMP)
OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP)
/* /*
@ -355,7 +355,7 @@ OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX|
OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX) OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX)
OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 0, JOF_JUMPX) OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 2, 1, 0, JOF_JUMPX)
OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX) OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX)
OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD) OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD)
OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD) OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD)

View File

@ -66,6 +66,12 @@
# define GENERATOR_INIT js_InitNullClass # define GENERATOR_INIT js_InitNullClass
#endif #endif
#if JS_HAS_NO_SUCH_METHOD
# define NO_SUCH_METHOD_INIT js_InitNoSuchMethodClass
#else
# define NO_SUCH_METHOD_INIT js_InitNullClass
#endif
#if JS_HAS_FILE_OBJECT #if JS_HAS_FILE_OBJECT
# define FILE_INIT js_InitFileClass # define FILE_INIT js_InitFileClass
#else #else
@ -109,6 +115,7 @@ JS_PROTO(UnusedProto28, 28, js_InitNullClass)
JS_PROTO(File, 29, FILE_INIT) JS_PROTO(File, 29, FILE_INIT)
JS_PROTO(Block, 30, js_InitBlockClass) JS_PROTO(Block, 30, js_InitBlockClass)
JS_PROTO(XMLFilter, 31, XMLFILTER_INIT) JS_PROTO(XMLFilter, 31, XMLFILTER_INIT)
JS_PROTO(NoSuchMethod, 32, NO_SUCH_METHOD_INIT)
#undef SCRIPT_INIT #undef SCRIPT_INIT
#undef XML_INIT #undef XML_INIT
@ -118,3 +125,4 @@ JS_PROTO(XMLFilter, 31, XMLFILTER_INIT)
#undef ATTRIBUTE_INIT #undef ATTRIBUTE_INIT
#undef GENERATOR_INIT #undef GENERATOR_INIT
#undef FILE_INIT #undef FILE_INIT
#undef NO_SUCH_METHOD_INIT