Bug 780309 - Implement InternalHandle and use it for Bindings. r=terrence

This commit is contained in:
Steve Fink 2012-09-10 11:26:46 -07:00
parent 5de9a55989
commit 2f044a0ac0
8 changed files with 113 additions and 60 deletions

View File

@ -114,7 +114,8 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
// Global/eval script bindings are always empty (all names are added to the
// scope dynamically via JSOP_DEFFUN/VAR).
if (!script->bindings.initWithTemporaryStorage(cx, 0, 0, NULL))
InternalHandle<Bindings*> bindings(script, &script->bindings);
if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL))
return NULL;
// We can specialize a bit for the given scope chain if that scope chain is the global object.
@ -327,7 +328,8 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
if (!script)
return false;
if (!funpc.generateFunctionBindings(cx, &script->bindings))
InternalHandle<Bindings*> bindings(script, &script->bindings);
if (!funpc.generateFunctionBindings(cx, bindings))
return false;
BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, /* callerFrame = */ NULL,

View File

@ -296,7 +296,7 @@ AppendPackedBindings(const ParseContext *pc, const DeclVector &vec, Binding *dst
}
bool
ParseContext::generateFunctionBindings(JSContext *cx, Bindings *bindings) const
ParseContext::generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const
{
JS_ASSERT(sc->inFunction());
@ -310,8 +310,11 @@ ParseContext::generateFunctionBindings(JSContext *cx, Bindings *bindings) const
AppendPackedBindings(this, args_, packedBindings);
AppendPackedBindings(this, vars_, packedBindings + args_.length());
if (!bindings->initWithTemporaryStorage(cx, args_.length(), vars_.length(), packedBindings))
if (!Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
packedBindings))
{
return false;
}
if (bindings->hasAnyAliasedBindings() || sc->funHasExtensibleScope())
sc->funbox()->fun()->flags |= JSFUN_HEAVYWEIGHT;
@ -1257,7 +1260,9 @@ LeaveFunction(ParseNode *fn, Parser *parser, PropertyName *funName = NULL,
}
}
if (!funpc->generateFunctionBindings(cx, &funbox->bindings))
InternalHandle<Bindings*> bindings =
InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
if (!funpc->generateFunctionBindings(cx, bindings))
return false;
funpc->lexdeps.releaseMap(cx);

View File

@ -151,7 +151,7 @@ struct ParseContext /* tree context for semantic checks */
* - Sometimes a script's bindings are accessed at runtime to retrieve the
* contents of the lexical scope (e.g., from the debugger).
*/
bool generateFunctionBindings(JSContext *cx, Bindings *bindings) const;
bool generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const;
public:
ParseNode *yieldNode; /* parse node for a yield expression that might

View File

@ -240,6 +240,73 @@ typedef JSObject * RawObject;
typedef JSString * RawString;
typedef Value RawValue;
/*
* InternalHandle is a handle to an internal pointer into a gcthing. Use
* InternalHandle when you have a pointer to a direct field of a gcthing, or
* when you need a parameter type for something that *may* be a pointer to a
* direct field of a gcthing.
*/
class InternalHandleBase
{
protected:
static void * const zeroPointer;
};
template <typename T>
class InternalHandle { };
template <typename T>
class InternalHandle<T*> : public InternalHandleBase
{
void * const *holder;
size_t offset;
public:
/*
* Create an InternalHandle using a Handle to the gcthing containing the
* field in question, and a pointer to the field.
*/
template<typename H>
InternalHandle(const Handle<H> &handle, T *field)
: holder((void**)handle.address()), offset(uintptr_t(field) - uintptr_t(handle.get()))
{
}
/*
* Create an InternalHandle to a field within a Rooted<>.
*/
template<typename R>
InternalHandle(const Rooted<R> &root, T *field)
: holder((void**)root.address()), offset(uintptr_t(field) - uintptr_t(root.get()))
{
}
T *get() const { return reinterpret_cast<T*>(uintptr_t(*holder) + offset); }
const T& operator *() const { return *get(); }
T* operator ->() const { return get(); }
static InternalHandle<T*> fromMarkedLocation(T *fieldPtr) {
return InternalHandle(fieldPtr);
}
private:
/*
* Create an InternalHandle to something that is not a pointer to a
* gcthing, and so does not need to be rooted in the first place. Use these
* InternalHandles to pass pointers into functions that also need to accept
* regular InternalHandles to gcthing fields.
*
* Make this private to prevent accidental misuse; this is only for
* fromMarkedLocation().
*/
InternalHandle(T *field)
: holder(reinterpret_cast<void * const *>(&zeroPointer)),
offset(uintptr_t(field))
{
}
};
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
/*

View File

@ -1061,7 +1061,6 @@ class JS_PUBLIC_API(AutoGCRooter) {
SHAPERANGE = -20, /* js::Shape::Range::AutoRooter */
STACKSHAPE = -21, /* js::StackShape::AutoRooter */
STACKBASESHAPE=-22,/* js::StackBaseShape::AutoRooter */
BINDINGS = -23, /* js::Bindings::AutoRooter */
GETTERSETTER =-24, /* js::AutoRooterGetterSetter */
REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */
NAMEVECTOR = -26, /* js::AutoNameVector */

View File

@ -103,6 +103,8 @@ using namespace mozilla;
using namespace js;
using namespace js::gc;
void * const JS::InternalHandleBase::zeroPointer = NULL;
namespace js {
namespace gc {
@ -2402,12 +2404,6 @@ AutoGCRooter::trace(JSTracer *trc)
return;
}
case BINDINGS: {
Bindings::AutoRooter *rooter = static_cast<Bindings::AutoRooter *>(this);
rooter->trace(trc);
return;
}
case GETTERSETTER: {
AutoRooterGetterSetter::Inner *rooter = static_cast<AutoRooterGetterSetter::Inner *>(this);
if ((rooter->attrs & JSPROP_GETTER) && *rooter->pgetter)
@ -2453,12 +2449,6 @@ Shape::Range::AutoRooter::trace(JSTracer *trc)
MarkShapeRoot(trc, const_cast<Shape**>(&r->cursor), "Shape::Range::AutoRooter");
}
void
Bindings::AutoRooter::trace(JSTracer *trc)
{
bindings->trace(trc);
}
void
RegExpStatics::AutoRooter::trace(JSTracer *trc)
{

View File

@ -60,23 +60,25 @@ Bindings::argumentsVarIndex(JSContext *cx) const
}
bool
Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray)
Bindings::initWithTemporaryStorage(JSContext *cx, InternalHandle<Bindings*> self,
unsigned numArgs, unsigned numVars,
Binding *bindingArray)
{
JS_ASSERT(!callObjShape_);
JS_ASSERT(bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
JS_ASSERT(!self->callObjShape_);
JS_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
if (numArgs > UINT16_MAX || numVars > UINT16_MAX) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
numArgs_ > numVars_ ?
self->numArgs_ > self->numVars_ ?
JSMSG_TOO_MANY_FUN_ARGS :
JSMSG_TOO_MANY_LOCALS);
return false;
}
JS_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
numArgs_ = numArgs;
numVars_ = numVars;
self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
self->numArgs_ = numArgs;
self->numVars_ = numVars;
/*
* Get the initial shape to use when creating CallObjects for this script.
@ -90,8 +92,8 @@ Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned num
JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == 2);
gc::AllocKind allocKind = gc::FINALIZE_OBJECT2_BACKGROUND;
JS_ASSERT(gc::GetGCKindSlots(allocKind) == CallObject::RESERVED_SLOTS);
callObjShape_ = EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(),
allocKind, BaseShape::VAROBJ);
self->callObjShape_ = EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(),
allocKind, BaseShape::VAROBJ);
#ifdef DEBUG
HashSet<PropertyName *> added(cx);
@ -99,9 +101,9 @@ Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned num
return false;
#endif
BindingIter bi(*this);
BindingIter bi(*self);
unsigned slot = CallObject::RESERVED_SLOTS;
for (unsigned i = 0, n = count(); i < n; i++, bi++) {
for (unsigned i = 0, n = self->count(); i < n; i++, bi++) {
if (!bi->aliased())
continue;
@ -123,8 +125,8 @@ Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned num
unsigned frameIndex = bi.frameIndex();
StackShape child(nbase, id, slot++, 0, attrs, Shape::HAS_SHORTID, frameIndex);
callObjShape_ = callObjShape_->getChildBinding(cx, child);
if (!callObjShape_)
self->callObjShape_ = self->callObjShape_->getChildBinding(cx, child);
if (!self->callObjShape_)
return false;
}
JS_ASSERT(!bi);
@ -144,7 +146,9 @@ Bindings::switchToScriptStorage(Binding *newBindingArray)
}
bool
Bindings::clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript)
Bindings::clone(JSContext *cx, InternalHandle<Bindings*> self,
uint8_t *dstScriptData,
HandleScript srcScript)
{
/* The clone has the same bindingArray_ offset as 'src'. */
Bindings &src = srcScript->bindings;
@ -157,9 +161,9 @@ Bindings::clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript)
* Since atoms are shareable throughout the runtime, we can simply copy
* the source's bindingArray directly.
*/
if (!initWithTemporaryStorage(cx, src.numArgs(), src.numVars(), src.bindingArray()))
if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), src.bindingArray()))
return false;
switchToScriptStorage(dstPackedBindings);
self->switchToScriptStorage(dstPackedBindings);
return true;
}
@ -210,7 +214,8 @@ XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, unsigned numArgs, un
bindingArray[i] = Binding(name, kind, aliased);
}
if (!script->bindings.initWithTemporaryStorage(cx, numArgs, numVars, bindingArray))
InternalHandle<Bindings*> bindings(script, &script->bindings);
if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars, bindingArray))
return false;
}
@ -2064,8 +2069,9 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
/* Bindings */
Bindings bindings;
Bindings::AutoRooter bindingRooter(cx, &bindings);
if (!bindings.clone(cx, data, src))
InternalHandle<Bindings*> bindingsHandle =
InternalHandle<Bindings*>::fromMarkedLocation(&bindings);
if (!Bindings::clone(cx, bindingsHandle, data, src))
return NULL;
/* Objects */
@ -2130,6 +2136,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
js_free(data);
return NULL;
}
AutoAssertNoGC nogc;
dst->bindings = bindings;

View File

@ -160,14 +160,17 @@ class Bindings
* storage is release, switchToScriptStorage must be called, providing a
* pointer into the Binding array stored in script->data.
*/
bool initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray);
static bool initWithTemporaryStorage(JSContext *cx, InternalHandle<Bindings*> self,
unsigned numArgs, unsigned numVars,
Binding *bindingArray);
uint8_t *switchToScriptStorage(Binding *newStorage);
/*
* Clone srcScript's bindings (as part of js::CloneScript). dstScriptData
* is the pointer to what will eventually be dstScript->data.
*/
bool clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript);
static bool clone(JSContext *cx, InternalHandle<Bindings*> self, uint8_t *dstScriptData, HandleScript srcScript);
unsigned numArgs() const { return numArgs_; }
unsigned numVars() const { return numVars_; }
@ -186,26 +189,6 @@ class Bindings
bool hasAnyAliasedBindings() const { return !callObjShape_->isEmptyShape(); }
void trace(JSTracer *trc);
class AutoRooter;
};
class Bindings::AutoRooter : private AutoGCRooter
{
public:
explicit AutoRooter(JSContext *cx, Bindings *bindings_
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoGCRooter(cx, BINDINGS), bindings(bindings_), skip(cx, bindings_)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
friend void AutoGCRooter::trace(JSTracer *trc);
void trace(JSTracer *trc);
private:
Bindings *bindings;
SkipRoot skip;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class ScriptCounts