Merge tracemonkey to mozilla-central.
Merge tracemonkey to mozilla-central.
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1033,16 +1033,20 @@ class JSAutoTempValueRooter
explicit JSAutoTempValueRooter(JSContext *cx, jsval v = JSVAL_NULL)
: mContext(cx) {
JS_PUSH_SINGLE_TEMP_ROOT(mContext, v, &mTvr);
}
JSAutoTempValueRooter(JSContext *cx, JSString *str)
: mContext(cx) {
JS_PUSH_TEMP_ROOT_STRING(mContext, str, &mTvr);
}
+ JSAutoTempValueRooter(JSContext *cx, JSObject *obj)
+ : mContext(cx) {
+ JS_PUSH_TEMP_ROOT_OBJECT(mContext, obj, &mTvr);
+ }
~JSAutoTempValueRooter() {
JS_POP_TEMP_ROOT(mContext, &mTvr);
}
jsval value() { return mTvr.u.value; }
jsval * addr() { return &mTvr.u.value; }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -658,96 +658,139 @@ js_FreeStack(JSContext *cx, void *mark)
/* Release the stackPool space allocated since mark was set. */
JS_ARENA_RELEASE(&cx->stackPool, mark);
}
JSObject *
js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
{
- JSObject *obj, *cursor, *clonedChild, *parent;
- JSTempValueRooter tvr;
-
- obj = fp->blockChain;
- if (!obj) {
+ JSObject *sharedBlock = fp->blockChain;
+
+ if (!sharedBlock) {
/*
* Don't force a call object for a lightweight function call, but do
* insist that there is a call object for a heavyweight function call.
*/
JS_ASSERT(!fp->fun ||
!(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
fp->callobj);
JS_ASSERT(fp->scopeChain);
return fp->scopeChain;
}
+ /* We don't handle cloning blocks on trace. */
+ js_LeaveTrace(cx);
+
/*
* We have one or more lexical scopes to reflect into fp->scopeChain, so
* make sure there's a call object at the current head of the scope chain,
* if this frame is a call frame.
+ *
+ * Also, identify the innermost compiler-allocated block we needn't clone.
*/
+ JSObject *limitBlock, *limitClone;
if (fp->fun && !fp->callobj) {
JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
if (!js_GetCallObject(cx, fp))
return NULL;
+
+ /* We know we must clone everything on blockChain. */
+ limitBlock = limitClone = NULL;
+ } else {
+ /*
+ * scopeChain includes all blocks whose static scope we're within that
+ * have already been cloned. Find the innermost such block. Its
+ * prototype should appear on blockChain; we'll clone blockChain up
+ * to, but not including, that prototype.
+ */
+ limitClone = fp->scopeChain;
+ while (OBJ_GET_CLASS(cx, limitClone) == &js_WithClass)
+ limitClone = OBJ_GET_PARENT(cx, limitClone);
+ JS_ASSERT(limitClone);
+
+ /*
+ * It may seem like we don't know enough about limitClone to be able
+ * to just grab its prototype as we do here, but it's actually okay.
+ *
+ * If limitClone is a block object belonging to this frame, then its
+ * prototype is the innermost entry in blockChain that we have already
+ * cloned, and is thus the place to stop when we clone below.
+ *
+ * Otherwise, there are no blocks for this frame on scopeChain, and we
+ * need to clone the whole blockChain. In this case, limitBlock can
+ * point to any object known not to be on blockChain, since we simply
+ * loop until we hit limitBlock or NULL. If limitClone is a block, it
+ * isn't a block from this function, since blocks can't be nested
+ * within themselves on scopeChain (recursion is dynamic nesting, not
+ * static nesting). If limitClone isn't a block, its prototype won't
+ * be a block either. So we can just grab limitClone's prototype here
+ * regardless of its type or which frame it belongs to.
+ */
+ limitBlock = OBJ_GET_PROTO(cx, limitClone);
+
+ /* If the innermost block has already been cloned, we are done. */
+ if (limitBlock == sharedBlock)
+ return fp->scopeChain;
}
/*
- * Clone the block chain. To avoid recursive cloning we set the parent of
- * the cloned child after we clone the parent. In the following loop when
- * clonedChild is null it indicates the first iteration when no special GC
- * rooting is necessary. On the second and the following iterations we
- * have to protect cloned so far chain against the GC during cloning of
- * the cursor object.
+ * Special-case cloning the innermost block; this doesn't have enough in
+ * common with subsequent steps to include in the loop.
+ *
+ * We pass fp->scopeChain and not null even if we override the parent slot
+ * later as null triggers useless calculations of slot's value in
+ * js_NewObject that js_CloneBlockObject calls.
*/
- cursor = obj;
- clonedChild = NULL;
+ JSObject *innermostNewChild
+ = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp);
+ if (!innermostNewChild)
+ return NULL;
+ JSAutoTempValueRooter tvr(cx, innermostNewChild);
+
+ /*
+ * Clone our way towards outer scopes until we reach the innermost
+ * enclosing function, or the innermost block we've already cloned.
+ */
+ JSObject *newChild = innermostNewChild;
for (;;) {
- parent = OBJ_GET_PARENT(cx, cursor);
+ JS_ASSERT(OBJ_GET_PROTO(cx, newChild) == sharedBlock);
+ sharedBlock = OBJ_GET_PARENT(cx, sharedBlock);
+
+ /* Sometimes limitBlock will be NULL, so check that first. */
+ if (sharedBlock == limitBlock || !sharedBlock)
+ break;
+
+ /* As in the call above, we don't know the real parent yet. */
+ JSObject *clone
+ = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp);
+ if (!clone)
+ return NULL;
/*
- * We pass fp->scopeChain and not null even if we override the parent
- * slot later as null triggers useless calculations of slot's value in
- * js_NewObject that js_CloneBlockObject calls.
+ * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to
+ * other threads.
*/
- cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
- if (!cursor) {
- if (clonedChild)
- JS_POP_TEMP_ROOT(cx, &tvr);
- return NULL;
- }
- if (!clonedChild) {
- /*
- * The first iteration. Check if other follow and root obj if so
- * to protect the whole cloned chain against GC.
- */
- obj = cursor;
- if (!parent)
- break;
- JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
- } else {
- /*
- * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
- * other threads.
- */
- STOBJ_SET_PARENT(clonedChild, cursor);
- if (!parent) {
- JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
- JS_POP_TEMP_ROOT(cx, &tvr);
- break;
- }
- }
- clonedChild = cursor;
- cursor = parent;
+ STOBJ_SET_PARENT(newChild, clone);
+ newChild = clone;
}
- fp->flags |= JSFRAME_POP_BLOCKS;
- fp->scopeChain = obj;
- fp->blockChain = NULL;
- return obj;
+
+ /*
+ * If we found a limit block belonging to this frame, then we should have
+ * found it in blockChain.
+ */
+ JS_ASSERT_IF(limitBlock &&
+ OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
+ OBJ_GET_PRIVATE(cx, limitClone) == fp,
+ sharedBlock);
+
+ /* Place our newly cloned blocks at the head of the scope chain. */
+ fp->scopeChain = innermostNewChild;
+ return fp->scopeChain;
}
JSBool
js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
{
jsval v;
JSObject *obj;
@@ -6697,61 +6740,68 @@ js_Interpret(JSContext *cx)
vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(regs.sp < vp);
JS_ASSERT(vp <= fp->slots + script->nslots);
while (regs.sp < vp) {
STORE_OPND(0, JSVAL_VOID);
regs.sp++;
}
+#ifdef DEBUG
+ JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj));
+
/*
- * If this frame had to reflect the compile-time block chain into
- * the runtime scope chain, we can't optimize block scopes out of
- * runtime any longer, because an outer block that parents obj has
- * been cloned onto the scope chain. To avoid re-cloning such a
- * parent and accumulating redundant clones via js_GetScopeChain,
- * we must clone each block eagerly on entry, and push it on the
- * scope chain, until this frame pops.
+ * The young end of fp->scopeChain may omit blocks if we
+ * haven't closed over them, but if there are any closure
+ * blocks on fp->scopeChain, they'd better be (clones of)
+ * ancestors of the block we're entering now; anything
+ * else we should have popped off fp->scopeChain when we
+ * left its static scope.
*/
- if (fp->flags & JSFRAME_POP_BLOCKS) {
- JS_ASSERT(!fp->blockChain);
- obj = js_CloneBlockObject(cx, obj, fp->scopeChain, fp);
- if (!obj)
- goto error;
- fp->scopeChain = obj;
- } else {
- JS_ASSERT(!fp->blockChain ||
- OBJ_GET_PARENT(cx, obj) == fp->blockChain);
- fp->blockChain = obj;
+ obj2 = fp->scopeChain;
+ while ((clasp = OBJ_GET_CLASS(cx, obj2)) == &js_WithClass)
+ obj2 = OBJ_GET_PARENT(cx, obj2);
+ if (clasp == &js_BlockClass &&
+ OBJ_GET_PRIVATE(cx, obj2) == fp) {
+ JSObject *youngestProto = OBJ_GET_PROTO(cx, obj2);
+ JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
+ parent = obj;
+ while ((parent = OBJ_GET_PARENT(cx, parent)) != youngestProto)
+ JS_ASSERT(parent);
}
+#endif
+
+ fp->blockChain = obj;
END_CASE(JSOP_ENTERBLOCK)
BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
BEGIN_CASE(JSOP_LEAVEBLOCK)
{
#ifdef DEBUG
- uintN blockDepth = OBJ_BLOCK_DEPTH(cx,
- fp->blockChain
- ? fp->blockChain
- : fp->scopeChain);
-
- JS_ASSERT(blockDepth <= StackDepth(script));
+ JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
+ uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
+
+ JS_ASSERT(blockDepth <= StackDepth(script));
#endif
- if (fp->blockChain) {
- JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
- fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);
- } else {
- /*
- * This block was cloned into fp->scopeChain, so clear its
- * private data and sync its locals to their property slots.
- */
+ /*
+ * If we're about to leave the dynamic scope of a block that has
+ * been cloned onto fp->scopeChain, clear its private data, move
+ * its locals from the stack into the clone, and pop it off the
+ * chain.
+ */
+ obj = fp->scopeChain;
+ if (OBJ_GET_PROTO(cx, obj) == fp->blockChain) {
+ JS_ASSERT (OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
if (!js_PutBlockObject(cx, JS_TRUE))
goto error;
}
+ /* Pop the block chain, too. */
+ fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);
+
/*
* We will move the result of the expression to the new topmost
* stack slot.
*/
if (op == JSOP_LEAVEBLOCKEXPR)
rval = FETCH_OPND(-1);
regs.sp -= GET_UINT16(regs.pc);
if (op == JSOP_LEAVEBLOCKEXPR) {
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -77,23 +77,61 @@ struct JSStackFrame {
JSScript *script; /* script being interpreted */
JSFunction *fun; /* function being called or null */
JSObject *thisp; /* "this" pointer if in method */
uintN argc; /* actual argument count */
jsval *argv; /* base of argument stack slots */
jsval rval; /* function return value */
JSStackFrame *down; /* previous frame */
void *annotation; /* used by Java security */
- JSObject *scopeChain; /* scope chain */
+
+ /*
+ * We can't determine in advance which local variables can live on
+ * the stack and be freed when their dynamic scope ends, and which
+ * will be closed over and need to live in the heap. So we place
+ * variables on the stack initially, note when they are closed
+ * over, and copy those that are out to the heap when we leave
+ * their dynamic scope.
+ *
+ * The bytecode compiler produces a tree of block objects
+ * accompanying each JSScript representing those lexical blocks in
+ * the script that have let-bound variables associated with them.
+ * These block objects are never modified, and never become part
+ * of any function's scope chain. Their parent slots point to the
+ * innermost block that encloses them, or are NULL in the
+ * outermost blocks within a function or in eval or global code.
+ *
+ * When we are in the static scope of such a block, blockChain
+ * points to its compiler-allocated block object; otherwise, it is
+ * NULL.
+ *
+ * scopeChain is the current scope chain, including 'call' and
+ * 'block' objects for those function calls and lexical blocks
+ * whose static scope we are currently executing in, and 'with'
+ * objects for with statements; the chain is typically terminated
+ * by a global object. However, as an optimization, the young end
+ * of the chain omits block objects we have not yet cloned. To
+ * create a closure, we clone the missing blocks from blockChain
+ * (which is always current), place them at the head of
+ * scopeChain, and use that for the closure's scope chain. If we
+ * never close over a lexical block, we never place a mutable
+ * clone of it on scopeChain.
+ *
+ * This lazy cloning is implemented in js_GetScopeChain, which is
+ * also used in some other cases --- entering 'with' blocks, for
+ * example.
+ */
+ JSObject *scopeChain;
+ JSObject *blockChain;
+
uintN sharpDepth; /* array/object initializer depth */
JSObject *sharpArray; /* scope for #n= initializer vars */
uint32 flags; /* frame flags -- see below */
JSStackFrame *dormantNext; /* next dormant frame chain */
JSObject *xmlNamespace; /* null or default xml namespace in E4X */
- JSObject *blockChain; /* active compile-time block scopes */
JSStackFrame *displaySave; /* previous value of display entry for
script->staticDepth */
#ifdef DEBUG
jsrefcount pcDisabledSave; /* for balanced property cache control */
#endif
};
#ifdef __cplusplus
@@ -135,17 +173,16 @@ typedef struct JSInlineFrame {
#define JSFRAME_COMPUTED_THIS 0x02 /* frame.thisp was computed already */
#define JSFRAME_ASSIGNING 0x04 /* a complex (not simplex JOF_ASSIGNING) op
is currently assigning to a property */
#define JSFRAME_DEBUGGER 0x08 /* frame for JS_EvaluateInStackFrame */
#define JSFRAME_EVAL 0x10 /* frame for obj_eval */
#define JSFRAME_ROOTED_ARGV 0x20 /* frame.argv is rooted by the caller */
#define JSFRAME_YIELDING 0x40 /* js_Interpret dispatched JSOP_YIELD */
#define JSFRAME_ITERATOR 0x80 /* trying to get an iterator for for-in */
-#define JSFRAME_POP_BLOCKS 0x100 /* scope chain contains blocks to pop */
#define JSFRAME_GENERATOR 0x200 /* frame belongs to generator-iterator */
#define JSFRAME_IMACRO_START 0x400 /* imacro starting -- see jstracer.h */
#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */
#define JSFRAME_OVERRIDE_BITS 8
#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -767,16 +767,18 @@ js_NewGenerator(JSContext *cx, JSStackFr
gen->frame.regs = &gen->savedRegs;
/* Copy remaining state (XXX sharp* and xml* should be local vars). */
gen->frame.sharpDepth = 0;
gen->frame.sharpArray = NULL;
gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
gen->frame.dormantNext = NULL;
gen->frame.xmlNamespace = NULL;
+ /* JSOP_GENERATOR appears in the prologue, outside all blocks. */
+ JS_ASSERT(!fp->blockChain);
gen->frame.blockChain = NULL;
/* Note that gen is newborn. */
gen->state = JSGEN_NEWBORN;
if (!JS_SetPrivate(cx, obj, gen)) {
JS_free(cx, gen);
goto bad;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1201,16 +1201,17 @@ TraceRecorder::TraceRecorder(JSContext*
TreeInfo* ti, unsigned stackSlots, unsigned ngslots, uint8* typeMap,
VMSideExit* innermostNestedGuard, jsbytecode* outer)
{
JS_ASSERT(!_fragment->vmprivate && ti);
this->cx = cx;
this->traceMonitor = &JS_TRACE_MONITOR(cx);
this->globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
+ this->lexicalBlock = cx->fp->blockChain;
this->anchor = _anchor;
this->fragment = _fragment;
this->lirbuf = _fragment->lirbuf;
this->treeInfo = ti;
this->callDepth = _anchor ? _anchor->calldepth : 0;
this->atoms = FrameAtomBase(cx, cx->fp);
this->deepAborted = false;
this->trashSelf = false;
@@ -3203,17 +3204,16 @@ js_SynthesizeFrame(JSContext* cx, const
newifp->frame.fun = fun;
bool constructing = (fi.s.argc & 0x8000) != 0;
newifp->frame.argc = argc;
newifp->callerRegs.pc = fi.pc;
newifp->callerRegs.sp = fp->slots + fi.s.spdist;
fp->imacpc = fi.imacpc;
- JS_ASSERT(!(fp->flags & JSFRAME_POP_BLOCKS));
#ifdef DEBUG
if (fi.block != fp->blockChain) {
for (JSObject* obj = fi.block; obj != fp->blockChain; obj = STOBJ_GET_PARENT(obj))
JS_ASSERT(obj);
}
#endif
fp->blockChain = fi.block;
@@ -3943,17 +3943,16 @@ js_ExecuteTree(JSContext* cx, Fragment*
void *reserve;
state.stackMark = JS_ARENA_MARK(&cx->stackPool);
JS_ARENA_ALLOCATE(reserve, &cx->stackPool, MAX_INTERP_STACK_BYTES);
if (!reserve)
return NULL;
#ifdef DEBUG
- state.jsframe_pop_blocks_set_on_entry = ((cx->fp->flags & JSFRAME_POP_BLOCKS) != 0);
memset(stack_buffer, 0xCD, sizeof(stack_buffer));
memset(state.global, 0xCD, (globalFrameSize+1)*sizeof(double));
#endif
debug_only(*(uint64*)&state.global[globalFrameSize] = 0xdeadbeefdeadbeefLL;)
debug_only_v(printf("entering trace at %s:%u@%u, native stack slots: %u code: %p\n",
cx->fp->script->filename,
js_FramePCToLineNumber(cx, cx->fp),
@@ -4129,18 +4128,16 @@ LeaveTree(InterpState& state, VMSideExit
}
/* Adjust sp and pc relative to the tree we exited from (not the tree we entered into).
These are our final values for sp and pc since js_SynthesizeFrame has already taken
care of all frames in between. But first we recover fp->blockChain, which comes from
the side exit struct. */
JSStackFrame* fp = cx->fp;
- JS_ASSERT_IF(fp->flags & JSFRAME_POP_BLOCKS,
- calldepth == 0 && state.jsframe_pop_blocks_set_on_entry);
fp->blockChain = innermost->block;
/* If we are not exiting from an inlined frame the state->sp is spbase, otherwise spbase
is whatever slots frames around us consume. */
fp->regs->pc = innermost->pc;
fp->imacpc = innermost->imacpc;
fp->regs->sp = StackBase(fp) + (innermost->sp_adj / sizeof(double)) - calldepth_slots;
JS_ASSERT_IF(!fp->imacpc,
@@ -9128,34 +9125,32 @@ JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_TYPEOFEXPR()
{
return record_JSOP_TYPEOF();
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_ENTERBLOCK()
{
- if (cx->fp->flags & JSFRAME_POP_BLOCKS)
- ABORT_TRACE("can't trace after js_GetScopeChain");
-
JSScript* script = cx->fp->script;
JSFrameRegs& regs = *cx->fp->regs;
JSObject* obj;
JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(0), obj);
LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++)
stack(i, void_ins);
return true;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_LEAVEBLOCK()
{
- return true;
+ /* We mustn't exit the lexical block we began recording in. */
+ return cx->fp->blockChain != lexicalBlock;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_GENERATOR()
{
return false;
#if 0
JSStackFrame* fp = cx->fp;
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -377,16 +377,17 @@ enum JSMonitorRecordingStatus {
JSMRS_STOP,
JSMRS_IMACRO
};
class TraceRecorder : public avmplus::GCObject {
JSContext* cx;
JSTraceMonitor* traceMonitor;
JSObject* globalObj;
+ JSObject* lexicalBlock;
Tracker tracker;
Tracker nativeFrameTracker;
char* entryTypeMap;
unsigned callDepth;
JSAtom** atoms;
VMSideExit* anchor;
nanojit::Fragment* fragment;
TreeInfo* treeInfo;