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
Родитель 570c37620c
Коммит a69ef8533a
5 изменённых файлов: 336 добавлений и 278 удалений

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -4763,21 +4763,34 @@ js_DecompileFunction(JSPrinter *jp)
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 *
js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
JSString *fallback)
{
JSStackFrame *fp, *down;
JSStackFrame *fp;
jsbytecode *pc, *begin, *end;
jsval *sp, *spbase, *base, *limit;
intN depth, pcdepth;
JSScript *script;
JSOp op;
const JSCodeSpec *cs;
jssrcnote *sn;
ptrdiff_t len, oplen;
ptrdiff_t len;
intN pcdepth;
jsval *sp;
JSPrinter *jp;
char *name;
@ -4790,113 +4803,73 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
if (!fp)
goto do_fallback;
/* Try to find sp's generating pc depth slots under it on the stack. */
pc = fp->pc;
sp = fp->sp;
spbase = fp->spbase;
if ((uintN)(sp - spbase) > fp->script->depth) {
/*
* Preparing to make an internal invocation, using an argv stack
* segment pushed just above fp's operand stack space. Such an argv
* stack has no generating pc "basement", so we must fall back.
*/
if (!pc)
goto do_fallback;
script = fp->script;
if (pc < script->main || script->code + script->length <= pc) {
JS_NOT_REACHED("bug");
goto do_fallback;
}
if (spindex == JSDVG_SEARCH_STACK) {
if (!pc) {
/*
* Current frame is native: look under it for a scripted call
* in which a decompilable bytecode string that generated the
* value as an actual argument might exist.
*/
JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun)));
down = fp->down;
if (!down)
goto do_fallback;
script = down->script;
spbase = down->spbase;
base = fp->argv;
limit = base + fp->argc;
if (spindex != JSDVG_IGNORE_STACK) {
jsbytecode **pcstack;
/*
* Prepare computing pcstack containing pointers to opcodes that
* populated interpreter's stack with its current content.
*/
pcstack = (jsbytecode **)
JS_malloc(cx, script->depth * sizeof *pcstack);
if (!pcstack)
return NULL;
pcdepth = ReconstructPCStack(cx, script, fp->pc, pcstack);
if (pcdepth < 0)
goto release_pcstack;
if (spindex != JSDVG_SEARCH_STACK) {
JS_ASSERT(spindex < 0);
pcdepth += spindex;
if (pcdepth < 0)
goto release_pcstack;
pc = pcstack[pcdepth];
} else {
/*
* This should be a script activation, either a top-level
* script or a scripted function. But be paranoid about calls
* to js_DecompileValueGenerator from code that hasn't fully
* initialized a (default-all-zeroes) frame.
* We search from fp->sp to base to find the most recently
* calculated value matching v under assumption that it is
* it that caused exception, see bug 328664.
*/
script = fp->script;
spbase = base = fp->spbase;
limit = fp->sp;
}
JS_ASSERT((size_t) (fp->sp - fp->spbase) <= fp->script->depth);
sp = fp->sp;
do {
if (sp == fp->spbase) {
pcdepth = -1;
goto release_pcstack;
}
} while (*--sp != v);
/*
* Pure paranoia about default-zeroed frames being active while
* js_DecompileValueGenerator is called. It can't hurt much now;
* error reporting performance is not an issue.
*/
if (!script || !base || !limit)
goto do_fallback;
/*
* 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;
if (sp >= fp->spbase + pcdepth) {
/*
* This happens when the value comes from a temporary slot
* that the interpreter uses for GC roots. Assume that it is
* fp->pc that caused the exception.
*/
pc = fp->pc;
} else {
pc = pcstack[sp - fp->spbase];
}
}
} 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) {
JS_ASSERT(spindex < 0);
depth = (intN)script->depth;
#if !JS_HAS_NO_SUCH_METHOD
JS_ASSERT(-depth <= spindex);
#endif
sp = fp->sp + spindex;
if ((jsuword) (sp - fp->spbase) < (jsuword) depth)
pc = (jsbytecode *) *(sp - depth);
}
release_pcstack:
JS_free(cx, pcstack);
if (pcdepth < 0)
goto do_fallback;
}
/*
* Again, be paranoid, this time about possibly loading an invalid pc
* from fp->sp[spindex - script->depth)].
* We know the address of the opcode that triggered the diagnostic. Find
* 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;
if (op == JSOP_TRAP)
op = JS_GetTrapOpcode(cx, script, pc);
@ -4952,16 +4925,57 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
if (len <= 0)
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: Optimize to use last empty-stack sequence point.
*/
LOCAL_ASSERT(script->main <= pc && pc < script->code + script->length);
pcdepth = 0;
begin = pc;
for (pc = script->main; pc < begin; pc += oplen) {
uint32 type;
intN nuses, ndefs;
op = (JSOp) *pc;
if (op == JSOP_TRAP)
op = JS_GetTrapOpcode(cx, script, pc);
@ -4970,6 +4984,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
if (op == JSOP_POPN) {
pcdepth -= GET_UINT16(pc);
LOCAL_ASSERT(pcdepth >= 0);
continue;
}
@ -5002,6 +5017,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
* since we have moved beyond the IFEQ now.
*/
--pcdepth;
LOCAL_ASSERT(pcdepth >= 0);
}
}
@ -5068,7 +5084,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
nuses = GET_UINT16(pc);
}
pcdepth -= nuses;
JS_ASSERT(pcdepth >= 0);
LOCAL_ASSERT(pcdepth >= 0);
ndefs = cs->ndefs;
if (op == JSOP_FINALLY) {
@ -5083,27 +5099,65 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth);
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;
}
LOCAL_ASSERT(pc == begin);
return pcdepth;
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));
#undef LOCAL_ASSERT
}
#undef LOCAL_ASSERT_RV

Просмотреть файл

@ -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.
*/
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)
/*
@ -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_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_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_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)

Просмотреть файл

@ -66,6 +66,12 @@
# define GENERATOR_INIT js_InitNullClass
#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
# define FILE_INIT js_InitFileClass
#else
@ -109,6 +115,7 @@ JS_PROTO(UnusedProto28, 28, js_InitNullClass)
JS_PROTO(File, 29, FILE_INIT)
JS_PROTO(Block, 30, js_InitBlockClass)
JS_PROTO(XMLFilter, 31, XMLFILTER_INIT)
JS_PROTO(NoSuchMethod, 32, NO_SUCH_METHOD_INIT)
#undef SCRIPT_INIT
#undef XML_INIT
@ -118,3 +125,4 @@ JS_PROTO(XMLFilter, 31, XMLFILTER_INIT)
#undef ATTRIBUTE_INIT
#undef GENERATOR_INIT
#undef FILE_INIT
#undef NO_SUCH_METHOD_INIT