Bug 783315 - Don't touch script->bindings.bindingArray if the script isn't fully compiled (r=billm)

--HG--
extra : rebase_source : fb4e163eaf9f3f7d84ad632d5f940b9d343c8ffc
This commit is contained in:
Luke Wagner 2012-08-16 15:38:22 -07:00
parent 8dab9caf9c
commit b41894b285
5 changed files with 65 additions and 34 deletions

View File

@ -102,7 +102,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
// Global/eval script bindings are always empty (all names are added to the // Global/eval script bindings are always empty (all names are added to the
// scope dynamically via JSOP_DEFFUN/VAR). // scope dynamically via JSOP_DEFFUN/VAR).
if (!script->bindings.init(cx, 0, 0, NULL)) if (!script->bindings.initWithTemporaryStorage(cx, 0, 0, NULL))
return NULL; return NULL;
// We can specialize a bit for the given scope chain if that scope chain is the global object. // We can specialize a bit for the given scope chain if that scope chain is the global object.

View File

@ -223,7 +223,7 @@ TreeContext::generateFunctionBindings(JSContext *cx, Bindings *bindings) const
AppendPackedBindings(this, args_, packedBindings); AppendPackedBindings(this, args_, packedBindings);
AppendPackedBindings(this, vars_, packedBindings + args_.length()); AppendPackedBindings(this, vars_, packedBindings + args_.length());
if (!bindings->init(cx, args_.length(), vars_.length(), packedBindings)) if (!bindings->initWithTemporaryStorage(cx, args_.length(), vars_.length(), packedBindings))
return false; return false;
if (bindings->hasAnyAliasedBindings() || sc->funHasExtensibleScope()) if (bindings->hasAnyAliasedBindings() || sc->funHasExtensibleScope())

View File

@ -62,10 +62,10 @@ Bindings::argumentsVarIndex(JSContext *cx) const
} }
bool bool
Bindings::init(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray) Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray)
{ {
JS_ASSERT(!callObjShape_); JS_ASSERT(!callObjShape_);
JS_ASSERT(!bindingArray_); JS_ASSERT(bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
if (numArgs > UINT16_MAX || numVars > UINT16_MAX) { if (numArgs > UINT16_MAX || numVars > UINT16_MAX) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
@ -75,11 +75,11 @@ Bindings::init(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindi
return false; return false;
} }
bindingArray_ = bindingArray; JS_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
numArgs_ = numArgs; numArgs_ = numArgs;
numVars_ = numVars; numVars_ = numVars;
/* /*
* Get the initial shape to use when creating CallObjects for this script. * Get the initial shape to use when creating CallObjects for this script.
* Since unaliased variables are, by definition, only accessed by local * Since unaliased variables are, by definition, only accessed by local
@ -135,10 +135,13 @@ Bindings::init(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindi
} }
uint8_t * uint8_t *
Bindings::switchStorageTo(Binding *newBindingArray) Bindings::switchToScriptStorage(Binding *newBindingArray)
{ {
PodCopy(newBindingArray, bindingArray_, count()); JS_ASSERT(bindingArrayUsingTemporaryStorage());
bindingArray_ = newBindingArray; JS_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT));
PodCopy(newBindingArray, bindingArray(), count());
bindingArrayAndFlag_ = uintptr_t(newBindingArray);
return reinterpret_cast<uint8_t *>(newBindingArray + count()); return reinterpret_cast<uint8_t *>(newBindingArray + count());
} }
@ -147,7 +150,7 @@ Bindings::clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript)
{ {
/* The clone has the same bindingArray_ offset as 'src'. */ /* The clone has the same bindingArray_ offset as 'src'. */
Bindings &src = srcScript->bindings; Bindings &src = srcScript->bindings;
ptrdiff_t off = (uint8_t *)src.bindingArray_ - srcScript->data; ptrdiff_t off = (uint8_t *)src.bindingArray() - srcScript->data;
JS_ASSERT(off >= 0); JS_ASSERT(off >= 0);
JS_ASSERT(off <= (srcScript->code - srcScript->data)); JS_ASSERT(off <= (srcScript->code - srcScript->data));
Binding *dstPackedBindings = (Binding *)(dstScriptData + off); Binding *dstPackedBindings = (Binding *)(dstScriptData + off);
@ -156,8 +159,10 @@ Bindings::clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript)
* Since atoms are shareable throughout the runtime, we can simply copy * Since atoms are shareable throughout the runtime, we can simply copy
* the source's bindingArray directly. * the source's bindingArray directly.
*/ */
PodCopy(dstPackedBindings, src.bindingArray_, src.count()); if (!initWithTemporaryStorage(cx, src.numArgs(), src.numVars(), src.bindingArray()))
return init(cx, src.numArgs(), src.numVars(), dstPackedBindings); return false;
switchToScriptStorage(dstPackedBindings);
return true;
} }
template<XDRMode mode> template<XDRMode mode>
@ -207,7 +212,7 @@ XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, unsigned numArgs, un
bindingArray[i] = Binding(name, kind, aliased); bindingArray[i] = Binding(name, kind, aliased);
} }
if (!script->bindings.init(cx, numArgs, numVars, bindingArray)) if (!script->bindings.initWithTemporaryStorage(cx, numArgs, numVars, bindingArray))
return false; return false;
} }
@ -218,7 +223,7 @@ bool
Bindings::bindingIsAliased(unsigned bindingIndex) Bindings::bindingIsAliased(unsigned bindingIndex)
{ {
JS_ASSERT(bindingIndex < count()); JS_ASSERT(bindingIndex < count());
return bindingArray_[bindingIndex].aliased(); return bindingArray()[bindingIndex].aliased();
} }
void void
@ -227,7 +232,15 @@ Bindings::trace(JSTracer *trc)
if (callObjShape_) if (callObjShape_)
MarkShape(trc, &callObjShape_, "callObjShape"); MarkShape(trc, &callObjShape_, "callObjShape");
for (Binding *b = bindingArray_, *end = b + count(); b != end; b++) { /*
* As the comment in Bindings explains, bindingsArray may point into freed
* storage when bindingArrayUsingTemporaryStorage so we don't mark it.
* Note: during compilation, atoms are already kept alive by gcKeepAtoms.
*/
if (bindingArrayUsingTemporaryStorage())
return;
for (Binding *b = bindingArray(), *end = b + count(); b != end; b++) {
PropertyName *name = b->name(); PropertyName *name = b->name();
MarkStringUnbarriered(trc, &name, "bindingArray"); MarkStringUnbarriered(trc, &name, "bindingArray");
} }
@ -1573,7 +1586,7 @@ JSScript::partiallyInit(JSContext *cx, Handle<JSScript*> script,
cursor += vectorSize; cursor += vectorSize;
} }
cursor = script->bindings.switchStorageTo(reinterpret_cast<Binding *>(cursor)); cursor = script->bindings.switchToScriptStorage(reinterpret_cast<Binding *>(cursor));
script->code = (jsbytecode *)cursor; script->code = (jsbytecode *)cursor;
JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == script->data + size); JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == script->data + size);

View File

@ -87,20 +87,20 @@ class Binding
* One JSScript stores one Binding per formal/variable so we use a * One JSScript stores one Binding per formal/variable so we use a
* packed-word representation. * packed-word representation.
*/ */
size_t bits_; uintptr_t bits_;
static const size_t KIND_MASK = 0x3; static const uintptr_t KIND_MASK = 0x3;
static const size_t ALIASED_BIT = 0x4; static const uintptr_t ALIASED_BIT = 0x4;
static const size_t NAME_MASK = ~(KIND_MASK | ALIASED_BIT); static const uintptr_t NAME_MASK = ~(KIND_MASK | ALIASED_BIT);
public: public:
explicit Binding() : bits_(0) {} explicit Binding() : bits_(0) {}
Binding(PropertyName *name, BindingKind kind, bool aliased) { Binding(PropertyName *name, BindingKind kind, bool aliased) {
JS_STATIC_ASSERT(CONSTANT <= KIND_MASK); JS_STATIC_ASSERT(CONSTANT <= KIND_MASK);
JS_ASSERT((size_t(name) & ~NAME_MASK) == 0); JS_ASSERT((uintptr_t(name) & ~NAME_MASK) == 0);
JS_ASSERT((size_t(kind) & ~KIND_MASK) == 0); JS_ASSERT((uintptr_t(kind) & ~KIND_MASK) == 0);
bits_ = size_t(name) | size_t(kind) | (aliased ? ALIASED_BIT : 0); bits_ = uintptr_t(name) | uintptr_t(kind) | (aliased ? ALIASED_BIT : 0);
} }
PropertyName *name() const { PropertyName *name() const {
@ -116,7 +116,7 @@ class Binding
} }
}; };
JS_STATIC_ASSERT(sizeof(Binding) == sizeof(size_t)); JS_STATIC_ASSERT(sizeof(Binding) == sizeof(uintptr_t));
/* /*
* Formal parameters and local variables are stored in a shape tree * Formal parameters and local variables are stored in a shape tree
@ -130,20 +130,38 @@ class Bindings
friend class AliasedFormalIter; friend class AliasedFormalIter;
HeapPtr<Shape> callObjShape_; HeapPtr<Shape> callObjShape_;
Binding *bindingArray_; uintptr_t bindingArrayAndFlag_;
uint16_t numArgs_; uint16_t numArgs_;
uint16_t numVars_; uint16_t numVars_;
/*
* During parsing, bindings are allocated out of a temporary LifoAlloc.
* After parsing, a JSScript object is created and the bindings are
* permanently transferred to it. On error paths, the JSScript object may
* end up with bindings that still point to the (new released) LifoAlloc
* memory. To avoid tracing these bindings during GC, we keep track of
* whether the bindings are temporary or permanent in the low bit of
* bindingArrayAndFlag_.
*/
static const uintptr_t TEMPORARY_STORAGE_BIT = 0x1;
bool bindingArrayUsingTemporaryStorage() const {
return bindingArrayAndFlag_ & TEMPORARY_STORAGE_BIT;
}
Binding *bindingArray() const {
return reinterpret_cast<Binding *>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT);
}
public: public:
inline Bindings(); inline Bindings();
/* /*
* Initialize a Bindings. bindingArray must have length numArgs+numVars and * Initialize a Bindings with a pointer into temporary storage.
* must outlive Bindings. To use a temporary bindingArray, the caller may * bindingArray must have length numArgs+numVars. Before the temporary
* call switchStorageTo, providing new storage for Bindings to use. * storage is release, switchToScriptStorage must be called, providing a
* pointer into the Binding array stored in script->data.
*/ */
bool init(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray); bool initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray);
uint8_t *switchStorageTo(Binding *newStorage); uint8_t *switchToScriptStorage(Binding *newStorage);
/* /*
* Clone srcScript's bindings (as part of js::CloneScript). dstScriptData * Clone srcScript's bindings (as part of js::CloneScript). dstScriptData
@ -858,8 +876,8 @@ class BindingIter
return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs(); return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs();
} }
const Binding &operator*() const { JS_ASSERT(!done()); return bindings_->bindingArray_[i_]; } const Binding &operator*() const { JS_ASSERT(!done()); return bindings_->bindingArray()[i_]; }
const Binding *operator->() const { JS_ASSERT(!done()); return &bindings_->bindingArray_[i_]; } const Binding *operator->() const { JS_ASSERT(!done()); return &bindings_->bindingArray()[i_]; }
}; };
/* /*

View File

@ -24,7 +24,7 @@ namespace js {
inline inline
Bindings::Bindings() Bindings::Bindings()
: callObjShape_(NULL), bindingArray_(NULL), numArgs_(0), numVars_(0) : callObjShape_(NULL), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT), numArgs_(0), numVars_(0)
{} {}
bool bool
@ -35,7 +35,7 @@ Bindings::extensibleParents()
inline inline
AliasedFormalIter::AliasedFormalIter(JSScript *script) AliasedFormalIter::AliasedFormalIter(JSScript *script)
: begin_(script->bindings.bindingArray_), : begin_(script->bindings.bindingArray()),
p_(begin_), p_(begin_),
end_(begin_ + (script->funHasAnyAliasedFormal ? script->bindings.numArgs() : 0)), end_(begin_ + (script->funHasAnyAliasedFormal ? script->bindings.numArgs() : 0)),
slot_(CallObject::RESERVED_SLOTS) slot_(CallObject::RESERVED_SLOTS)