mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug=420399 r=brendan a1.9=blocking1.9 eliminating the pc stack in the interpreter
This commit is contained in:
parent
fb447201e2
commit
03f433c720
@ -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);
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user