mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 468782 - TM: js_FastValueToIterator and js_FastCallIteratorNext can reenter (relanding with a bug fix). 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:
parent
08c7886403
commit
6142999fde
@ -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)
|
||||
|
@ -98,22 +98,42 @@ function formatoffset(n, w) {
|
||||
|
||||
function immediate(op) {
|
||||
let info = op.info;
|
||||
let imm1Expr = /^\(/.test(op.imm1);
|
||||
if (info.flags.indexOf("JOF_ATOM") >= 0) {
|
||||
if (/^(?:void|object|function|string|number|boolean)$/.test(op.imm1))
|
||||
return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_" + op.imm1.toUpperCase() + ")";
|
||||
return "0, COMMON_ATOM_INDEX(" + op.imm1 + ")";
|
||||
}
|
||||
if (info.flags.indexOf("JOF_JUMP") >= 0)
|
||||
if (info.flags.indexOf("JOF_JUMP") >= 0) {
|
||||
ASSERT(!imm1Expr);
|
||||
return ((op.target >> 8) & 0xff) + ", " + (op.target & 0xff);
|
||||
}
|
||||
if (info.flags.indexOf("JOF_UINT8") >= 0 ||
|
||||
info.flags.indexOf("JOF_INT8") >= 0) {
|
||||
if (imm1Expr)
|
||||
return op.imm1;
|
||||
if (isNaN(Number(op.imm1)) || Number(op.imm1) != parseInt(op.imm1))
|
||||
throw new Error("invalid 8-bit operand: " + op.imm1);
|
||||
return (op.imm1 & 0xff);
|
||||
}
|
||||
if (info.flags.indexOf("JOF_UINT16") >= 0)
|
||||
if (info.flags.indexOf("JOF_UINT16") >= 0) {
|
||||
if (imm1Expr)
|
||||
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 +141,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 +164,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]);
|
||||
|
||||
|
@ -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, (JSITER_ENUMERATE),
|
||||
/* 5*/ JSOP_CALL, 0, 1,
|
||||
/* 8*/ JSOP_PUSH,
|
||||
/* 9*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator),
|
||||
/* 3*/ JSOP_INT8, (JSITER_ENUMERATE|JSITER_FOREACH),
|
||||
/* 5*/ JSOP_CALL, 0, 1,
|
||||
/* 8*/ JSOP_PUSH,
|
||||
/* 9*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff),
|
||||
/* 3*/ JSOP_INT8, (JSITER_ENUMERATE),
|
||||
/* 5*/ JSOP_CALL, 0, 1,
|
||||
/* 8*/ JSOP_PUSH,
|
||||
/* 9*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff),
|
||||
/* 3*/ JSOP_INT8, (JSITER_ENUMERATE|JSITER_FOREACH),
|
||||
/* 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 */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -254,6 +254,12 @@ typedef enum JSRuntimeState {
|
||||
JSRTS_LANDING
|
||||
} JSRuntimeState;
|
||||
|
||||
typedef enum JSBuiltinFunctionId {
|
||||
JSBUILTIN_ObjectToIterator,
|
||||
JSBUILTIN_CallIteratorNext,
|
||||
JSBUILTIN_LIMIT
|
||||
} JSBuiltinFunctionId;
|
||||
|
||||
typedef struct JSPropertyTreeEntry {
|
||||
JSDHashEntryHdr hdr;
|
||||
JSScopeProperty *child;
|
||||
@ -363,6 +369,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;
|
||||
|
||||
|
@ -3131,6 +3131,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) {
|
||||
|
@ -3215,7 +3215,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)
|
||||
@ -6724,6 +6723,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);
|
||||
@ -6833,10 +6845,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];
|
||||
@ -6855,7 +6869,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;
|
||||
|
@ -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.
|
||||
|
@ -2106,7 +2106,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");
|
||||
@ -2133,11 +2133,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);
|
||||
@ -7685,114 +7684,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
|
||||
@ -8821,6 +8746,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()
|
||||
{
|
||||
@ -8979,7 +8995,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;
|
||||
}
|
||||
|
||||
@ -8990,7 +9006,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;
|
||||
@ -9016,4 +9032,3 @@ UNUSED(207)
|
||||
UNUSED(208)
|
||||
UNUSED(209)
|
||||
UNUSED(219)
|
||||
UNUSED(226)
|
||||
|
@ -520,7 +520,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; }
|
||||
@ -577,6 +576,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)
|
||||
|
@ -2545,6 +2545,42 @@ 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 testForEach() {
|
||||
var r;
|
||||
var a = ["zero", "one", "two", "three"];
|
||||
for (var i = 0; i < RUNLOOP; i++) {
|
||||
r = "";
|
||||
for each (var s in a)
|
||||
r += s + " ";
|
||||
}
|
||||
return r;
|
||||
}
|
||||
testForEach.expected = "zero one two three ";
|
||||
test(testForEach);
|
||||
|
||||
function testThinForEach() {
|
||||
var a = ["red"];
|
||||
var n = 0;
|
||||
for (var i = 0; i < 10; i++)
|
||||
for each (var v in a)
|
||||
if (v)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
testThinForEach.expected = 10;
|
||||
test(testThinForEach);
|
||||
|
||||
function testComparisons()
|
||||
{
|
||||
// All the special values from each of the types in
|
||||
|
Loading…
Reference in New Issue
Block a user