Bug 468782 - TM: js_FastValueToIterator and js_FastCallIteratorNext can reenter. r=brendan. Note that this changeset alone does not fix the bug; an upcoming patch in bug 462027 completes the fix.

This commit is contained in:
Jason Orendorff 2009-01-27 00:19:58 -06:00
parent 519b56ea2f
commit c0cc74fa99
12 changed files with 306 additions and 130 deletions

View File

@ -81,8 +81,6 @@ BUILTIN3(extern, JSVAL, js_Any_getprop, CONTEXT, OBJECT, STRING,
BUILTIN4(extern, BOOL, js_Any_setprop, CONTEXT, OBJECT, STRING, JSVAL, 0, 0)
BUILTIN3(extern, JSVAL, js_Any_getelem, CONTEXT, OBJECT, INT32, 0, 0)
BUILTIN4(extern, BOOL, js_Any_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0)
BUILTIN3(extern, OBJECT, js_FastValueToIterator, CONTEXT, UINT32, JSVAL, 0, 0)
BUILTIN2(extern, JSVAL, js_FastCallIteratorNext, CONTEXT, OBJECT, 0, 0)
BUILTIN2(FRIEND, BOOL, js_CloseIterator, CONTEXT, JSVAL, 0, 0)
BUILTIN2(extern, SIDEEXIT, js_CallTree, INTERPSTATE, FRAGMENT, 0, 0)
BUILTIN2(extern, OBJECT, js_FastNewObject, CONTEXT, OBJECT, 0, 0)

View File

@ -109,11 +109,24 @@ function immediate(op) {
info.flags.indexOf("JOF_INT8") >= 0) {
return (op.imm1 & 0xff);
}
if (info.flags.indexOf("JOF_UINT16") >= 0)
if (info.flags.indexOf("JOF_UINT16") >= 0) {
if (/^\(/.test(op.imm1))
return '(_ & 0xff00) >> 8, (_ & 0xff)'.replace(/_/g, op.imm1);
return ((op.imm1 & 0xff00) >> 8) + ", " + (op.imm1 & 0xff);
}
throw new Error(info.jsop + " format not yet implemented");
}
const line_regexp_parts = [
"^(?:(\\w+):)?",
"\\s*(\\.?\\w+)",
"(?:\\s+(\\w+|\\([^)]*\\)))?",
"(?:\\s+([\\w-]+|\\([^)]*\\)))?",
"(?:\\s*(?:#.*))?$"
];
const line_regexp = new RegExp(line_regexp_parts.join(""));
/*
* Syntax (spaces are significant only to delimit tokens):
*
@ -121,10 +134,13 @@ function immediate(op) {
* Directive ::= (name ':')? Operation
* Operation ::= opname Operands?
* Operands ::= Operand (',' Operand)*
* Operand ::= name | number
* Operand ::= name | number | '(' Expr ')'
* Expr ::= a constant-expression in the C++ language
* containing no parentheses
*
* We simplify given line structure and the maximum of one immediate operand,
* by parsing using split and regexps.
* by parsing using split and regexps. For ease of parsing, parentheses are
* banned in an Expr for now, even in quotes or a C++ comment.
*
* Pseudo-ops start with . and include .igroup and .imacro, terminated by .end.
* .imacro must nest in .igroup, neither nests in itself. See imacros.jsasm for
@ -141,7 +157,7 @@ function assemble(filename) {
for (let i = 0; i < a.length; i++) {
if (/^\s*(?:#.*)?$/.test(a[i]))
continue;
let m = /(?:(\w+):)?\s*(\.?\w+)(?:\s+(\w+))?(?:\s+([\w-]+))?(?:\s*(?:#.*))?$/.exec(a[i]);
let m = line_regexp.exec(a[i]);
if (!m)
throw new Error(a[i]);
@ -208,7 +224,6 @@ function assemble(filename) {
print(" {");
for (let k = 0; k < imacro.code.length; k++) {
let op = imacro.code[k];
print("/*" + formatoffset(op.offset,2) + "*/ " + op.info.jsop +
(op.imm1 ? ", " + immediate(op) : "") + ",");

View File

@ -536,6 +536,64 @@ static struct {
/* 6*/ JSOP_STOP,
},
};
static struct {
jsbytecode for_in[10];
jsbytecode for_each[10];
jsbytecode for_in_native[10];
jsbytecode for_each_native[10];
} iter_imacros = {
{
/* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator),
/* 3*/ JSOP_INT8, 0,
/* 5*/ JSOP_CALL, 0, 1,
/* 8*/ JSOP_PUSH,
/* 9*/ JSOP_STOP,
},
{
/* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator),
/* 3*/ JSOP_INT8, 0,
/* 5*/ JSOP_CALL, 0, 1,
/* 8*/ JSOP_PUSH,
/* 9*/ JSOP_STOP,
},
{
/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff),
/* 3*/ JSOP_INT8, 0,
/* 5*/ JSOP_CALL, 0, 1,
/* 8*/ JSOP_PUSH,
/* 9*/ JSOP_STOP,
},
{
/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff),
/* 3*/ JSOP_INT8, 0,
/* 5*/ JSOP_CALL, 0, 1,
/* 8*/ JSOP_PUSH,
/* 9*/ JSOP_STOP,
},
};
static struct {
jsbytecode custom_iter_next[10];
jsbytecode native_iter_next[12];
} nextiter_imacros = {
{
/* 0*/ JSOP_POP,
/* 1*/ JSOP_DUP,
/* 2*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(next),
/* 5*/ JSOP_CALL, 0, 0,
/* 8*/ JSOP_TRUE,
/* 9*/ JSOP_STOP,
},
{
/* 0*/ JSOP_POP,
/* 1*/ JSOP_DUP,
/* 2*/ JSOP_CALLBUILTIN, ((JSBUILTIN_CallIteratorNext) & 0xff00) >> 8, ((JSBUILTIN_CallIteratorNext) & 0xff),
/* 5*/ JSOP_CALL, 0, 0,
/* 8*/ JSOP_DUP,
/* 9*/ JSOP_HOLE,
/*10*/ JSOP_STRICTNE,
/*11*/ JSOP_STOP,
},
};
uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_NOP */
0, /* JSOP_PUSH */
@ -612,8 +670,8 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_STRICTEQ */
0, /* JSOP_STRICTNE */
0, /* JSOP_NULLTHIS */
0, /* JSOP_ITER */
0, /* JSOP_NEXTITER */
3, /* JSOP_ITER */
2, /* JSOP_NEXTITER */
0, /* JSOP_ENDITER */
7, /* JSOP_APPLY */
0, /* JSOP_SWAP */
@ -763,7 +821,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_CALLGVAR */
0, /* JSOP_CALLLOCAL */
0, /* JSOP_CALLARG */
0, /* JSOP_UNUSED226 */
0, /* JSOP_CALLBUILTIN */
0, /* JSOP_INT8 */
0, /* JSOP_INT32 */
0, /* JSOP_LENGTH */

View File

@ -575,3 +575,63 @@
.end #
.end
.igroup iter JSOP_ITER
.imacro for_in # obj
callprop iterator # fun obj
int8 (JSITER_ENUMERATE) # fun obj flags
call 1 # iterobj
push # iterobj undef
stop
.end
.imacro for_each # obj
callprop iterator # fun obj
int8 (JSITER_ENUMERATE|JSITER_FOREACH) # fun obj flags
call 1 # iterobj
push # iterobj undef
stop
.end
.imacro for_in_native # obj
callbuiltin (JSBUILTIN_ObjectToIterator) # fun obj
int8 JSITER_ENUMERATE # fun obj flags
call 1 # iterobj
push # iterobj undef
stop
.end
.imacro for_each_native # obj
callbuiltin (JSBUILTIN_ObjectToIterator) # fun obj
int8 (JSITER_ENUMERATE|JSITER_FOREACH) # fun obj flags
call 1 # iterobj
push # iterobj undef
stop
.end
.end
.igroup nextiter JSOP_NEXTITER
.imacro custom_iter_next # iterobj prevval
pop # iterobj
dup # iterobj iterobj
callprop next # iterobj fun iterobj
call 0 # iterobj nextval
true # iterobj nextval true
stop
.end
.imacro native_iter_next # iterobj prevval
pop # iterobj
dup # iterobj iterobj
callbuiltin (JSBUILTIN_CallIteratorNext) # iterobj fun iterobj
call 0 # iterobj nextval?
dup # iterobj nextval? nextval?
hole # iterobj nextval? nextval? hole
strictne # iterobj nextval? boolean
stop
.end
.end

View File

@ -243,23 +243,6 @@ js_Any_setelem(JSContext* cx, JSObject* obj, int32 index, jsval v)
return OBJ_SET_PROPERTY(cx, obj, id, &v);
}
JSObject* FASTCALL
js_FastValueToIterator(JSContext* cx, jsuint flags, jsval v)
{
if (!js_ValueToIterator(cx, flags, &v))
return NULL;
return JSVAL_TO_OBJECT(v);
}
jsval FASTCALL
js_FastCallIteratorNext(JSContext* cx, JSObject* iterobj)
{
jsval v;
if (!js_CallIteratorNext(cx, iterobj, &v))
return JSVAL_ERROR_COOKIE;
return v;
}
SideExit* FASTCALL
js_CallTree(InterpState* state, Fragment* f)
{

View File

@ -232,6 +232,12 @@ typedef enum JSRuntimeState {
JSRTS_LANDING
} JSRuntimeState;
typedef enum JSBuiltinFunctionId {
JSBUILTIN_ObjectToIterator,
JSBUILTIN_CallIteratorNext,
JSBUILTIN_LIMIT
} JSBuiltinFunctionId;
typedef struct JSPropertyTreeEntry {
JSDHashEntryHdr hdr;
JSScopeProperty *child;
@ -340,6 +346,14 @@ struct JSRuntime {
JSString *emptyString;
JSString **unitStrings;
/*
* Builtin functions, lazily created and held for use by the trace recorder.
*
* This field would be #ifdef JS_TRACER, but XPConnect is compiled without
* -DJS_TRACER and includes this header.
*/
JSObject *builtinFunctions[JSBUILTIN_LIMIT];
/* List of active contexts sharing this runtime; protected by gcLock. */
JSCList contextList;

View File

@ -3105,6 +3105,11 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
#ifdef JS_TRACER
for (int i = 0; i < JSBUILTIN_LIMIT; i++) {
if (rt->builtinFunctions[i])
JS_CALL_OBJECT_TRACER(trc, rt->builtinFunctions[i], "builtin function");
}
#ifdef JS_THREADSAFE
/* Trace the loop table(s) which can contain pointers to code objects. */
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {

View File

@ -3221,7 +3221,6 @@ js_Interpret(JSContext *cx)
CHECK_INTERRUPT_HANDLER();
rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
PUSH(rval);
TRACE_0(IteratorNextComplete);
END_CASE(JSOP_NEXTITER)
BEGIN_CASE(JSOP_ENDITER)
@ -6726,6 +6725,19 @@ js_Interpret(JSContext *cx)
}
END_CASE(JSOP_LEAVEBLOCK)
BEGIN_CASE(JSOP_CALLBUILTIN)
#ifdef JS_TRACER
obj = js_GetBuiltinFunction(cx, GET_INDEX(regs.pc));
if (!obj)
goto error;
rval = FETCH_OPND(-1);
PUSH_OPND(rval);
STORE_OPND(-2, OBJECT_TO_JSVAL(obj));
#else
goto bad_opcode; /* This is an imacro-only opcode. */
#endif
END_CASE(JSOP_CALLBUILTIN)
#if JS_HAS_GENERATORS
BEGIN_CASE(JSOP_GENERATOR)
ASSERT_NOT_THROWING(cx);
@ -6835,10 +6847,12 @@ js_Interpret(JSContext *cx)
L_JSOP_UNUSED208:
L_JSOP_UNUSED209:
L_JSOP_UNUSED219:
L_JSOP_UNUSED226:
#else /* !JS_THREADED_INTERP */
default:
#endif
#ifndef JS_TRACER
bad_opcode:
#endif
{
char numBuf[12];
@ -6857,7 +6871,8 @@ js_Interpret(JSContext *cx)
if (fp->imacpc && cx->throwing) {
// To keep things simple, we hard-code imacro exception handlers here.
if (*fp->imacpc == JSOP_NEXTITER) {
JS_ASSERT(*regs.pc == JSOP_CALL);
// pc may point to JSOP_DUP here due to bug 474854.
JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
if (js_ValueIsStopIteration(cx->exception)) {
cx->throwing = JS_FALSE;
cx->exception = JSVAL_VOID;

View File

@ -545,7 +545,7 @@ OPDEF(JSOP_INDEXBASE3, 222,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |
OPDEF(JSOP_CALLGVAR, 223, "callgvar", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLLOCAL, 224, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLARG, 225, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_UNUSED226, 226, "unused226", NULL, 1, 0, 1, 1, JOF_BYTE)
OPDEF(JSOP_CALLBUILTIN, 226, "callbuiltin", NULL, 3, 0, 2, 0, JOF_UINT16)
/*
* Opcodes to hold 8-bit and 32-bit immediate integer operands.

View File

@ -2072,7 +2072,7 @@ TraceRecorder::snapshot(ExitType exitType)
bool resumeAfter = (pendingTraceableNative &&
JSTN_ERRTYPE(pendingTraceableNative) == FAIL_JSVAL);
if (resumeAfter) {
JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEXTITER);
JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY);
pc += cs.length;
regs->pc = pc;
MUST_FLOW_THROUGH("restore_pc");
@ -2099,11 +2099,10 @@ TraceRecorder::snapshot(ExitType exitType)
);
JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots);
/* If we are capturing the stack state on a specific instruction, the value on or near
the top of the stack is a boxed value. Either pc[-cs.length] is JSOP_NEXTITER and we
want one below top of stack, or else it's JSOP_CALL and we want top of stack. */
/* If we are capturing the stack state on a specific instruction, the value on
the top of the stack is a boxed value. */
if (resumeAfter) {
typemap[stackSlots + ((pc[-cs.length] == JSOP_NEXTITER) ? -2 : -1)] = JSVAL_BOXED;
typemap[stackSlots - 1] = JSVAL_BOXED;
/* Now restore the the original pc (after which early returns are ok). */
MUST_FLOW_LABEL(restore_pc);
@ -7674,114 +7673,40 @@ TraceRecorder::record_JSOP_IMACOP()
return true;
}
static struct {
jsbytecode for_in[10];
jsbytecode for_each[10];
} iter_imacros = {
{
JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator),
JSOP_INT8, JSITER_ENUMERATE,
JSOP_CALL, 0, 1,
JSOP_PUSH,
JSOP_STOP
},
{
JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator),
JSOP_INT8, JSITER_ENUMERATE | JSITER_FOREACH,
JSOP_CALL, 0, 1,
JSOP_PUSH,
JSOP_STOP
}
};
JS_STATIC_ASSERT(sizeof(iter_imacros) < IMACRO_PC_ADJ_LIMIT);
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_ITER()
{
jsval& v = stackval(-1);
if (!JSVAL_IS_PRIMITIVE(v)) {
jsuint flags = cx->fp->regs->pc[1];
if (JSVAL_IS_PRIMITIVE(v))
ABORT_TRACE("for-in on a primitive value");
if (!hasIteratorMethod(JSVAL_TO_OBJECT(v))) {
LIns* args[] = { get(&v), INS_CONST(flags), cx_ins };
LIns* v_ins = lir->insCall(&js_FastValueToIterator_ci, args);
guard(false, lir->ins_eq0(v_ins), MISMATCH_EXIT);
set(&v, v_ins);
LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
stack(0, void_ins);
return true;
}
jsuint flags = cx->fp->regs->pc[1];
if (hasIteratorMethod(JSVAL_TO_OBJECT(v))) {
if (flags == JSITER_ENUMERATE)
return call_imacro(iter_imacros.for_in);
if (flags == (JSITER_ENUMERATE | JSITER_FOREACH))
return call_imacro(iter_imacros.for_each);
ABORT_TRACE("unimplemented JSITER_* flags");
} else {
if (flags == JSITER_ENUMERATE)
return call_imacro(iter_imacros.for_in_native);
if (flags == (JSITER_ENUMERATE | JSITER_FOREACH))
return call_imacro(iter_imacros.for_each_native);
}
ABORT_TRACE("for-in on a primitive value");
ABORT_TRACE("unimplemented JSITER_* flags");
}
static JSTraceableNative js_FastCallIteratorNext_tn = {
NULL, // JSFastNative native;
&js_FastCallIteratorNext_ci, // const nanojit::CallInfo *builtin;
"C", // const char *prefix;
"o", // const char *argtypes;
FAIL_JSVAL // uintN flags;
};
static jsbytecode nextiter_imacro[] = {
JSOP_POP,
JSOP_DUP,
JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(next),
JSOP_CALL, 0, 0,
JSOP_TRUE,
JSOP_STOP
};
JS_STATIC_ASSERT(sizeof(nextiter_imacro) < IMACRO_PC_ADJ_LIMIT);
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NEXTITER()
{
jsval& iterobj_val = stackval(-2);
if (!JSVAL_IS_PRIMITIVE(iterobj_val)) {
LIns* iterobj_ins = get(&iterobj_val);
if (JSVAL_IS_PRIMITIVE(iterobj_val))
ABORT_TRACE("for-in on a primitive value");
if (guardClass(JSVAL_TO_OBJECT(iterobj_val), iterobj_ins, &js_IteratorClass, BRANCH_EXIT)) {
LIns* args[] = { iterobj_ins, cx_ins };
LIns* v_ins = lir->insCall(&js_FastCallIteratorNext_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
LIns* flag_ins = lir->ins_eq0(lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_HOLE)));
stack(-1, v_ins);
stack(0, flag_ins);
pendingTraceableNative = &js_FastCallIteratorNext_tn;
return true;
}
// Custom iterator, possibly a generator.
return call_imacro(nextiter_imacro);
}
ABORT_TRACE("for-in on a primitive value");
}
JS_REQUIRES_STACK bool
TraceRecorder::record_IteratorNextComplete()
{
JS_ASSERT(*cx->fp->regs->pc == JSOP_NEXTITER);
JS_ASSERT(pendingTraceableNative == &js_FastCallIteratorNext_tn);
jsval& v = stackval(-2);
LIns* v_ins = get(&v);
unbox_jsval(v, v_ins);
set(&v, v_ins);
return true;
LIns* iterobj_ins = get(&iterobj_val);
if (guardClass(JSVAL_TO_OBJECT(iterobj_val), iterobj_ins, &js_IteratorClass, BRANCH_EXIT))
return call_imacro(nextiter_imacros.native_iter_next);
return call_imacro(nextiter_imacros.custom_iter_next);
}
JS_REQUIRES_STACK bool
@ -8810,6 +8735,97 @@ TraceRecorder::record_JSOP_CALLARG()
return true;
}
/* Functions for use with JSOP_CALLBUILTIN. */
static JSBool
ObjectToIterator(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv = JS_ARGV(cx, vp);
JS_ASSERT(JSVAL_IS_INT(argv[0]));
JS_SET_RVAL(cx, vp, JS_THIS(cx, vp));
return js_ValueToIterator(cx, JSVAL_TO_INT(argv[0]), &JS_RVAL(cx, vp));
}
static JSObject* FASTCALL
ObjectToIterator_tn(JSContext* cx, JSObject *obj, int32 flags)
{
jsval v = OBJECT_TO_JSVAL(obj);
if (!js_ValueToIterator(cx, flags, &v))
return NULL;
return JSVAL_TO_OBJECT(v);
}
static JSBool
CallIteratorNext(JSContext *cx, uintN argc, jsval *vp)
{
return js_CallIteratorNext(cx, JS_THIS_OBJECT(cx, vp), &JS_RVAL(cx, vp));
}
static jsval FASTCALL
CallIteratorNext_tn(JSContext* cx, JSObject* iterobj)
{
jsval v;
if (!js_CallIteratorNext(cx, iterobj, &v))
return JSVAL_ERROR_COOKIE;
return v;
}
JS_DEFINE_TRCINFO_1(ObjectToIterator,
(3, (static, OBJECT_FAIL_NULL, ObjectToIterator_tn, CONTEXT, THIS, INT32, 0, 0)))
JS_DEFINE_TRCINFO_1(CallIteratorNext,
(2, (static, JSVAL_FAIL, CallIteratorNext_tn, CONTEXT, THIS, 0, 0)))
static const struct BuiltinFunctionInfo {
JSTraceableNative *tn;
int nargs;
} builtinFunctionInfo[JSBUILTIN_LIMIT] = {
{ObjectToIterator_trcinfo, 1},
{CallIteratorNext_trcinfo, 0}
};
JSObject *
js_GetBuiltinFunction(JSContext *cx, uintN index)
{
JSRuntime *rt = cx->runtime;
JSObject *funobj = rt->builtinFunctions[index];
if (!funobj) {
/* Use NULL parent and atom. Builtin functions never escape to scripts. */
JSFunction *fun = js_NewFunction(cx,
NULL,
(JSNative) builtinFunctionInfo[index].tn,
builtinFunctionInfo[index].nargs,
JSFUN_FAST_NATIVE | JSFUN_TRACEABLE,
NULL,
NULL);
if (fun) {
funobj = FUN_OBJECT(fun);
STOBJ_CLEAR_PROTO(funobj);
STOBJ_CLEAR_PARENT(funobj);
JS_LOCK_GC(rt);
if (!rt->builtinFunctions[index]) /* retest now that the lock is held */
rt->builtinFunctions[index] = funobj;
else
funobj = rt->builtinFunctions[index];
JS_UNLOCK_GC(rt);
}
}
return funobj;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_CALLBUILTIN()
{
JSObject *obj = js_GetBuiltinFunction(cx, GET_INDEX(cx->fp->regs->pc));
if (!obj)
return false;
stack(0, get(&stackval(-1)));
stack(-1, INS_CONSTPTR(obj));
return true;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NULLTHIS()
{
@ -8968,7 +8984,7 @@ static void
InitIMacroCode()
{
if (imacro_code[JSOP_NEXTITER]) {
JS_ASSERT(imacro_code[JSOP_NEXTITER] == nextiter_imacro - 1);
JS_ASSERT(imacro_code[JSOP_NEXTITER] == (jsbytecode*)&nextiter_imacros - 1);
return;
}
@ -8979,7 +8995,7 @@ InitIMacroCode()
imacro_code[JSOP_ADD] = (jsbytecode*)&add_imacros - 1;
imacro_code[JSOP_ITER] = (jsbytecode*)&iter_imacros - 1;
imacro_code[JSOP_NEXTITER] = nextiter_imacro - 1;
imacro_code[JSOP_NEXTITER] = (jsbytecode*)&nextiter_imacros - 1;
imacro_code[JSOP_APPLY] = (jsbytecode*)&apply_imacros - 1;
imacro_code[JSOP_NEG] = (jsbytecode*)&unary_imacros - 1;
@ -9005,4 +9021,3 @@ UNUSED(207)
UNUSED(208)
UNUSED(209)
UNUSED(219)
UNUSED(226)

View File

@ -518,7 +518,6 @@ public:
JS_REQUIRES_STACK bool record_SetPropMiss(JSPropCacheEntry* entry);
JS_REQUIRES_STACK bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
JS_REQUIRES_STACK bool record_FastNativeCallComplete();
JS_REQUIRES_STACK bool record_IteratorNextComplete();
nanojit::Fragment* getOuterToBlacklist() { return outerToBlacklist; }
void deepAbort() { deepAborted = true; }
@ -575,6 +574,9 @@ js_FlushJITCache(JSContext* cx);
extern void
js_FlushJITOracle(JSContext* cx);
extern JSObject *
js_GetBuiltinFunction(JSContext *cx, uintN index);
#else /* !JS_TRACER */
#define TRACE_0(x) ((void)0)

View File

@ -2545,6 +2545,17 @@ function testApply() {
testApply.expected = "5,5,5,5,5,5,5,5,5,5";
test(testApply);
function testNestedForIn() {
var a = {x: 1, y: 2, z: 3};
var s = '';
for (var p1 in a)
for (var p2 in a)
s += p1 + p2 + ' ';
return s;
}
testNestedForIn.expected = 'xx xy xz yx yy yz zx zy zz ';
test(testNestedForIn);
function testComparisons()
{
// All the special values from each of the types in