Merge tracemonkey to mozilla-central

This commit is contained in:
Robert Sayre 2009-04-05 21:24:42 -04:00
commit e0ea0bea06
26 changed files with 456 additions and 289 deletions

View File

@ -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);

View File

@ -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

View File

@ -922,6 +922,7 @@ struct JSContext {
char *lastMessage;
#ifdef DEBUG
void *tracefp;
JSOp tracePrevOp;
#endif
/* Per-context optional error reporter. */

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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.

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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; }

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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"].