зеркало из https://github.com/mozilla/pjs.git
Merge tracemonkey to mozilla-central
This commit is contained in:
Коммит
79fa88a5b2
|
@ -746,10 +746,9 @@ js_GetDenseArrayElementValue(JSObject *obj, JSProperty *prop)
|
|||
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
|
||||
JS_ASSERT((void *) prop ==
|
||||
(void *) &(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));
|
||||
JS_ASSERT((jsval) prop->id == obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]);
|
||||
JS_ASSERT(JSVAL_IS_INT(prop->id));
|
||||
JS_ASSERT(JSVAL_IS_INT(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));
|
||||
|
||||
jsint i = JSID_TO_INT(prop->id);
|
||||
jsint i = JSVAL_TO_INT(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]);
|
||||
JS_ASSERT(i >= 0);
|
||||
jsval v = obj->dslots[i];
|
||||
JS_ASSERT(v != JSVAL_HOLE);
|
||||
|
|
|
@ -1557,7 +1557,7 @@ js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
|
|||
|
||||
#if defined DEBUG && defined XP_UNIX
|
||||
/* For gdb usage. */
|
||||
void js_traceon(JSContext *cx) { cx->tracefp = stderr; }
|
||||
void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevOp = JSOP_LIMIT; }
|
||||
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
|
||||
#endif
|
||||
|
||||
|
|
|
@ -922,6 +922,7 @@ struct JSContext {
|
|||
char *lastMessage;
|
||||
#ifdef DEBUG
|
||||
void *tracefp;
|
||||
JSOp tracePrevOp;
|
||||
#endif
|
||||
|
||||
/* Per-context optional error reporter. */
|
||||
|
|
|
@ -3449,7 +3449,10 @@ EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
/*
|
||||
* Duplicate the value being destructured to use as a reference base.
|
||||
* If dup is not the first one, annotate it for the decompiler.
|
||||
*/
|
||||
if (pn2 != pn->pn_head && js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
|
||||
return JS_FALSE;
|
||||
if (js_Emit1(cx, cg, JSOP_DUP) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -3571,7 +3574,6 @@ EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
|
|||
if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
JS_ASSERT_IF(pn->pn_type == TOK_DEFSHARP, pn->pn_kid);
|
||||
if (!js_EmitTree(cx, cg, pn))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
@ -3625,9 +3627,7 @@ MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
|
|||
lhs = pn->pn_left;
|
||||
rhs = pn->pn_right;
|
||||
if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB &&
|
||||
lhs->pn_count <= rhs->pn_count &&
|
||||
(rhs->pn_count == 0 ||
|
||||
rhs->pn_head->pn_type != TOK_DEFSHARP)) {
|
||||
lhs->pn_count <= rhs->pn_count) {
|
||||
if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs))
|
||||
return JS_FALSE;
|
||||
*pop = JSOP_NOP;
|
||||
|
@ -3916,6 +3916,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
JSOp op;
|
||||
JSTokenType type;
|
||||
uint32 argc;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
jsint sharpnum;
|
||||
#endif
|
||||
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
|
||||
|
@ -5075,6 +5078,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
#endif
|
||||
|
||||
case TOK_LC:
|
||||
{
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (pn->pn_arity == PN_UNARY) {
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid))
|
||||
|
@ -5096,6 +5100,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
}
|
||||
|
||||
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
|
||||
|
||||
JSParseNode *pchild = pn->pn_head;
|
||||
if (pn->pn_extra & PNX_FUNCDEFS) {
|
||||
/*
|
||||
* This block contains top-level function definitions. To ensure
|
||||
|
@ -5109,7 +5115,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
* mode for scripts does not allow separate emitter passes.
|
||||
*/
|
||||
JS_ASSERT(cg->treeContext.flags & TCF_IN_FUNCTION);
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
if (pn->pn_extra & PNX_DESTRARGS) {
|
||||
/*
|
||||
* Assign the destructuring arguments before defining any
|
||||
* functions, see bug 419662.
|
||||
*/
|
||||
JS_ASSERT(pchild->pn_type == TOK_SEMI);
|
||||
JS_ASSERT(pchild->pn_kid->pn_type == TOK_COMMA);
|
||||
if (!js_EmitTree(cx, cg, pchild))
|
||||
return JS_FALSE;
|
||||
pchild = pchild->pn_next;
|
||||
}
|
||||
|
||||
for (pn2 = pchild; pn2; pn2 = pn2->pn_next) {
|
||||
if (pn2->pn_type == TOK_FUNCTION) {
|
||||
if (pn2->pn_op == JSOP_NOP) {
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
|
@ -5126,7 +5144,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
}
|
||||
}
|
||||
}
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
for (pn2 = pchild; pn2; pn2 = pn2->pn_next) {
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
@ -5139,6 +5157,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
|
||||
ok = js_PopStatementCG(cx, cg);
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_SEQ:
|
||||
JS_ASSERT(pn->pn_arity == PN_LIST);
|
||||
|
@ -6036,27 +6055,29 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
* If no sharp variable is defined and the initialiser is not for an
|
||||
* array comprehension, use JSOP_NEWARRAY.
|
||||
*/
|
||||
pn2 = pn->pn_head;
|
||||
op = JSOP_NEWARRAY;
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (pn2 && pn2->pn_type == TOK_DEFSHARP)
|
||||
op = JSOP_NEWINIT;
|
||||
sharpnum = -1;
|
||||
do_emit_array:
|
||||
#endif
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
#if JS_HAS_GENERATORS || JS_HAS_SHARP_VARS
|
||||
op = JSOP_NEWARRAY;
|
||||
# if JS_HAS_GENERATORS
|
||||
if (pn->pn_type == TOK_ARRAYCOMP)
|
||||
op = JSOP_NEWINIT;
|
||||
#endif
|
||||
|
||||
if (op == JSOP_NEWINIT &&
|
||||
js_Emit2(cx, cg, op, (jsbytecode) JSProto_Array) < 0) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
# endif
|
||||
# if JS_HAS_SHARP_VARS
|
||||
JS_ASSERT_IF(sharpnum >= 0, cg->treeContext.flags & TCF_HAS_SHARPS);
|
||||
if (cg->treeContext.flags & TCF_HAS_SHARPS)
|
||||
op = JSOP_NEWINIT;
|
||||
# endif
|
||||
if (op == JSOP_NEWINIT) {
|
||||
if (js_Emit2(cx, cg, op, (jsbytecode) JSProto_Array) < 0)
|
||||
return JS_FALSE;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
|
||||
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
|
||||
pn2 = pn2->pn_next;
|
||||
if (sharpnum >= 0)
|
||||
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -6067,12 +6088,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
/*
|
||||
* Pass the new array's stack index to the TOK_ARRAYPUSH case by
|
||||
* storing it in pn->pn_extra, then simply traverse the TOK_FOR
|
||||
* node and its kids under pn2 to generate this comprehension.
|
||||
* node and its kids to generate this comprehension.
|
||||
*/
|
||||
JS_ASSERT(cg->stackDepth > 0);
|
||||
saveDepth = cg->arrayCompDepth;
|
||||
cg->arrayCompDepth = (uint32) (cg->stackDepth - 1);
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
if (!js_EmitTree(cx, cg, pn->pn_head))
|
||||
return JS_FALSE;
|
||||
cg->arrayCompDepth = saveDepth;
|
||||
|
||||
|
@ -6083,10 +6104,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
}
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
pn2 = pn->pn_head;
|
||||
for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (op == JSOP_NEWINIT && !EmitNumberOp(cx, atomIndex, cg))
|
||||
return JS_FALSE;
|
||||
|
||||
#endif
|
||||
if (pn2->pn_type == TOK_COMMA) {
|
||||
if (js_Emit1(cx, cg, JSOP_HOLE) < 0)
|
||||
return JS_FALSE;
|
||||
|
@ -6094,10 +6117,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (op == JSOP_NEWINIT && js_Emit1(cx, cg, JSOP_INITELEM) < 0)
|
||||
return JS_FALSE;
|
||||
#endif
|
||||
}
|
||||
JS_ASSERT(atomIndex == pn->pn_count);
|
||||
|
||||
if (pn->pn_extra & PNX_ENDCOMMA) {
|
||||
/* Emit a source note so we know to decompile an extra comma. */
|
||||
|
@ -6105,22 +6130,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (op == JSOP_NEWARRAY) {
|
||||
JS_ASSERT(atomIndex == pn->pn_count);
|
||||
off = js_EmitN(cx, cg, op, 3);
|
||||
if (off < 0)
|
||||
return JS_FALSE;
|
||||
pc = CG_CODE(cg, off);
|
||||
SET_UINT24(pc, atomIndex);
|
||||
UpdateDepth(cx, cg, off);
|
||||
} else {
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (op == JSOP_NEWINIT) {
|
||||
/* Emit an op for sharp array cleanup and decompilation. */
|
||||
if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
off = js_EmitN(cx, cg, JSOP_NEWARRAY, 3);
|
||||
if (off < 0)
|
||||
return JS_FALSE;
|
||||
pc = CG_CODE(cg, off);
|
||||
SET_UINT24(pc, atomIndex);
|
||||
UpdateDepth(cx, cg, off);
|
||||
break;
|
||||
|
||||
case TOK_RC:
|
||||
#if JS_HAS_SHARP_VARS
|
||||
sharpnum = -1;
|
||||
do_emit_object:
|
||||
#endif
|
||||
#if JS_HAS_DESTRUCTURING_SHORTHAND
|
||||
if (pn->pn_extra & PNX_SHORTHAND) {
|
||||
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR,
|
||||
|
@ -6139,29 +6169,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) JSProto_Object) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
pn2 = pn->pn_head;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
|
||||
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
|
||||
pn2 = pn2->pn_next;
|
||||
}
|
||||
if (sharpnum >= 0)
|
||||
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum);
|
||||
#endif
|
||||
|
||||
for (; pn2; pn2 = pn2->pn_next) {
|
||||
/* Emit an index for t[2], else map an atom for t.p or t['%q']. */
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
|
||||
pn3 = pn2->pn_left;
|
||||
if (pn3->pn_type == TOK_NUMBER) {
|
||||
#ifdef __GNUC__
|
||||
ale = NULL; /* quell GCC overwarning */
|
||||
#endif
|
||||
if (!EmitNumberOp(cx, pn3->pn_dval, cg))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
JS_ASSERT(pn3->pn_type == TOK_NAME ||
|
||||
pn3->pn_type == TOK_STRING);
|
||||
ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Emit code for the property initializer. */
|
||||
|
@ -6182,6 +6200,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
JS_ASSERT(pn3->pn_type == TOK_NAME ||
|
||||
pn3->pn_type == TOK_STRING);
|
||||
ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
EMIT_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
|
||||
}
|
||||
}
|
||||
|
@ -6193,12 +6216,25 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
case TOK_DEFSHARP:
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid))
|
||||
JS_ASSERT(cg->treeContext.flags & TCF_HAS_SHARPS);
|
||||
sharpnum = pn->pn_num;
|
||||
pn = pn->pn_kid;
|
||||
if (pn->pn_type == TOK_RB)
|
||||
goto do_emit_array;
|
||||
# if JS_HAS_GENERATORS
|
||||
if (pn->pn_type == TOK_ARRAYCOMP)
|
||||
goto do_emit_array;
|
||||
# endif
|
||||
if (pn->pn_type == TOK_RC)
|
||||
goto do_emit_object;
|
||||
|
||||
if (!js_EmitTree(cx, cg, pn))
|
||||
return JS_FALSE;
|
||||
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);
|
||||
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum);
|
||||
break;
|
||||
|
||||
case TOK_USESHARP:
|
||||
JS_ASSERT(cg->treeContext.flags & TCF_HAS_SHARPS);
|
||||
EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
|
||||
break;
|
||||
#endif /* JS_HAS_SHARP_VARS */
|
||||
|
|
|
@ -196,6 +196,8 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
#define TCF_COMPILE_N_GO 0x400 /* compiler-and-go mode of script, can
|
||||
optimize name references based on scope
|
||||
chain */
|
||||
#define TCF_HAS_SHARPS 0x800 /* source contains sharp defs or uses */
|
||||
|
||||
/*
|
||||
* Flags to propagate out of the blocks.
|
||||
*/
|
||||
|
@ -206,7 +208,8 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
*/
|
||||
#define TCF_FUN_FLAGS (TCF_FUN_IS_GENERATOR | \
|
||||
TCF_FUN_HEAVYWEIGHT | \
|
||||
TCF_FUN_USES_NONLOCALS)
|
||||
TCF_FUN_USES_NONLOCALS | \
|
||||
TCF_HAS_SHARPS)
|
||||
|
||||
/*
|
||||
* Flags field, not stored in JSTreeContext.flags, for passing staticDepth
|
||||
|
@ -591,7 +594,8 @@ typedef enum JSSrcNoteType {
|
|||
do-while loop */
|
||||
SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break;
|
||||
also used on JSOP_ENDINIT if extra comma
|
||||
at end of array literal: [1,2,,] */
|
||||
at end of array literal: [1,2,,];
|
||||
JSOP_DUP continuing destructuring pattern */
|
||||
SRC_DECL = 6, /* type of a declaration (var, const, let*) */
|
||||
SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment
|
||||
operation, with SRC_DECL_* offset operand */
|
||||
|
|
|
@ -2045,12 +2045,11 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
|
|||
#ifdef DEBUG
|
||||
|
||||
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
|
||||
js_TraceOpcode(JSContext *cx, jsint len)
|
||||
js_TraceOpcode(JSContext *cx)
|
||||
{
|
||||
FILE *tracefp;
|
||||
JSStackFrame *fp;
|
||||
JSFrameRegs *regs;
|
||||
JSOp prevop;
|
||||
intN ndefs, n, nuses;
|
||||
jsval *siter;
|
||||
JSString *str;
|
||||
|
@ -2060,10 +2059,22 @@ js_TraceOpcode(JSContext *cx, jsint len)
|
|||
JS_ASSERT(tracefp);
|
||||
fp = cx->fp;
|
||||
regs = fp->regs;
|
||||
if (len != 0) {
|
||||
prevop = (JSOp) regs->pc[-len];
|
||||
ndefs = js_CodeSpec[prevop].ndefs;
|
||||
if (ndefs != 0) {
|
||||
|
||||
/*
|
||||
* Operations in prologues don't produce interesting values, and
|
||||
* js_DecompileValueGenerator isn't set up to handle them anyway.
|
||||
*/
|
||||
if (cx->tracePrevOp != JSOP_LIMIT && regs->pc >= fp->script->main) {
|
||||
ndefs = js_GetStackDefs(cx, &js_CodeSpec[cx->tracePrevOp],
|
||||
cx->tracePrevOp, fp->script, regs->pc);
|
||||
|
||||
/*
|
||||
* If there aren't that many elements on the stack, then
|
||||
* we have probably entered a new frame, and printing output
|
||||
* would just be misleading.
|
||||
*/
|
||||
if (ndefs != 0 &&
|
||||
ndefs < regs->sp - fp->slots) {
|
||||
for (n = -ndefs; n < 0; n++) {
|
||||
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
|
||||
NULL);
|
||||
|
@ -2094,7 +2105,7 @@ js_TraceOpcode(JSContext *cx, jsint len)
|
|||
regs->pc - fp->script->code,
|
||||
JS_FALSE, tracefp);
|
||||
op = (JSOp) *regs->pc;
|
||||
nuses = js_CodeSpec[op].nuses;
|
||||
nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
|
||||
if (nuses != 0) {
|
||||
for (n = -nuses; n < 0; n++) {
|
||||
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
|
||||
|
@ -2108,6 +2119,10 @@ js_TraceOpcode(JSContext *cx, jsint len)
|
|||
}
|
||||
fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
|
||||
}
|
||||
cx->tracePrevOp = op;
|
||||
|
||||
/* It's nice to have complete traces when debugging a crash. */
|
||||
fflush(tracefp);
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
@ -2534,6 +2549,30 @@ js_Interpret(JSContext *cx)
|
|||
# define JS_EXTENSION_(s) s
|
||||
#endif
|
||||
|
||||
# ifdef DEBUG
|
||||
/*
|
||||
* We call this macro from BEGIN_CASE in threaded interpreters,
|
||||
* and before entering the switch in non-threaded interpreters.
|
||||
* However, reaching such points doesn't mean we've actually
|
||||
* fetched an OP from the instruction stream: some opcodes use
|
||||
* 'op=x; DO_OP()' to let another opcode's implementation finish
|
||||
* their work, and many opcodes share entry points with a run of
|
||||
* consecutive BEGIN_CASEs.
|
||||
*
|
||||
* Take care to trace OP only when it is the opcode fetched from
|
||||
* the instruction stream, so the trace matches what one would
|
||||
* expect from looking at the code. (We do omit POPs after SETs;
|
||||
* unfortunate, but not worth fixing.)
|
||||
*/
|
||||
# define TRACE_OPCODE(OP) JS_BEGIN_MACRO \
|
||||
if (JS_UNLIKELY(cx->tracefp != NULL) && \
|
||||
(OP) == *regs.pc) \
|
||||
js_TraceOpcode(cx); \
|
||||
JS_END_MACRO
|
||||
# else
|
||||
# define TRACE_OPCODE(OP) ((void) 0)
|
||||
# endif
|
||||
|
||||
#if JS_THREADED_INTERP
|
||||
static void *const normalJumpTable[] = {
|
||||
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
|
||||
|
@ -2572,15 +2611,6 @@ js_Interpret(JSContext *cx)
|
|||
DO_OP(); \
|
||||
JS_END_MACRO
|
||||
|
||||
# ifdef DEBUG
|
||||
# define TRACE_OPCODE(OP) JS_BEGIN_MACRO \
|
||||
if (cx->tracefp) \
|
||||
js_TraceOpcode(cx, len); \
|
||||
JS_END_MACRO
|
||||
# else
|
||||
# define TRACE_OPCODE(OP) (void)0
|
||||
# endif
|
||||
|
||||
# define BEGIN_CASE(OP) L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER();
|
||||
# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
|
||||
# define END_VARLEN_CASE DO_NEXT_OP(len);
|
||||
|
@ -2846,13 +2876,10 @@ js_Interpret(JSContext *cx)
|
|||
advance_pc:
|
||||
regs.pc += len;
|
||||
op = (JSOp) *regs.pc;
|
||||
# ifdef DEBUG
|
||||
if (cx->tracefp)
|
||||
js_TraceOpcode(cx, len);
|
||||
# endif
|
||||
|
||||
do_op:
|
||||
CHECK_RECORDER();
|
||||
TRACE_OPCODE(op);
|
||||
switchOp = intN(op) | switchMask;
|
||||
do_switch:
|
||||
switch (switchOp) {
|
||||
|
@ -5908,6 +5935,10 @@ js_Interpret(JSContext *cx)
|
|||
|
||||
obj = FUN_OBJECT(fun);
|
||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||
#ifdef JS_TRACER
|
||||
if (TRACE_RECORDER(cx))
|
||||
js_AbortRecording(cx, "DEFLOCALFUN for closure");
|
||||
#endif
|
||||
obj = js_CloneFunctionObject(cx, fun, parent);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
@ -6908,6 +6939,11 @@ js_Interpret(JSContext *cx)
|
|||
L_JSOP_ARRAYPUSH:
|
||||
# endif
|
||||
|
||||
# if !JS_HAS_SHARP_VARS
|
||||
L_JSOP_DEFSHARP:
|
||||
L_JSOP_USESHARP:
|
||||
# endif
|
||||
|
||||
# if !JS_HAS_DESTRUCTURING
|
||||
L_JSOP_ENUMCONSTELEM:
|
||||
# endif
|
||||
|
|
|
@ -611,7 +611,7 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2);
|
|||
* previous opcode.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK void
|
||||
js_TraceOpcode(JSContext *cx, jsint len);
|
||||
js_TraceOpcode(JSContext *cx);
|
||||
|
||||
/*
|
||||
* JS_OPMETER helper functions.
|
||||
|
|
|
@ -82,20 +82,6 @@ struct JSObjectOps {
|
|||
JSSetRequiredSlotOp setRequiredSlot;
|
||||
};
|
||||
|
||||
/*
|
||||
* Classes that expose JSObjectOps via a non-null getObjectOps class hook may
|
||||
* derive a property structure from this struct, return a pointer to it from
|
||||
* lookupProperty and defineProperty, and use the pointer to avoid rehashing
|
||||
* in getAttributes and setAttributes.
|
||||
*
|
||||
* The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an
|
||||
* internal pointer that is opaque to users of this API, but which users may
|
||||
* convert from and to a jsval using JS_ValueToId and JS_IdToValue.
|
||||
*/
|
||||
struct JSProperty {
|
||||
jsid id;
|
||||
};
|
||||
|
||||
struct JSObjectMap {
|
||||
jsrefcount nrefs; /* count of all referencing objects */
|
||||
JSObjectOps *ops; /* high level object operation vtable */
|
||||
|
|
|
@ -1624,13 +1624,20 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
|
|||
break;
|
||||
|
||||
/*
|
||||
* Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another
|
||||
* destructuring initialiser abuts this one, and we should stop. This
|
||||
* happens with source of the form '[a] = [b] = c'.
|
||||
* We should stop if JSOP_DUP is either without notes or its note is
|
||||
* not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
|
||||
* last destructuring reference implementing an op= assignment like in
|
||||
* '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
|
||||
* means another destructuring initialiser abuts this one like in
|
||||
* '[a] = [b] = c'.
|
||||
*/
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_DESTRUCT)
|
||||
if (!sn)
|
||||
break;
|
||||
if (SN_TYPE(sn) != SRC_CONTINUE) {
|
||||
LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
|
||||
return NULL;
|
||||
|
|
|
@ -605,7 +605,7 @@ js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
|
|||
}
|
||||
|
||||
/*
|
||||
* Global variables and regexps shares the index space with locals. Due to
|
||||
* Global variables and regexps share the index space with locals. Due to
|
||||
* incremental code generation we need to patch the bytecode to adjust the
|
||||
* local references to skip the globals.
|
||||
*/
|
||||
|
@ -1388,6 +1388,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (body->pn_tail == &body->pn_head)
|
||||
body->pn_tail = &item->pn_next;
|
||||
++body->pn_count;
|
||||
body->pn_extra |= PNX_DESTRARGS;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1925,8 +1926,6 @@ FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
|
|||
step = 0;
|
||||
ASSERT_VALID_PROPERTY_KEY(pnid);
|
||||
pnhead = pn->pn_head;
|
||||
if (pnhead && pnhead->pn_type == TOK_DEFSHARP)
|
||||
pnhead = pnhead->pn_next;
|
||||
if (pnid->pn_type == TOK_NUMBER) {
|
||||
for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
|
||||
JS_ASSERT(pnprop->pn_type == TOK_COLON);
|
||||
|
@ -2011,11 +2010,6 @@ CheckDestructuring(JSContext *cx, BindData *data,
|
|||
|
||||
fpvd.table.ops = NULL;
|
||||
lhs = left->pn_head;
|
||||
if (lhs && lhs->pn_type == TOK_DEFSHARP) {
|
||||
pn = lhs;
|
||||
goto no_var_name;
|
||||
}
|
||||
|
||||
if (left->pn_type == TOK_RB) {
|
||||
rhs = (right && right->pn_type == left->pn_type)
|
||||
? right->pn_head
|
||||
|
@ -5373,24 +5367,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
{
|
||||
JSParseNode *pn, *pn2, *pn3;
|
||||
JSOp op;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
JSParseNode *defsharp;
|
||||
JSBool notsharp;
|
||||
#endif
|
||||
|
||||
JS_CHECK_RECURSION(cx, return NULL);
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
defsharp = NULL;
|
||||
notsharp = JS_FALSE;
|
||||
again:
|
||||
/*
|
||||
* Control flows here after #n= is scanned. If the following primary is
|
||||
* not valid after such a "sharp variable" definition, the tt switch case
|
||||
* should set notsharp.
|
||||
*/
|
||||
#endif
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
if (tt == TOK_NAME) {
|
||||
tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
|
||||
|
@ -5432,13 +5411,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
pn->pn_type = TOK_RB;
|
||||
pn->pn_op = JSOP_NEWINIT;
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (defsharp) {
|
||||
PN_INIT_LIST_1(pn, defsharp);
|
||||
defsharp = NULL;
|
||||
} else
|
||||
#endif
|
||||
PN_INIT_LIST(pn);
|
||||
PN_INIT_LIST(pn);
|
||||
|
||||
ts->flags |= TSF_OPERAND;
|
||||
matched = js_MatchToken(cx, ts, TOK_RB);
|
||||
|
@ -5480,8 +5453,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
#if JS_HAS_GENERATORS
|
||||
/*
|
||||
* At this point, (index == 0 && pn->pn_count != 0) implies one
|
||||
* element initialiser was parsed (possibly with a defsharp before
|
||||
* the left bracket).
|
||||
* element initialiser was parsed.
|
||||
*
|
||||
* An array comprehension of the form:
|
||||
*
|
||||
|
@ -5565,14 +5537,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
return NULL;
|
||||
pn->pn_type = TOK_RC;
|
||||
pn->pn_op = JSOP_NEWINIT;
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (defsharp) {
|
||||
PN_INIT_LIST_1(pn, defsharp);
|
||||
defsharp = NULL;
|
||||
} else
|
||||
#endif
|
||||
PN_INIT_LIST(pn);
|
||||
PN_INIT_LIST(pn);
|
||||
|
||||
afterComma = JS_FALSE;
|
||||
for (;;) {
|
||||
|
@ -5713,16 +5678,28 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
case TOK_DEFSHARP:
|
||||
if (defsharp)
|
||||
goto badsharp;
|
||||
defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!defsharp)
|
||||
pn = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
|
||||
pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
goto again;
|
||||
if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
tt == TOK_STAR || tt == TOK_AT ||
|
||||
tt == TOK_XMLSTAGO /* XXXbe could be sharp? */ ||
|
||||
#endif
|
||||
tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_PRIMARY) {
|
||||
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
|
||||
JSMSG_BAD_SHARP_VAR_DEF);
|
||||
return NULL;
|
||||
}
|
||||
pn->pn_kid = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
|
||||
if (!pn->pn_kid)
|
||||
return NULL;
|
||||
tc->flags |= TCF_HAS_SHARPS;
|
||||
break;
|
||||
|
||||
case TOK_USESHARP:
|
||||
/* Check for forward/dangling references at runtime, to allow eval. */
|
||||
|
@ -5730,7 +5707,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (!pn)
|
||||
return NULL;
|
||||
pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
|
||||
notsharp = JS_TRUE;
|
||||
tc->flags |= TCF_HAS_SHARPS;
|
||||
break;
|
||||
#endif /* JS_HAS_SHARP_VARS */
|
||||
|
||||
|
@ -5746,23 +5723,12 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
return NULL;
|
||||
if (genexp)
|
||||
return pn2;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
|
||||
|
||||
/* Check if parentheses were unnecessary. */
|
||||
if (pn2->pn_type == TOK_RP ||
|
||||
(js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec &&
|
||||
!afterDot)) {
|
||||
/*
|
||||
* Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly
|
||||
* to help the decompiler look ahead from a JSOP_ENDINIT to see a
|
||||
* JSOP_GROUP followed by a POP or POPV. That sequence means the
|
||||
* parentheses are mandatory, to disambiguate object initialisers
|
||||
* as expression statements from block statements.
|
||||
*
|
||||
* Also drop pn if pn2 is a member or a primary expression of any
|
||||
* kind. This is required to avoid generating a JSOP_GROUP that
|
||||
* will null the |obj| interpreter register, causing |this| in any
|
||||
* call of that member expression to bind to the global object.
|
||||
*/
|
||||
RecycleTree(pn, tc);
|
||||
pn = pn2;
|
||||
} else {
|
||||
|
@ -5778,27 +5744,23 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
pn = QualifiedIdentifier(cx, ts, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
notsharp = JS_TRUE;
|
||||
break;
|
||||
|
||||
case TOK_AT:
|
||||
pn = AttributeIdentifier(cx, ts, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
notsharp = JS_TRUE;
|
||||
break;
|
||||
|
||||
case TOK_XMLSTAGO:
|
||||
pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
notsharp = JS_TRUE; /* XXXbe could be sharp? */
|
||||
break;
|
||||
#endif /* JS_HAS_XML_SUPPORT */
|
||||
|
||||
case TOK_STRING:
|
||||
#if JS_HAS_SHARP_VARS
|
||||
notsharp = JS_TRUE;
|
||||
/* FALL THROUGH */
|
||||
#endif
|
||||
|
||||
|
@ -5896,9 +5858,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
return NULL;
|
||||
pn->pn_op = JSOP_DOUBLE;
|
||||
pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
notsharp = JS_TRUE;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TOK_PRIMARY:
|
||||
|
@ -5906,9 +5865,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
if (!pn)
|
||||
return NULL;
|
||||
pn->pn_op = CURRENT_TOKEN(ts).t_op;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
notsharp = JS_TRUE;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TOK_ERROR:
|
||||
|
@ -5920,19 +5876,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
JSMSG_SYNTAX_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (defsharp) {
|
||||
if (notsharp) {
|
||||
badsharp:
|
||||
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
|
||||
JSMSG_BAD_SHARP_VAR_DEF);
|
||||
return NULL;
|
||||
}
|
||||
defsharp->pn_kid = pn;
|
||||
return defsharp;
|
||||
}
|
||||
#endif
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,18 +164,15 @@ JS_BEGIN_EXTERN_C
|
|||
* call is a MEMBER expr naming a callable object
|
||||
* TOK_RB list pn_head: list of pn_count array element exprs
|
||||
* [,,] holes are represented by TOK_COMMA nodes
|
||||
* #n=[...] produces TOK_DEFSHARP at head of list
|
||||
* pn_extra: PN_ENDCOMMA if extra comma at end
|
||||
* TOK_RC list pn_head: list of pn_count TOK_COLON nodes where
|
||||
* each has pn_left: property id, pn_right: value
|
||||
* #n={...} produces TOK_DEFSHARP at head of list
|
||||
* var {x} = object destructuring shorthand shares
|
||||
* PN_NAME node for x on left and right of TOK_COLON
|
||||
* node in TOK_RC's list, has PNX_SHORTHAND flag
|
||||
* TOK_DEFSHARP unary pn_num: jsint value of n in #n=
|
||||
* pn_kid: null for #n=[...] and #n={...}, primary
|
||||
* if #n=primary for function, paren, name, object
|
||||
* literal expressions
|
||||
* pn_kid: primary function, paren, name, object or
|
||||
* array literal expressions
|
||||
* TOK_USESHARP nullary pn_num: jsint value of n in #n#
|
||||
* TOK_RP unary pn_kid: parenthesized expression
|
||||
* TOK_NAME, name pn_atom: name, string, or object atom
|
||||
|
@ -363,6 +360,8 @@ struct JSParseNode {
|
|||
statements */
|
||||
#define PNX_SHORTHAND 0x200 /* shorthand syntax used, at present
|
||||
object destructuring ({x,y}) only */
|
||||
#define PNX_DESTRARGS 0x400 /* the first child is node defining
|
||||
destructuring arguments */
|
||||
|
||||
/*
|
||||
* Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off
|
||||
|
|
|
@ -98,6 +98,7 @@ typedef struct JSParseContext JSParseContext;
|
|||
typedef struct JSParsedObjectBox JSParsedObjectBox;
|
||||
typedef struct JSParseNode JSParseNode;
|
||||
typedef struct JSPropCacheEntry JSPropCacheEntry;
|
||||
typedef struct JSProperty JSProperty;
|
||||
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
||||
typedef struct JSTempValueRooter JSTempValueRooter;
|
||||
typedef struct JSThread JSThread;
|
||||
|
|
|
@ -139,7 +139,6 @@ typedef struct JSFunction JSFunction;
|
|||
typedef struct JSFunctionSpec JSFunctionSpec;
|
||||
typedef struct JSTracer JSTracer;
|
||||
typedef struct JSIdArray JSIdArray;
|
||||
typedef struct JSProperty JSProperty;
|
||||
typedef struct JSPropertyDescriptor JSPropertyDescriptor;
|
||||
typedef struct JSPropertySpec JSPropertySpec;
|
||||
typedef struct JSObject JSObject;
|
||||
|
|
|
@ -1220,7 +1220,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
|
|||
TreeInfo* ti, unsigned stackSlots, unsigned ngslots, uint8* typeMap,
|
||||
VMSideExit* innermostNestedGuard, jsbytecode* outer)
|
||||
{
|
||||
JS_ASSERT(!_fragment->vmprivate && ti);
|
||||
JS_ASSERT(!_fragment->vmprivate && ti && cx->fp->regs->pc == (jsbytecode*)_fragment->ip);
|
||||
|
||||
/* Reset the fragment state we care about in case we got a recycled fragment. */
|
||||
_fragment->lastIns = NULL;
|
||||
|
@ -1241,6 +1241,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
|
|||
this->loop = true; /* default assumption is we are compiling a loop */
|
||||
this->wasRootFragment = _fragment == _fragment->root;
|
||||
this->outer = outer;
|
||||
this->pendingTraceableNative = NULL;
|
||||
this->generatedTraceableNative = new JSTraceableNative();
|
||||
JS_ASSERT(generatedTraceableNative);
|
||||
|
||||
|
@ -1347,6 +1348,12 @@ void TraceRecorder::removeFragmentoReferences()
|
|||
fragment = NULL;
|
||||
}
|
||||
|
||||
void TraceRecorder::deepAbort()
|
||||
{
|
||||
debug_only_v(printf("deep abort");)
|
||||
deepAborted = true;
|
||||
}
|
||||
|
||||
/* Add debug information to a LIR instruction as we emit it. */
|
||||
inline LIns*
|
||||
TraceRecorder::addName(LIns* ins, const char* name)
|
||||
|
@ -1748,7 +1755,7 @@ skip:
|
|||
|
||||
/* Emit load instructions onto the trace that read the initial stack state. */
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
||||
TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8 t,
|
||||
const char *prefix, uintN index, JSStackFrame *fp)
|
||||
{
|
||||
LIns* ins;
|
||||
|
@ -1761,7 +1768,7 @@ TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
|||
ins = lir->insLoadi(base, offset);
|
||||
ins = lir->ins1(LIR_i2f, ins);
|
||||
} else {
|
||||
JS_ASSERT(t == JSVAL_BOXED || isNumber(*p) == (t == JSVAL_DOUBLE));
|
||||
JS_ASSERT_IF(t != JSVAL_BOXED, isNumber(*p) == (t == JSVAL_DOUBLE));
|
||||
if (t == JSVAL_DOUBLE) {
|
||||
ins = lir->insLoad(LIR_ldq, base, offset);
|
||||
} else if (t == JSVAL_BOOLEAN) {
|
||||
|
@ -1772,6 +1779,7 @@ TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
|||
}
|
||||
checkForGlobalObjectReallocation();
|
||||
tracker.set(p, ins);
|
||||
|
||||
#ifdef DEBUG
|
||||
char name[64];
|
||||
JS_ASSERT(strlen(prefix) < 10);
|
||||
|
@ -1828,25 +1836,49 @@ TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigne
|
|||
uint8* globalTypeMap = typeMap + stackSlots;
|
||||
unsigned length = treeInfo->nGlobalTypes();
|
||||
|
||||
/* This is potentially the typemap of the side exit and thus shorter than the tree's
|
||||
global type map. */
|
||||
if (ngslots < length)
|
||||
/*
|
||||
* This is potentially the typemap of the side exit and thus shorter than the tree's
|
||||
* global type map.
|
||||
*/
|
||||
if (ngslots < length) {
|
||||
mergeTypeMaps(&globalTypeMap/*out param*/, &ngslots/*out param*/,
|
||||
treeInfo->globalTypeMap(), length,
|
||||
(uint8*)alloca(sizeof(uint8) * length));
|
||||
}
|
||||
JS_ASSERT(ngslots == treeInfo->nGlobalTypes());
|
||||
|
||||
/* the first time we compile a tree this will be empty as we add entries lazily */
|
||||
/*
|
||||
* Check whether there are any values on the stack we have to unbox and do that first
|
||||
* before we waste any time fetching the state from the stack.
|
||||
*/
|
||||
ptrdiff_t offset = -treeInfo->nativeStackBase;
|
||||
uint8* m = typeMap;
|
||||
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
||||
if (*m == JSVAL_BOXED) {
|
||||
import(sp, offset, vp, JSVAL_BOXED, "boxed", vpnum, cx->fp);
|
||||
LIns* vp_ins = get(vp);
|
||||
unbox_jsval(*vp, vp_ins, copy(anchor));
|
||||
set(vp, vp_ins);
|
||||
}
|
||||
m++; offset += sizeof(double);
|
||||
);
|
||||
|
||||
/*
|
||||
* The first time we compile a tree this will be empty as we add entries lazily.
|
||||
*/
|
||||
uint16* gslots = treeInfo->globalSlots->data();
|
||||
uint8* m = globalTypeMap;
|
||||
m = globalTypeMap;
|
||||
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
||||
JS_ASSERT(*m != JSVAL_BOXED);
|
||||
import(lirbuf->state, nativeGlobalOffset(vp), vp, *m, vpname, vpnum, NULL);
|
||||
m++;
|
||||
);
|
||||
ptrdiff_t offset = -treeInfo->nativeStackBase;
|
||||
offset = -treeInfo->nativeStackBase;
|
||||
m = typeMap;
|
||||
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
||||
import(sp, offset, vp, *m, vpname, vpnum, fp);
|
||||
if (*m != JSVAL_BOXED) {
|
||||
import(sp, offset, vp, *m, vpname, vpnum, fp);
|
||||
}
|
||||
m++; offset += sizeof(double);
|
||||
);
|
||||
}
|
||||
|
@ -2000,7 +2032,7 @@ js_IsLoopEdge(jsbytecode* pc, jsbytecode* header)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Promote slots if necessary to match the called tree' type map and report error if thats
|
||||
/* Promote slots if necessary to match the called tree's type map and report error if thats
|
||||
impossible. */
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::adjustCallerTypes(Fragment* f)
|
||||
|
@ -2074,7 +2106,11 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
/* Check for a return-value opcode that needs to restart at the next instruction. */
|
||||
const JSCodeSpec& cs = js_CodeSpec[*pc];
|
||||
|
||||
/* WARNING: don't return before restoring the original pc if (resumeAfter). */
|
||||
/*
|
||||
* When calling a _FAIL native, make the snapshot's pc point to the next
|
||||
* instruction after the CALL or APPLY. Even on failure, a _FAIL native must not
|
||||
* be called again from the interpreter.
|
||||
*/
|
||||
bool resumeAfter = (pendingTraceableNative &&
|
||||
JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS);
|
||||
if (resumeAfter) {
|
||||
|
@ -2105,13 +2141,15 @@ 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
|
||||
the top of the stack is a boxed value. */
|
||||
if (resumeAfter) {
|
||||
if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER)
|
||||
typemap[stackSlots - 1] = JSVAL_BOXED;
|
||||
/*
|
||||
* If we are currently executing a traceable native or we are attaching a second trace
|
||||
* to it, the value on top of the stack is boxed. Make a note of this in the typemap.
|
||||
*/
|
||||
if (pendingTraceableNative && (pendingTraceableNative->flags & JSTN_UNBOX_AFTER))
|
||||
typemap[stackSlots - 1] = JSVAL_BOXED;
|
||||
|
||||
/* Now restore the the original pc (after which early returns are ok). */
|
||||
/* Now restore the the original pc (after which early returns are ok). */
|
||||
if (resumeAfter) {
|
||||
MUST_FLOW_LABEL(restore_pc);
|
||||
regs->pc = pc - cs.length;
|
||||
} else {
|
||||
|
@ -2126,8 +2164,10 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
|
||||
JS_STATIC_ASSERT (sizeof(GuardRecord) + sizeof(VMSideExit) < MAX_SKIP_BYTES);
|
||||
|
||||
/* Check if we already have a matching side exit. If so use that side exit structure,
|
||||
otherwise we have to create our own. */
|
||||
/*
|
||||
* Check if we already have a matching side exit. If so use that side exit structure
|
||||
* by cloning it, otherwise we have to create our own.
|
||||
*/
|
||||
VMSideExit** exits = treeInfo->sideExits.data();
|
||||
unsigned nexits = treeInfo->sideExits.length();
|
||||
if (exitType == LOOP_EXIT) {
|
||||
|
@ -2135,15 +2175,8 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
VMSideExit* e = exits[n];
|
||||
if (e->pc == pc && e->imacpc == fp->imacpc &&
|
||||
!memcmp(getFullTypeMap(exits[n]), typemap, typemap_size)) {
|
||||
LIns* data = lir->skip(sizeof(GuardRecord));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
/* setup guard record structure with shared side exit */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
VMSideExit* exit = exits[n];
|
||||
rec->exit = exit;
|
||||
exit->addGuard(rec);
|
||||
AUDIT(mergedLoopExits);
|
||||
return data;
|
||||
return clone(exits[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2151,7 +2184,7 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
if (sizeof(GuardRecord) +
|
||||
sizeof(VMSideExit) +
|
||||
(stackSlots + ngslots) * sizeof(uint8) >= MAX_SKIP_BYTES) {
|
||||
/**
|
||||
/*
|
||||
* ::snapshot() is infallible in the sense that callers don't
|
||||
* expect errors; but this is a trace-aborting error condition. So
|
||||
* mangle the request to consume zero slots, and mark the tree as
|
||||
|
@ -2170,10 +2203,12 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
(stackSlots + ngslots) * sizeof(uint8));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
VMSideExit* exit = (VMSideExit*)(rec + 1);
|
||||
/* setup guard record structure */
|
||||
|
||||
/* Setup guard record structure. */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
rec->exit = exit;
|
||||
/* setup side exit structure */
|
||||
|
||||
/* Setup side exit structure. */
|
||||
memset(exit, 0, sizeof(VMSideExit));
|
||||
exit->from = fragment;
|
||||
exit->calldepth = callDepth;
|
||||
|
@ -2200,24 +2235,77 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
return data;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK LIns*
|
||||
TraceRecorder::clone(VMSideExit* exit)
|
||||
{
|
||||
LIns* data = lir->skip(sizeof(GuardRecord));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
/* setup guard record structure with shared side exit */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
rec->exit = exit;
|
||||
exit->addGuard(rec);
|
||||
return data;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK LIns*
|
||||
TraceRecorder::copy(VMSideExit* copy)
|
||||
{
|
||||
unsigned typemap_size = copy->numGlobalSlots + copy->numStackSlots;
|
||||
LIns* data = lir->skip(sizeof(GuardRecord) +
|
||||
sizeof(VMSideExit) +
|
||||
typemap_size * sizeof(uint8));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
VMSideExit* exit = (VMSideExit*)(rec + 1);
|
||||
|
||||
/* Setup guard record structure. */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
rec->exit = exit;
|
||||
|
||||
/* Copy side exit structure. */
|
||||
memcpy(exit, copy, sizeof(VMSideExit) + typemap_size * sizeof(uint8));
|
||||
exit->guards = rec;
|
||||
exit->from = fragment;
|
||||
exit->target = NULL;
|
||||
|
||||
/* BIG FAT WARNING: If compilation fails, we currently don't reset the lirbuf so its safe
|
||||
to keep references to the side exits here. If we ever start rewinding those lirbufs,
|
||||
we have to make sure we purge the side exits that then no longer will be in valid
|
||||
memory. */
|
||||
if (exit->exitType == LOOP_EXIT)
|
||||
treeInfo->sideExits.add(exit);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected)
|
||||
and using the supplied side exit if the conditon doesn't hold. */
|
||||
LIns*
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::guard(bool expected, LIns* cond, LIns* exit)
|
||||
{
|
||||
if (!cond->isCond()) {
|
||||
expected = !expected;
|
||||
cond = lir->ins_eq0(cond);
|
||||
}
|
||||
return lir->insGuard(expected ? LIR_xf : LIR_xt, cond, exit);
|
||||
#ifdef DEBUG
|
||||
LIns* guard =
|
||||
#endif
|
||||
lir->insGuard(expected ? LIR_xf : LIR_xt, cond, exit);
|
||||
#ifdef DEBUG
|
||||
if (guard) {
|
||||
GuardRecord* lr = guard->record();
|
||||
VMSideExit* e = (VMSideExit*)lr->exit;
|
||||
debug_only_v(printf(" lr=%p exitType=%d\n", (SideExit*)e, e->exitType);)
|
||||
} else {
|
||||
debug_only_v(printf(" redundant guard, eliminated\n");)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected)
|
||||
and generate a side exit with type exitType to jump to if the condition does not hold. */
|
||||
JS_REQUIRES_STACK LIns*
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType)
|
||||
{
|
||||
return guard(expected, cond, snapshot(exitType));
|
||||
guard(expected, cond, snapshot(exitType));
|
||||
}
|
||||
|
||||
/* Try to match the type of a slot to type t. checkType is used to verify that the type of
|
||||
|
@ -2800,19 +2888,27 @@ JS_REQUIRES_STACK void
|
|||
TraceRecorder::emitTreeCall(Fragment* inner, VMSideExit* exit)
|
||||
{
|
||||
TreeInfo* ti = (TreeInfo*)inner->vmprivate;
|
||||
|
||||
/* Invoke the inner tree. */
|
||||
LIns* args[] = { INS_CONSTPTR(inner), lirbuf->state }; /* reverse order */
|
||||
LIns* ret = lir->insCall(&js_CallTree_ci, args);
|
||||
|
||||
/* Read back all registers, in case the called tree changed any of them. */
|
||||
JS_ASSERT(!memchr(getGlobalTypeMap(exit), JSVAL_BOXED, exit->numGlobalSlots) &&
|
||||
!memchr(getStackTypeMap(exit), JSVAL_BOXED, exit->numStackSlots));
|
||||
import(ti, inner_sp_ins, exit->numStackSlots, exit->numGlobalSlots,
|
||||
exit->calldepth, getFullTypeMap(exit));
|
||||
|
||||
/* Restore sp and rp to their original values (we still have them in a register). */
|
||||
if (callDepth > 0) {
|
||||
lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp));
|
||||
lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp));
|
||||
}
|
||||
/* Guard that we come out of the inner tree along the same side exit we came out when
|
||||
we called the inner tree at recording time. */
|
||||
|
||||
/*
|
||||
* Guard that we come out of the inner tree along the same side exit we came out when
|
||||
* we called the inner tree at recording time.
|
||||
*/
|
||||
guard(true, lir->ins2(LIR_eq, ret, INS_CONSTPTR(exit)), NESTED_EXIT);
|
||||
/* Register us as a dependent tree of the inner tree. */
|
||||
((TreeInfo*)inner->vmprivate)->dependentTrees.addUnique(fragment->root);
|
||||
|
@ -4408,12 +4504,19 @@ js_AbortRecording(JSContext* cx, const char* reason)
|
|||
|
||||
/* Abort the trace and blacklist its starting point. */
|
||||
Fragment* f = tm->recorder->getFragment();
|
||||
if (!f) {
|
||||
|
||||
/*
|
||||
* If the recorder already had its fragment disposed, or we actually finished
|
||||
* recording and this recorder merely is passing through the deep abort state
|
||||
* to the next recorder on the stack, just destroy the recorder. There is
|
||||
* nothing to abort.
|
||||
*/
|
||||
if (!f || f->lastIns) {
|
||||
js_DeleteRecorder(cx);
|
||||
return;
|
||||
}
|
||||
JS_ASSERT(!f->vmprivate);
|
||||
|
||||
JS_ASSERT(!f->vmprivate);
|
||||
#ifdef DEBUG
|
||||
TreeInfo* ti = tm->recorder->getTreeInfo();
|
||||
debug_only_a(printf("Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
|
||||
|
@ -5830,6 +5933,11 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
|
|||
ABORT_TRACE("failed to lookup property");
|
||||
|
||||
if (prop) {
|
||||
if (!OBJ_IS_NATIVE(obj2)) {
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
ABORT_TRACE("property found on non-native object");
|
||||
}
|
||||
|
||||
js_FillPropertyCache(cx, aobj, OBJ_SHAPE(aobj), 0, protoIndex, obj2,
|
||||
(JSScopeProperty*) prop, &entry);
|
||||
}
|
||||
|
@ -6044,7 +6152,7 @@ TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
|
|||
}
|
||||
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
||||
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit)
|
||||
{
|
||||
if (isNumber(v)) {
|
||||
// JSVAL_IS_NUMBER(v)
|
||||
|
@ -6055,7 +6163,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
|||
lir->ins2(LIR_piand, v_ins,
|
||||
INS_CONST(JSVAL_TAGMASK)),
|
||||
JSVAL_DOUBLE))),
|
||||
MISMATCH_EXIT);
|
||||
exit);
|
||||
LIns* args[] = { v_ins };
|
||||
v_ins = lir->insCall(&js_UnboxDouble_ci, args);
|
||||
return;
|
||||
|
@ -6066,16 +6174,15 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
|||
lir->ins2i(LIR_eq,
|
||||
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
||||
JSVAL_BOOLEAN),
|
||||
MISMATCH_EXIT);
|
||||
exit);
|
||||
v_ins = lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS);
|
||||
return;
|
||||
case JSVAL_OBJECT:
|
||||
if (JSVAL_IS_NULL(v)) {
|
||||
// JSVAL_NULL maps to type JSVAL_TNULL, so insist that v_ins == 0 here.
|
||||
guard(true, lir->ins_eq0(v_ins), MISMATCH_EXIT);
|
||||
guard(true, lir->ins_eq0(v_ins), exit);
|
||||
} else {
|
||||
// We must guard that v_ins has JSVAL_OBJECT tag but is not JSVAL_NULL.
|
||||
LIns* exit = snapshot(MISMATCH_EXIT);
|
||||
guard(true,
|
||||
lir->ins2i(LIR_eq,
|
||||
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
||||
|
@ -6090,7 +6197,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
|||
lir->ins2i(LIR_eq,
|
||||
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
||||
JSVAL_STRING),
|
||||
MISMATCH_EXIT);
|
||||
exit);
|
||||
v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK));
|
||||
return;
|
||||
}
|
||||
|
@ -6138,10 +6245,11 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
|
|||
|
||||
bool cond = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity);
|
||||
if (cond) {
|
||||
LIns* exit = snapshot(exitType);
|
||||
/* Guard array length */
|
||||
LIns* exit = guard(true,
|
||||
lir->ins2(LIR_ult, idx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
|
||||
exitType)->oprnd2();
|
||||
guard(true,
|
||||
lir->ins2(LIR_ult, idx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
|
||||
exit);
|
||||
/* dslots must not be NULL */
|
||||
guard(false,
|
||||
lir->ins_eq0(dslots_ins),
|
||||
|
@ -7849,13 +7957,12 @@ TraceRecorder::record_FastNativeCallComplete()
|
|||
because that would cause the interpreter to re-execute the native
|
||||
function, which might have side effects.
|
||||
|
||||
Instead, snapshot(), which is invoked from unbox_jsval() below, will see
|
||||
that we are currently parked on a traceable native's JSOP_CALL
|
||||
instruction, and it will advance the pc to restore by the length of the
|
||||
current opcode. If the native's return type is jsval, snapshot() will
|
||||
also indicate in the type map that the element on top of the stack is a
|
||||
boxed value which doesn't need to be boxed if the type guard generated
|
||||
by unbox_jsval() fails. */
|
||||
Instead, the snapshot() call below sees that we are currently parked on
|
||||
a traceable native's JSOP_CALL instruction, and it will advance the pc
|
||||
to restore by the length of the current opcode. If the native's return
|
||||
type is jsval, snapshot() will also indicate in the type map that the
|
||||
element on top of the stack is a boxed value which doesn't need to be
|
||||
boxed if the type guard generated by unbox_jsval() fails. */
|
||||
|
||||
if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS) {
|
||||
#ifdef DEBUG
|
||||
|
@ -7909,7 +8016,13 @@ TraceRecorder::record_FastNativeCallComplete()
|
|||
|
||||
bool ok = true;
|
||||
if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER) {
|
||||
unbox_jsval(v, v_ins);
|
||||
/*
|
||||
* If we side exit on the unboxing code due to a type change, make sure that the boxed
|
||||
* value is actually currently associated with that location, and that we are talking
|
||||
* about the top of the stack here, which is where we expected boxed values.
|
||||
*/
|
||||
JS_ASSERT(&v == &cx->fp->regs->sp[-1] && get(&v) == v_ins);
|
||||
unbox_jsval(v, v_ins, snapshot(BRANCH_EXIT));
|
||||
set(&v, v_ins);
|
||||
} else if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_NEG) {
|
||||
/* Already added i2f in functionCall. */
|
||||
|
@ -7996,8 +8109,6 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
|||
}
|
||||
|
||||
v_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
|
||||
JS_ASSERT(cs.ndefs == 1);
|
||||
stack(-cs.nuses, v_ins);
|
||||
slot = SPROP_INVALID_SLOT;
|
||||
return true;
|
||||
}
|
||||
|
@ -8024,10 +8135,13 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
|||
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
|
||||
v_ins = lir->insCall(&js_CallGetter_ci, args);
|
||||
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
|
||||
/*
|
||||
* BIG FAT WARNING: This snapshot cannot be a BRANCH_EXIT, since
|
||||
* the value to the top of the stack is not the value we unbox.
|
||||
*/
|
||||
unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
|
||||
v_ins);
|
||||
JS_ASSERT(cs.ndefs == 1);
|
||||
stack(-cs.nuses, v_ins);
|
||||
v_ins,
|
||||
snapshot(MISMATCH_EXIT));
|
||||
return true;
|
||||
}
|
||||
ABORT_TRACE("non-stub getter");
|
||||
|
@ -8057,7 +8171,8 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
|||
}
|
||||
|
||||
v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
|
||||
unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins);
|
||||
unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins, snapshot(BRANCH_EXIT));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8120,7 +8235,7 @@ TraceRecorder::elem(jsval& oval, jsval& idx, jsval*& vp, LIns*& v_ins, LIns*& ad
|
|||
|
||||
/* Load the value and guard on its type to unbox it. */
|
||||
v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
|
||||
unbox_jsval(*vp, v_ins);
|
||||
unbox_jsval(*vp, v_ins, snapshot(BRANCH_EXIT));
|
||||
|
||||
if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
|
||||
// Optimize to guard for a hole only after untagging, so we know that
|
||||
|
|
|
@ -415,7 +415,7 @@ class TraceRecorder : public avmplus::GCObject {
|
|||
bool isGlobal(jsval* p) const;
|
||||
ptrdiff_t nativeGlobalOffset(jsval* p) const;
|
||||
JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(jsval* p) const;
|
||||
JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
||||
JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8 t,
|
||||
const char *prefix, uintN index, JSStackFrame *fp);
|
||||
JS_REQUIRES_STACK void import(TreeInfo* treeInfo, nanojit::LIns* sp, unsigned stackSlots,
|
||||
unsigned callDepth, unsigned ngslots, uint8* typeMap);
|
||||
|
@ -424,9 +424,8 @@ class TraceRecorder : public avmplus::GCObject {
|
|||
JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop);
|
||||
JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* guard(bool expected, nanojit::LIns* cond,
|
||||
ExitType exitType);
|
||||
nanojit::LIns* guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
|
||||
JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, ExitType exitType);
|
||||
JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
|
||||
|
||||
nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
|
||||
|
||||
|
@ -520,7 +519,7 @@ class TraceRecorder : public avmplus::GCObject {
|
|||
JS_REQUIRES_STACK bool getThis(nanojit::LIns*& this_ins);
|
||||
|
||||
JS_REQUIRES_STACK void box_jsval(jsval v, nanojit::LIns*& v_ins);
|
||||
JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins);
|
||||
JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins, nanojit::LIns* exit);
|
||||
JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
|
||||
nanojit::LIns* exit);
|
||||
JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
|
@ -561,6 +560,8 @@ public:
|
|||
|
||||
JS_REQUIRES_STACK uint8 determineSlotType(jsval* vp);
|
||||
JS_REQUIRES_STACK nanojit::LIns* snapshot(ExitType exitType);
|
||||
nanojit::LIns* clone(VMSideExit* exit);
|
||||
nanojit::LIns* copy(VMSideExit* exit);
|
||||
nanojit::Fragment* getFragment() const { return fragment; }
|
||||
TreeInfo* getTreeInfo() const { return treeInfo; }
|
||||
JS_REQUIRES_STACK void compile(JSTraceMonitor* tm);
|
||||
|
@ -578,6 +579,7 @@ public:
|
|||
void pushAbortStack();
|
||||
void popAbortStack();
|
||||
void removeFragmentoReferences();
|
||||
void deepAbort();
|
||||
|
||||
JS_REQUIRES_STACK bool record_EnterFrame();
|
||||
JS_REQUIRES_STACK bool record_LeaveFrame();
|
||||
|
@ -586,7 +588,6 @@ public:
|
|||
JS_REQUIRES_STACK bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
|
||||
JS_REQUIRES_STACK bool record_FastNativeCallComplete();
|
||||
|
||||
void deepAbort() { deepAborted = true; }
|
||||
bool wasDeepAborted() { return deepAborted; }
|
||||
TreeInfo* getTreeInfo() { return treeInfo; }
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
|||
* before deserialization of bytecode. If the saved version does not match
|
||||
* the current version, abort deserialization and invalidate the file.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 40)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 42)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
|
|
@ -4913,7 +4913,7 @@ xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
|
|||
|
||||
/*
|
||||
* js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
|
||||
* be native. Therefore, xml_lookupProperty must return a valid JSProperty
|
||||
* be native. Thus xml_lookupProperty must return a valid JSScopeProperty
|
||||
* pointer parameter via *propp to signify "property found". Since the only
|
||||
* call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
|
||||
* js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case
|
||||
|
|
|
@ -48,11 +48,17 @@ function testmath(funcname, args, expected) {
|
|||
}
|
||||
testfunc.name = funcname + "(" + args + ")";
|
||||
testfunc.expected = expected;
|
||||
testfunc.jitstats = {
|
||||
recorderStarted: 1,
|
||||
recorderAborted: 0,
|
||||
traceTriggered: 1
|
||||
};
|
||||
|
||||
// Disable jitstats check. This never worked right. The actual part of the
|
||||
// loop we cared about was never traced. We traced the filler parts early
|
||||
// and then took a mismatch side exit on every subequent array read with
|
||||
// a different type (gal, discovered when fixing bug 479110).
|
||||
// testfunc.jitstats = {
|
||||
// recorderStarted: 1,
|
||||
// recorderAborted: 0,
|
||||
// traceTriggered: 1
|
||||
// };
|
||||
|
||||
test(testfunc);
|
||||
}
|
||||
|
||||
|
|
|
@ -1859,13 +1859,8 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
static JSBool
|
||||
Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JSBool bval;
|
||||
JSString *str;
|
||||
FILE *file;
|
||||
|
||||
#if JS_THREADED_INTERP
|
||||
JS_ReportError(cx, "tracing not supported in JS_THREADED_INTERP builds");
|
||||
return JS_FALSE;
|
||||
#else
|
||||
if (argc == 0) {
|
||||
*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
|
||||
return JS_TRUE;
|
||||
|
@ -1873,24 +1868,39 @@ Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
|
||||
switch (JS_TypeOfValue(cx, argv[0])) {
|
||||
case JSTYPE_NUMBER:
|
||||
bval = JSVAL_IS_INT(argv[0])
|
||||
? JSVAL_TO_INT(argv[0])
|
||||
: (jsint) *JSVAL_TO_DOUBLE(argv[0]);
|
||||
case JSTYPE_BOOLEAN: {
|
||||
JSBool bval;
|
||||
if (!JS_ValueToBoolean(cx, argv[0], &bval))
|
||||
goto bad_argument;
|
||||
file = bval ? stderr : NULL;
|
||||
break;
|
||||
case JSTYPE_BOOLEAN:
|
||||
bval = JSVAL_TO_BOOLEAN(argv[0]);
|
||||
break;
|
||||
default:
|
||||
str = JS_ValueToString(cx, argv[0]);
|
||||
if (!str)
|
||||
}
|
||||
case JSTYPE_STRING: {
|
||||
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
||||
file = fopen(name, "w");
|
||||
if (!file) {
|
||||
JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
|
||||
name, strerror(errno));
|
||||
return JS_FALSE;
|
||||
JS_ReportError(cx, "tracing: illegal argument %s",
|
||||
JS_GetStringBytes(str));
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto bad_argument;
|
||||
}
|
||||
cx->tracefp = bval ? stderr : NULL;
|
||||
if (cx->tracefp && cx->tracefp != stderr)
|
||||
fclose((FILE *)cx->tracefp);
|
||||
cx->tracefp = file;
|
||||
cx->tracePrevOp = JSOP_LIMIT;
|
||||
return JS_TRUE;
|
||||
#endif
|
||||
|
||||
bad_argument:
|
||||
JSString *str = JS_ValueToString(cx, argv[0]);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
JS_ReportError(cx, "tracing: illegal argument %s",
|
||||
JS_GetStringBytes(str));
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -3676,7 +3686,8 @@ static const char *const shell_help_messages[] = {
|
|||
"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
|
||||
" Interface to JS_DumpHeap with output sent to file",
|
||||
"notes([fun]) Show source notes for functions",
|
||||
"tracing([toggle]) Turn tracing on or off",
|
||||
"tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
|
||||
" With filename, send to file.\n",
|
||||
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
||||
#endif
|
||||
#ifdef TEST_CVTARGS
|
||||
|
|
|
@ -4790,6 +4790,28 @@ testNewWithNonNativeProto.jitstats = {
|
|||
};
|
||||
test(testNewWithNonNativeProto);
|
||||
|
||||
function testLengthOnNonNativeProto()
|
||||
{
|
||||
var o = {};
|
||||
o.__proto__ = [3];
|
||||
for (var j = 0; j < 5; j++)
|
||||
o[0];
|
||||
|
||||
var o2 = {};
|
||||
o2.__proto__ = [];
|
||||
for (var j = 0; j < 5; j++)
|
||||
o2.length;
|
||||
|
||||
function foo() { }
|
||||
foo.__proto__ = [];
|
||||
for (var j = 0; j < 5; j++)
|
||||
foo.length;
|
||||
|
||||
return "no assertion";
|
||||
}
|
||||
testLengthOnNonNativeProto.expected = "no assertion";
|
||||
test(testLengthOnNonNativeProto);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
|
|
|
@ -60,7 +60,7 @@ gTestfile = '15.5.4.5-1.js';
|
|||
(2), where the first character in Result(1) is
|
||||
considered to be at position 0.
|
||||
|
||||
Note that the charCodeAt funciton is intentionally
|
||||
Note that the charCodeAt function is intentionally
|
||||
generic; it does not require that its this value be a
|
||||
String object. Therefore it can be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
|
|
@ -60,7 +60,7 @@ gTestfile = '15.5.4.5-2.js';
|
|||
(2), where the first character in Result(1) is
|
||||
considered to be at position 0.
|
||||
|
||||
Note that the charCodeAt funciton is intentionally
|
||||
Note that the charCodeAt function is intentionally
|
||||
generic; it does not require that its this value be a
|
||||
String object. Therefore it can be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
|
|
@ -60,7 +60,7 @@ gTestfile = '15.5.4.5-3.js';
|
|||
(2), where the first character in Result(1) is
|
||||
considered to be at position 0.
|
||||
|
||||
Note that the charCodeAt funciton is intentionally
|
||||
Note that the charCodeAt function is intentionally
|
||||
generic; it does not require that its this value be a
|
||||
String object. Therefore it can be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
|
|
@ -60,7 +60,7 @@ gTestfile = '15.5.4.5-5.js';
|
|||
(2), where the first character in Result(1) is
|
||||
considered to be at position 0.
|
||||
|
||||
Note that the charCodeAt funciton is intentionally
|
||||
Note that the charCodeAt function is intentionally
|
||||
generic; it does not require that its this value be a
|
||||
String object. Therefore it can be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
|
|
@ -60,7 +60,7 @@ gTestfile = '15.5.4.5-6.js';
|
|||
(2), where the first character in Result(1) is
|
||||
considered to be at position 0.
|
||||
|
||||
Note that the charCodeAt funciton is intentionally
|
||||
Note that the charCodeAt function is intentionally
|
||||
generic; it does not require that its this value be a
|
||||
String object. Therefore it can be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
function run_test() {
|
||||
return;
|
||||
var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
|
||||
getService(Ci.nsIURLFormatter);
|
||||
var locale = Cc["@mozilla.org/chrome/chrome-registry;1"].
|
||||
|
|
Загрузка…
Ссылка в новой задаче