Bug 898347 - Generalize the newScript field on type objects into an "addendum" (Part 4/6) r=bhackett

This commit is contained in:
Nicholas D. Matsakis 2013-07-20 16:18:53 -04:00
parent d57113c33b
commit e088a95129
6 changed files with 152 additions and 77 deletions

View File

@ -1128,9 +1128,13 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
if (type->singleton && !type->lazy())
PushMarkStack(gcmarker, type->singleton);
if (type->newScript) {
PushMarkStack(gcmarker, type->newScript->fun);
PushMarkStack(gcmarker, type->newScript->shape.get());
if (type->addendum) {
switch (type->addendum->kind) {
case types::TypeObjectAddendum::NewScript:
PushMarkStack(gcmarker, type->newScript()->fun);
PushMarkStack(gcmarker, type->newScript()->shape.get());
break;
}
}
if (type->interpretedFunction)
@ -1153,9 +1157,13 @@ gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
if (type->singleton && !type->lazy())
MarkObject(trc, &type->singleton, "type_singleton");
if (type->newScript) {
MarkObject(trc, &type->newScript->fun, "type_new_function");
MarkShape(trc, &type->newScript->shape, "type_new_shape");
if (type->addendum) {
switch (type->addendum->kind) {
case types::TypeObjectAddendum::NewScript:
MarkObject(trc, &type->newScript()->fun, "type_new_function");
MarkShape(trc, &type->newScript()->shape, "type_new_shape");
break;
}
}
if (type->interpretedFunction)

View File

@ -4589,7 +4589,7 @@ IonBuilder::createThisScriptedSingleton(HandleFunction target, MDefinition *call
return NULL;
// Trigger recompilation if the templateObject changes.
if (templateObject->type()->newScript)
if (templateObject->type()->hasNewScript())
types::HeapTypeSet::WatchObjectStateChange(cx, templateObject->type());
MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(templateObject);

View File

@ -1921,12 +1921,12 @@ HeapTypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable)
* definite properties be invalidated.
*/
if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
if (object->newScript) {
if (object->hasNewScript()) {
Rooted<TypeObject*> typeObj(cx, object);
RootedFunction fun(cx, object->newScript->fun);
RootedFunction fun(cx, object->newScript()->fun);
CheckNewScriptProperties(cx, typeObj, fun);
} else {
JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED);
JS_ASSERT(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
}
}
@ -3679,8 +3679,8 @@ TypeObject::markUnknown(ExclusiveContext *cx)
JS_ASSERT(cx->compartment()->activeAnalysis);
JS_ASSERT(!unknownProperties());
if (!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED))
clearNewScript(cx);
if (!(flags & OBJECT_FLAG_ADDENDUM_CLEARED))
clearAddendum(cx);
InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
@ -3706,22 +3706,43 @@ TypeObject::markUnknown(ExclusiveContext *cx)
}
void
TypeObject::clearNewScript(ExclusiveContext *cx)
TypeObject::clearAddendum(ExclusiveContext *cx)
{
JS_ASSERT(!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED));
flags |= OBJECT_FLAG_NEW_SCRIPT_CLEARED;
JS_ASSERT(!(flags & OBJECT_FLAG_ADDENDUM_CLEARED));
flags |= OBJECT_FLAG_ADDENDUM_CLEARED;
/*
* It is possible for the object to not have a new script yet but to have
* one added in the future. When analyzing properties of new scripts we mix
* in adding constraints to trigger clearNewScript with changes to the
* type sets themselves (from breakTypeBarriers). It is possible that we
* could trigger one of these constraints before AnalyzeNewScriptProperties
* has finished, in which case we want to make sure that call fails.
* It is possible for the object to not have a new script or other
* addendum yet, but to have one added in the future. When
* analyzing properties of new scripts we mix in adding
* constraints to trigger clearNewScript with changes to the type
* sets themselves (from breakTypeBarriers). It is possible that
* we could trigger one of these constraints before
* AnalyzeNewScriptProperties has finished, in which case we want
* to make sure that call fails.
*/
if (!newScript)
if (!addendum)
return;
switch (addendum->kind) {
case TypeObjectAddendum::NewScript:
return clearNewScriptAddendum(cx);
}
/* We NULL out addendum *before* freeing it so the write barrier works. */
TypeObjectAddendum *savedAddendum = addendum;
addendum = NULL;
js_free(savedAddendum);
markStateChange(cx);
MOZ_ASSUME_UNREACHABLE("Invalid addendum kind");
}
void
TypeObject::clearNewScriptAddendum(ExclusiveContext *cx)
{
AutoEnterAnalysis enter(cx);
/*
@ -3754,7 +3775,7 @@ TypeObject::clearNewScript(ExclusiveContext *cx)
for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) {
pcOffsets.append(uint32_t(iter.pc() - iter.script()->code));
if (iter.isConstructing() &&
iter.callee() == newScript->fun &&
iter.callee() == newScript()->fun &&
iter.thisv().isObject() &&
!iter.thisv().toObject().hasLazyType() &&
iter.thisv().toObject().type() == this)
@ -3775,7 +3796,7 @@ TypeObject::clearNewScript(ExclusiveContext *cx)
size_t callDepth = pcOffsets.length() - 1;
uint32_t offset = pcOffsets[callDepth];
for (TypeNewScript::Initializer *init = newScript->initializerList;; init++) {
for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) {
if (init->kind == TypeNewScript::Initializer::SETPROP) {
if (!depth && init->offset > offset) {
/* Advanced past all properties which have been initialized. */
@ -3818,13 +3839,6 @@ TypeObject::clearNewScript(ExclusiveContext *cx)
// Threads with an ExclusiveContext are not allowed to run scripts.
JS_ASSERT(!cx->perThreadData->activation());
}
/* We NULL out newScript *before* freeing it so the write barrier works. */
TypeNewScript *savedNewScript = newScript;
newScript = NULL;
js_free(savedNewScript);
markStateChange(cx);
}
void
@ -4747,7 +4761,7 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
void newPropertyState(JSContext *cx, TypeSet *source)
{
if (!object->newScript)
if (!object->hasNewScript())
return;
/*
* Clear out the newScript shape and definite property information from
@ -4755,8 +4769,8 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
* non-writable, both of which are indicated by the source type set
* being marked as configured.
*/
if (!(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED) && source->ownProperty(true))
object->clearNewScript(cx);
if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->ownProperty(true))
object->clearAddendum(cx);
}
void newType(JSContext *cx, TypeSet *source, Type type) {}
@ -4800,11 +4814,11 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint
const char *kind() { return "clearDefiniteSingle"; }
void newType(JSContext *cx, TypeSet *source, Type type) {
if (object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)
if (object->flags & OBJECT_FLAG_ADDENDUM_CLEARED)
return;
if (source->baseFlags() || source->getObjectCount() > 1)
object->clearNewScript(cx);
object->clearAddendum(cx);
}
};
@ -5150,8 +5164,8 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu
/* Strawman object to add properties to and watch for duplicates. */
state.baseobj = NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16);
if (!state.baseobj) {
if (type->newScript)
type->clearNewScript(cx);
if (type->addendum)
type->clearAddendum(cx);
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
@ -5159,10 +5173,10 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu
AnalyzeNewScriptProperties(cx, type, fun, state);
if (!state.baseobj ||
state.baseobj->slotSpan() == 0 ||
!!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED))
!!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED))
{
if (type->newScript)
type->clearNewScript(cx);
if (type->addendum)
type->clearAddendum(cx);
return;
}
@ -5171,11 +5185,12 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu
* constraints and don't need to make another TypeNewScript. Make sure that
* the properties added to baseobj match the type's definite properties.
*/
if (type->newScript) {
if (type->hasNewScript()) {
if (!type->matchDefiniteProperties(state.baseobj))
type->clearNewScript(cx);
type->clearAddendum(cx);
return;
}
JS_ASSERT(!type->addendum);
gc::AllocKind kind = gc::GetGCObjectKind(state.baseobj->slotSpan());
@ -5200,29 +5215,31 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu
size_t numBytes = sizeof(TypeNewScript)
+ (state.initializerList.length() * sizeof(TypeNewScript::Initializer));
TypeNewScript *newScript;
#ifdef JSGC_ROOT_ANALYSIS
// calloc can legitimately return a pointer that appears to be poisoned.
void *p;
do {
p = cx->calloc_(numBytes);
} while (IsPoisonedPtr(p));
type->newScript = (TypeNewScript *) p;
newScript = (TypeNewScript *) p;
#else
type->newScript = (TypeNewScript *) cx->calloc_(numBytes);
newScript = (TypeNewScript *) cx->calloc_(numBytes);
#endif
type->addendum = newScript;
if (!type->newScript) {
if (!newScript) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
type->newScript->fun = fun;
type->newScript->allocKind = kind;
type->newScript->shape = state.baseobj->lastProperty();
newScript->fun = fun;
newScript->allocKind = kind;
newScript->shape = state.baseobj->lastProperty();
type->newScript->initializerList = (TypeNewScript::Initializer *)
((char *) type->newScript.get() + sizeof(TypeNewScript));
PodCopy(type->newScript->initializerList,
newScript->initializerList = (TypeNewScript::Initializer *)
((char *) newScript + sizeof(TypeNewScript));
PodCopy(newScript->initializerList,
state.initializerList.begin(),
state.initializerList.length());
}
@ -6058,8 +6075,8 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_)
* Object.create is called with a prototype object that is also the
* 'prototype' property of some scripted function.
*/
if (type->newScript && type->newScript->fun != fun_)
type->clearNewScript(this);
if (type->hasNewScript() && type->newScript()->fun != fun_)
type->clearAddendum(this);
return type;
}
@ -6235,7 +6252,7 @@ inline void
TypeObject::sweep(FreeOp *fop)
{
if (singleton) {
JS_ASSERT(!newScript);
JS_ASSERT(!hasNewScript());
/*
* All properties can be discarded. We will regenerate them as needed
@ -6247,8 +6264,8 @@ TypeObject::sweep(FreeOp *fop)
}
if (!isMarked()) {
if (newScript)
fop->free_(newScript);
if (addendum)
fop->free_(addendum);
return;
}
@ -6313,7 +6330,7 @@ TypeObject::sweep(FreeOp *fop)
* newScript information, these constraints will need to be regenerated
* the next time we compile code which depends on this info.
*/
if (newScript)
if (hasNewScript())
flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
}
@ -6665,11 +6682,11 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
* every GC. The type object is normally destroyed too, but we don't
* charge this to 'temporary' as this is not for GC heap values.
*/
JS_ASSERT(!newScript);
JS_ASSERT(!hasNewScript());
return 0;
}
return mallocSizeOf(newScript);
return mallocSizeOf(addendum);
}
TypeZone::TypeZone(Zone *zone)

View File

@ -355,8 +355,8 @@ enum {
/* Objects with this type are functions. */
OBJECT_FLAG_FUNCTION = 0x1,
/* If set, newScript information should not be installed on this object. */
OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x2,
/* If set, addendum information should not be installed on this object. */
OBJECT_FLAG_ADDENDUM_CLEARED = 0x2,
/*
* If set, type constraints covering the correctness of the newScript
@ -866,6 +866,29 @@ struct Property
static jsid getKey(Property *p) { return p->id; }
};
struct TypeNewScript;
struct TypeObjectAddendum
{
enum Kind {
NewScript
};
Kind kind;
bool isNewScript() {
return kind == NewScript;
}
TypeNewScript *asNewScript() {
JS_ASSERT(isNewScript());
return (TypeNewScript*) this;
}
static inline void writeBarrierPre(TypeObjectAddendum *newScript);
static void writeBarrierPost(TypeObjectAddendum *newScript, void *addr) {}
};
/*
* Information attached to a TypeObject if it is always constructed using 'new'
* on a particular script. This is used to manage state related to the definite
@ -876,7 +899,7 @@ struct Property
* remove the definite property information and repair the JS stack if the
* constraints are violated.
*/
struct TypeNewScript
struct TypeNewScript : public TypeObjectAddendum
{
HeapPtrFunction fun;
@ -912,7 +935,6 @@ struct TypeNewScript
Initializer *initializerList;
static inline void writeBarrierPre(TypeNewScript *newScript);
static void writeBarrierPost(TypeNewScript *newScript, void *addr) {}
};
/*
@ -970,11 +992,24 @@ struct TypeObject : gc::Cell
static inline size_t offsetOfFlags() { return offsetof(TypeObject, flags); }
/*
* If non-NULL, objects of this type have always been constructed using
* 'new' on the specified script, which adds some number of properties to
* the object in a definite order before the object escapes.
* This field allows various special classes of objects to attach
* additional information to a type object:
*
* - `TypeNewScript`: If addendum is a `TypeNewScript`, it
* indicates that objects of this type have always been
* constructed using 'new' on the specified script, which adds
* some number of properties to the object in a definite order
* before the object escapes.
*/
HeapPtr<TypeNewScript> newScript;
HeapPtr<TypeObjectAddendum> addendum;
bool hasNewScript() {
return addendum && addendum->isNewScript();
}
TypeNewScript *newScript() {
return addendum->asNewScript();
}
/*
* Properties of this object. This may contain JSID_VOID, representing the
@ -1068,7 +1103,8 @@ struct TypeObject : gc::Cell
void markStateChange(ExclusiveContext *cx);
void setFlags(ExclusiveContext *cx, TypeObjectFlags flags);
void markUnknown(ExclusiveContext *cx);
void clearNewScript(ExclusiveContext *cx);
void clearAddendum(ExclusiveContext *cx);
void clearNewScriptAddendum(ExclusiveContext *cx);
void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false);
void print();

View File

@ -1583,6 +1583,20 @@ TypeObject::readBarrier(TypeObject *type)
#endif
}
inline void
TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type)
{
#ifdef JSGC_INCREMENTAL
if (!type)
return;
switch (type->kind) {
case NewScript:
return TypeNewScript::writeBarrierPre(type->asNewScript());
}
#endif
}
inline void
TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
{

View File

@ -1543,18 +1543,18 @@ static inline JSObject *
CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
NewObjectKind newKind)
{
if (type->newScript) {
if (type->hasNewScript()) {
/*
* Make an object with the type's associated finalize kind and shape,
* which reflects any properties that will definitely be added to the
* object before it is read from.
*/
gc::AllocKind kind = type->newScript->allocKind;
gc::AllocKind kind = type->newScript()->allocKind;
RootedObject res(cx, NewObjectWithType(cx, type, parent, kind, newKind));
if (!res)
return NULL;
RootedObject metadata(cx, res->getMetadata());
RootedShape shape(cx, type->newScript->shape);
RootedShape shape(cx, type->newScript()->shape);
JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res, shape));
if (metadata && !JSObject::setMetadata(cx, res, metadata))
return NULL;
@ -2538,8 +2538,8 @@ JSObject::growSlots(ExclusiveContext *cx, HandleObject obj, uint32_t oldCount, u
* type to give these objects a larger number of fixed slots when future
* objects are constructed.
*/
if (!obj->hasLazyType() && !oldCount && obj->type()->newScript) {
gc::AllocKind kind = obj->type()->newScript->allocKind;
if (!obj->hasLazyType() && !oldCount && obj->type()->hasNewScript()) {
gc::AllocKind kind = obj->type()->newScript()->allocKind;
uint32_t newScriptSlots = gc::GetGCKindSlots(kind);
if (newScriptSlots == obj->numFixedSlots() &&
gc::TryIncrementAllocKind(&kind) &&
@ -2549,13 +2549,13 @@ JSObject::growSlots(ExclusiveContext *cx, HandleObject obj, uint32_t oldCount, u
AutoEnterAnalysis enter(ncx);
Rooted<TypeObject*> typeObj(cx, obj->type());
RootedShape shape(cx, typeObj->newScript->shape);
RootedShape shape(cx, typeObj->newScript()->shape);
JSObject *reshapedObj = NewReshapedObject(ncx, typeObj, obj->getParent(), kind, shape);
if (!reshapedObj)
return false;
typeObj->newScript->allocKind = kind;
typeObj->newScript->shape = reshapedObj->lastProperty();
typeObj->newScript()->allocKind = kind;
typeObj->newScript()->shape = reshapedObj->lastProperty();
typeObj->markStateChange(ncx);
}
}