mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-04 15:51:37 +00:00
Back out 480132 fix; static analysis red
This commit is contained in:
parent
bbfb18733b
commit
fe4cf97707
@ -1038,10 +1038,6 @@ class JSAutoTempValueRooter
|
||||
: 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);
|
||||
|
@ -663,9 +663,11 @@ js_FreeStack(JSContext *cx, void *mark)
|
||||
JSObject *
|
||||
js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *sharedBlock = fp->blockChain;
|
||||
JSObject *obj, *cursor, *clonedChild, *parent;
|
||||
JSTempValueRooter tvr;
|
||||
|
||||
if (!sharedBlock) {
|
||||
obj = fp->blockChain;
|
||||
if (!obj) {
|
||||
/*
|
||||
* 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.
|
||||
@ -677,131 +679,70 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
|
||||
return fp->scopeChain;
|
||||
}
|
||||
|
||||
/* We don't handle cloning blocks on trace, so abort recording.
|
||||
|
||||
It's true that fp->scopeChain could already have everything on
|
||||
fp->blockChain, in which case we may not actually clone
|
||||
anything on this call. But we should abort recording anyway,
|
||||
because whether or not fp->scopeChain is up to date can vary
|
||||
from one execution to the next. In code like:
|
||||
let (x = 0) {
|
||||
if (c) y = function () { return x };
|
||||
... loop that gets traced ...
|
||||
}
|
||||
even if c is true and no cloning is necessary when the loop is
|
||||
first recorded, c might be false and cloning might be necessary
|
||||
the next time the loop is entered. */
|
||||
#ifdef JS_TRACER
|
||||
if (TRACE_RECORDER(cx))
|
||||
js_AbortRecording(cx, "Can't create closure blocks");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
cursor = obj;
|
||||
clonedChild = NULL;
|
||||
for (;;) {
|
||||
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;
|
||||
parent = OBJ_GET_PARENT(cx, cursor);
|
||||
|
||||
/*
|
||||
* Avoid OBJ_SET_PARENT overhead as newChild cannot escape to
|
||||
* other threads.
|
||||
* 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.
|
||||
*/
|
||||
STOBJ_SET_PARENT(newChild, clone);
|
||||
newChild = clone;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
fp->flags |= JSFRAME_POP_BLOCKS;
|
||||
fp->scopeChain = obj;
|
||||
fp->blockChain = NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -6800,58 +6741,51 @@ js_Interpret(JSContext *cx)
|
||||
regs.sp++;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj));
|
||||
|
||||
/*
|
||||
* 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 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.
|
||||
*/
|
||||
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);
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
fp->blockChain = obj;
|
||||
END_CASE(JSOP_ENTERBLOCK)
|
||||
|
||||
BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
|
||||
BEGIN_CASE(JSOP_LEAVEBLOCK)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
|
||||
|
||||
JS_ASSERT(blockDepth <= StackDepth(script));
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx,
|
||||
fp->blockChain
|
||||
? fp->blockChain
|
||||
: fp->scopeChain);
|
||||
|
||||
JS_ASSERT(blockDepth <= StackDepth(script));
|
||||
#endif
|
||||
/*
|
||||
* 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 (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 (!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.
|
||||
|
@ -82,51 +82,13 @@ struct JSStackFrame {
|
||||
jsval rval; /* function return value */
|
||||
JSStackFrame *down; /* previous frame */
|
||||
void *annotation; /* used by Java security */
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
JSObject *scopeChain; /* scope chain */
|
||||
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
|
||||
@ -178,6 +140,7 @@ typedef struct JSInlineFrame {
|
||||
#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 */
|
||||
|
||||
|
@ -786,8 +786,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
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. */
|
||||
|
@ -1206,7 +1206,6 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
|
||||
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;
|
||||
@ -3209,6 +3208,7 @@ js_SynthesizeFrame(JSContext* cx, const FrameInfo& fi)
|
||||
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))
|
||||
@ -3948,6 +3948,7 @@ js_ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
|
||||
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
|
||||
@ -4133,6 +4134,8 @@ LeaveTree(InterpState& state, VMSideExit* lr)
|
||||
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
|
||||
@ -9130,6 +9133,9 @@ TraceRecorder::record_JSOP_TYPEOFEXPR()
|
||||
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;
|
||||
@ -9144,8 +9150,7 @@ TraceRecorder::record_JSOP_ENTERBLOCK()
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_LEAVEBLOCK()
|
||||
{
|
||||
/* We mustn't exit the lexical block we began recording in. */
|
||||
return cx->fp->blockChain != lexicalBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
|
@ -382,7 +382,6 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JSContext* cx;
|
||||
JSTraceMonitor* traceMonitor;
|
||||
JSObject* globalObj;
|
||||
JSObject* lexicalBlock;
|
||||
Tracker tracker;
|
||||
Tracker nativeFrameTracker;
|
||||
char* entryTypeMap;
|
||||
|
Loading…
x
Reference in New Issue
Block a user