[INFER] Direct instance property accesses, bug 649376.

This commit is contained in:
Brian Hackett 2011-04-12 20:39:16 -07:00
parent 793bec3ed6
commit 21fd2d8254
13 changed files with 699 additions and 63 deletions

View File

@ -0,0 +1,29 @@
function Foo(x)
{
this.f = x + 10;
}
function Bar()
{
this.g = 0;
}
Bar.prototype = Foo.prototype;
var x = new Foo(0);
var y = new Bar();
assertEq(10, eval("x.f"));
assertEq(undefined, eval("y.f"));
function Other(x)
{
this.f = x + 10;
}
var a = new Other(0);
var b = Object.create(Other.prototype);
assertEq(10, eval("a.f"));
assertEq(undefined, eval("b.f"));

View File

@ -0,0 +1,16 @@
function Foo(x)
{
this.f = x + 10;
}
var x = new Foo(0);
assertEq(10, eval("x.f"));
called = false;
Object.defineProperty(Foo.prototype, 'f', {set: function() { called = true; }});
var y = new Foo(0);
assertEq(10, eval("x.f"));
assertEq(undefined, eval("y.f"));
assertEq(called, true);

View File

@ -2329,7 +2329,8 @@ class AutoGCRooter {
STRING = -14, /* js::AutoStringRooter */
IDVECTOR = -15, /* js::AutoIdVector */
BINDINGS = -16, /* js::Bindings */
SHAPEVECTOR = -17 /* js::AutoShapeVector */
SHAPEVECTOR = -17, /* js::AutoShapeVector */
TYPE = -18 /* js::types::AutoTypeRooter */
};
private:

View File

@ -1611,6 +1611,13 @@ AutoGCRooter::trace(JSTracer *trc)
static_cast<js::AutoBindingsRooter *>(this)->bindings.trace(trc);
return;
}
case TYPE: {
types::TypeObject *type = static_cast<types::AutoTypeRooter *>(this)->type;
if (!type->marked)
type->trace(trc);
return;
}
}
JS_ASSERT(tag >= 0);

View File

@ -60,6 +60,7 @@
#include "methodjit/Retcon.h"
#include "jsatominlines.h"
#include "jsgcinlines.h"
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
@ -382,11 +383,11 @@ TypeSet::addTypeSet(JSContext *cx, ClonedTypeSet *types)
inline void
TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
{
JS_ASSERT_IF(!constraint->condensed() && !constraint->baseSubset(),
JS_ASSERT_IF(!constraint->condensed() && !constraint->persistentObject(),
constraint->script->compartment == cx->compartment);
JS_ASSERT_IF(!constraint->condensed(), cx->compartment->types.inferenceDepth);
JS_ASSERT_IF(typeFlags & TYPE_FLAG_INTERMEDIATE_SET,
!constraint->baseSubset() && !constraint->condensed());
!constraint->persistentObject() && !constraint->condensed());
if (!constraint) {
/* OOM failure while constructing the constraint. */
@ -432,6 +433,9 @@ TypeSet::print(JSContext *cx)
if (typeFlags & TYPE_FLAG_CONFIGURED_PROPERTY)
printf(" [configured]");
if (isDefiniteProperty())
printf(" [definite:%d]", definiteSlot());
if (baseFlags() == 0 && !objectCount) {
printf(" missing");
return;
@ -512,7 +516,7 @@ public:
void newType(JSContext *cx, TypeSet *source, jstype type);
TypeObject * baseSubset() { return object; }
TypeObject * persistentObject() { return object; }
};
void
@ -521,6 +525,28 @@ TypeSet::addBaseSubset(JSContext *cx, TypeObject *obj, TypeSet *target)
add(cx, cx->new_<TypeConstraintBaseSubset>(obj, target));
}
/* Constraint clearing out newScript and definite properties from an object. */
class TypeConstraintBaseClearDefinite : public TypeConstraint
{
public:
TypeObject *object;
TypeConstraintBaseClearDefinite(TypeObject *object)
: TypeConstraint("baseClearDefinite", (JSScript *) 0x1),
object(object)
{}
void newType(JSContext *cx, TypeSet *source, jstype type);
TypeObject * persistentObject() { return object; }
};
void
TypeSet::addBaseClearDefinite(JSContext *cx, TypeObject *object)
{
add(cx, cx->new_<TypeConstraintBaseClearDefinite>(object));
}
/* Condensed constraint marking a script dependent on this type set. */
class TypeConstraintCondensed : public TypeConstraint
{
@ -793,6 +819,18 @@ TypeConstraintBaseSubset::newType(JSContext *cx, TypeSet *source, jstype type)
target->addType(cx, type);
}
void
TypeConstraintBaseClearDefinite::newType(JSContext *cx, TypeSet *source, jstype type)
{
/*
* If the type could represent a setter, clear out the newScript shape and
* definite property information from the target object. Any type set with
* a getter/setter becomes unknown, so just watch for this type.
*/
if (type == TYPE_UNKNOWN && object->newScript)
object->clearNewScript(cx);
}
/* Get the object to use for a property access on type. */
static inline TypeObject *
GetPropertyObject(JSContext *cx, JSScript *script, jstype type)
@ -1752,6 +1790,19 @@ TypeCompartment::newInitializerTypeObject(JSContext *cx, JSScript *script,
res->initializerObject = true;
res->initializerOffset = offset;
if (JSOp(script->code[offset]) == JSOP_NEWOBJECT) {
/*
* This object is always constructed the same way and will not be
* observed by other code before all properties have been added. Mark
* all the properties as definite properties of the object.
*/
JS_ASSERT(!script->hasSharps);
JSObject *baseobj = script->getObject(GET_SLOTNO(script->code + offset));
if (!res->addDefiniteProperties(cx, baseobj, false))
return NULL;
}
return res;
}
@ -2306,7 +2357,11 @@ TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
if (p) {
obj->setType(p->value);
} else {
TypeObject *objType = newTypeObject(cx, NULL, "TableArray", false, true, obj->getProto());
static unsigned count = 0;
char *name = (char *) alloca(20);
JS_snprintf(name, 20, "TableArray:%u", ++count);
TypeObject *objType = newTypeObject(cx, NULL, name, false, true, obj->getProto());
if (!objType) {
js_ReportOutOfMemory(cx);
return false;
@ -2335,19 +2390,23 @@ struct ObjectTableKey
{
jsid *ids;
uint32 nslots;
uint32 nfixed;
JSObject *proto;
typedef JSObject * Lookup;
static inline uint32 hash(JSObject *obj) {
return (uint32) (JSID_BITS(obj->lastProperty()->id) ^
obj->slotSpan() ^
obj->slotSpan() ^ obj->numFixedSlots() ^
((uint32)(size_t)obj->getProto() >> 2));
}
static inline bool match(const ObjectTableKey &v, JSObject *obj) {
if (obj->slotSpan() != v.nslots || obj->getProto() != v.proto)
if (obj->slotSpan() != v.nslots ||
obj->numFixedSlots() != v.nfixed ||
obj->getProto() != v.proto) {
return false;
}
const Shape *shape = obj->lastProperty();
while (!JSID_IS_EMPTY(shape->id)) {
if (shape->id != v.ids[shape->slot])
@ -2431,7 +2490,11 @@ TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
}
AutoObjectRooter xvr(cx, xobj);
TypeObject *objType = newTypeObject(cx, NULL, "TableObject", false, false, obj->getProto());
static unsigned count = 0;
char *name = (char *) alloca(20);
JS_snprintf(name, 20, "TableObject:%u", ++count);
TypeObject *objType = newTypeObject(cx, NULL, name, false, false, obj->getProto());
if (!objType) {
js_ReportOutOfMemory(cx);
return false;
@ -2465,9 +2528,13 @@ TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
JS_ASSERT(!xobj->inDictionaryMode());
const Shape *newShape = xobj->lastProperty();
if (!objType->addDefiniteProperties(cx, xobj, false))
return false;
ObjectTableKey key;
key.ids = ids;
key.nslots = obj->slotSpan();
key.nfixed = obj->numFixedSlots();
key.proto = obj->getProto();
JS_ASSERT(ObjectTableKey::match(key, obj));
@ -2583,6 +2650,57 @@ TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
return true;
}
bool
TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj, bool clearUnknown)
{
if (unknownProperties())
return true;
/*
* Mark all properties of obj as definite properties of this type. Return
* false if there is a setter/getter for any of the properties in the
* type's prototype.
*/
AutoEnterTypeInference enter(cx);
const Shape *shape = obj->lastProperty();
while (!JSID_IS_EMPTY(shape->id)) {
jsid id = MakeTypeId(cx, shape->id);
if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot) &&
shape->slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
TypeSet *types = getProperty(cx, id, true);
if (!types)
return false;
types->setDefinite(shape->slot);
if (clearUnknown && proto) {
/*
* Ensure that if any of the properties named here could have
* a setter in the direct prototype (and thus its transitive
* prototypes), the definite properties and new shape attached
* to this object get cleared out. clearUnknown is set if the
* definite properties are affected by prototype setters
* (i.e. objects from scripted 'new', but not objects from
* initializers).
*/
TypeSet *parentTypes = proto->getType()->getProperty(cx, id, false);
if (!parentTypes || parentTypes->unknown())
return false;
parentTypes->addBaseClearDefinite(cx, this);
}
} else {
/*
* We should have filtered these properties out before adding them
* to the shape associated with the new type.
*/
JS_ASSERT(!clearUnknown);
}
shape = shape->previous();
}
return cx->compartment->types.checkPendingRecompiles(cx);
}
void
TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
{
@ -2634,6 +2752,34 @@ TypeObject::markUnknown(JSContext *cx)
}
}
void
TypeObject::clearNewScript(JSContext *cx)
{
JS_ASSERT(newScript);
newScript = NULL;
AutoEnterTypeInference enter(cx);
/*
* Any definite properties we added due to analysis of the new script when
* the type object was created are now invalid: objects with the same type
* can be created by using 'new' on a different script or through some
* other mechanism (e.g. Object.create). Rather than clear out the definite
* bits on the object's properties, just mark such properties as having
* been deleted/reconfigured, which will have the same effect on JITs
* wanting to use the definite bits to optimize property accesses.
*/
for (unsigned i = 0; i < getPropertyCount(); i++) {
Property *prop = getProperty(i);
if (!prop)
continue;
if (prop->types.isDefiniteProperty())
prop->types.setOwnProperty(cx, true);
}
cx->compartment->types.checkPendingRecompiles(cx); // :XXX: handle failure
}
void
TypeObject::print(JSContext *cx)
{
@ -3774,6 +3920,172 @@ AnalyzeScriptNew(JSContext *cx, JSScript *script)
prototypeTypes->addNewObject(cx, script, funType, script->thisTypes());
}
JSObject *
AnalyzeScriptProperties(JSContext *cx, JSScript *script)
{
/*
* When invoking 'new' on the specified script, try to find some properties
* which will definitely be added to the created object before it has a
* chance to escape and be accessed elsewhere. This analysis is a forward
* scan through the script looking for assignments to 'this.f'. Any
* branching kills it, along with any use of 'this' other than for property
* assignments.
*/
/* Strawman object to add properties to and watch for duplicates. */
JSObject *baseobj = NewBuiltinClassInstance(cx, &js_ObjectClass, gc::FINALIZE_OBJECT16);
if (!baseobj)
return NULL;
/* Number of added properties. */
unsigned numProperties = 0;
/* If 'this' is on the stack, index of its stack slot. */
unsigned thisSlot = unsigned(-1);
unsigned offset = 0;
unsigned depth = 0;
while (offset < script->length) {
jsbytecode *pc = script->code + offset;
JSOp op = JSOp(*pc);
unsigned nuses = analyze::GetUseCount(script, offset);
unsigned ndefs = analyze::GetDefCount(script, offset);
bool poppedThis = false;
if (thisSlot != unsigned(-1) && thisSlot >= depth - nuses) {
if (op != JSOP_SETPROP || thisSlot != depth - 2) {
/*
* 'this' escapes here and may be accessed before subsequent
* properties are added to the object.
*/
return baseobj;
}
poppedThis = true;
thisSlot = unsigned(-1);
}
depth -= nuses;
depth += ndefs;
switch (JSOp(*pc)) {
case JSOP_THIS:
thisSlot = depth - 1;
break;
case JSOP_SETPROP: {
if (!poppedThis)
return baseobj;
jsid id = GetAtomId(cx, script, pc, 0);
if (JSID_IS_VOID(id))
return baseobj;
if (id == id_prototype(cx) || id == id___proto__(cx) || id == id_constructor(cx))
return baseobj;
if (!js_DefineNativeProperty(cx, baseobj, id, UndefinedValue(), NULL, NULL,
JSPROP_ENUMERATE, 0, 0, NULL, 0)) {
return NULL;
}
numProperties++;
if (baseobj->slotSpan() != numProperties) {
/* Set a duplicate property. */
return baseobj;
}
if (baseobj->inDictionaryMode())
return NULL;
if (numProperties >= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
/* Maximum number of definite properties added. */
return baseobj;
}
break;
}
/* Whitelist of other ops that can be used while initializing 'this' properties. */
case JSOP_POP:
case JSOP_NOP:
case JSOP_LINENO:
case JSOP_POPN:
case JSOP_VOID:
case JSOP_PUSH:
case JSOP_ZERO:
case JSOP_ONE:
case JSOP_INT8:
case JSOP_INT32:
case JSOP_UINT16:
case JSOP_UINT24:
case JSOP_BITAND:
case JSOP_BITOR:
case JSOP_BITXOR:
case JSOP_BITNOT:
case JSOP_RSH:
case JSOP_LSH:
case JSOP_URSH:
case JSOP_FALSE:
case JSOP_TRUE:
case JSOP_EQ:
case JSOP_NE:
case JSOP_LT:
case JSOP_LE:
case JSOP_GT:
case JSOP_GE:
case JSOP_NOT:
case JSOP_STRICTEQ:
case JSOP_STRICTNE:
case JSOP_IN:
case JSOP_DOUBLE:
case JSOP_STRING:
case JSOP_REGEXP:
case JSOP_DUP:
case JSOP_DUP2:
case JSOP_GETGLOBAL:
case JSOP_GETGNAME:
case JSOP_GETARG:
case JSOP_SETARG:
case JSOP_INCARG:
case JSOP_DECARG:
case JSOP_ARGINC:
case JSOP_ARGDEC:
case JSOP_GETLOCAL:
case JSOP_SETLOCAL:
case JSOP_SETLOCALPOP:
case JSOP_INCLOCAL:
case JSOP_DECLOCAL:
case JSOP_LOCALINC:
case JSOP_LOCALDEC:
case JSOP_GETPROP:
case JSOP_GETARGPROP:
case JSOP_GETLOCALPROP:
case JSOP_GETELEM:
case JSOP_LENGTH:
case JSOP_ADD:
case JSOP_SUB:
case JSOP_MUL:
case JSOP_MOD:
case JSOP_DIV:
case JSOP_NEG:
case JSOP_POS:
case JSOP_NEWINIT:
case JSOP_NEWARRAY:
case JSOP_NEWOBJECT:
case JSOP_ENDINIT:
case JSOP_INITELEM:
case JSOP_HOLE:
case JSOP_INITPROP:
case JSOP_INITMETHOD:
break;
default:
return baseobj;
}
offset += analyze::GetBytecodeLength(pc);
}
/* We should have bailed out on a JSOP_STOP or similar. */
JS_NOT_REACHED("Mystery!");
return baseobj;
}
/////////////////////////////////////////////////////////////////////
// Printing
/////////////////////////////////////////////////////////////////////
@ -4194,7 +4506,7 @@ JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value
/////////////////////////////////////////////////////////////////////
void
JSObject::makeNewType(JSContext *cx)
JSObject::makeNewType(JSContext *cx, JSScript *newScript)
{
JS_ASSERT(!newType);
@ -4202,18 +4514,51 @@ JSObject::makeNewType(JSContext *cx)
if (!type)
return;
if (cx->typeInferenceEnabled() && !getType()->unknownProperties()) {
js::types::AutoEnterTypeInference enter(cx);
if (!cx->typeInferenceEnabled()) {
newType = type;
setDelegate();
return;
}
js::types::AutoEnterTypeInference enter(cx);
if (!getType()->unknownProperties()) {
/* Update the possible 'new' types for all prototype objects sharing the same type object. */
js::types::TypeSet *types = getType()->getProperty(cx, JSID_EMPTY, true);
if (types)
types->addType(cx, (js::types::jstype) type);
if (!cx->compartment->types.checkPendingRecompiles(cx))
return;
}
if (newScript && !type->unknownProperties()) {
JSObject *baseobj = js::types::AnalyzeScriptProperties(cx, newScript);
if (baseobj && baseobj->slotSpan() > 0) {
js::gc::FinalizeKind kind = js::gc::GetGCObjectKind(baseobj->slotSpan());
/* We should not have overflowed the maximum number of fixed slots for an object. */
JS_ASSERT(js::gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
/*
* The base object was created with a different type and
* finalize kind than we will use for subsequent new objects.
* Generate an object with the appropriate final shape.
*/
baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind,
(const js::Shape *) baseobj->lastProperty());
if (!baseobj)
return;
if (!type->addDefiniteProperties(cx, baseobj, true))
return;
type->newScript = newScript;
type->newScriptFinalizeKind = unsigned(kind);
type->newScriptShape = (js::Shape *) baseobj->lastProperty();
}
}
if (!cx->compartment->types.checkPendingRecompiles(cx))
return;
newType = type;
setDelegate();
}
@ -4261,6 +4606,11 @@ types::TypeObject::trace(JSTracer *trc)
if (singleton)
gc::MarkObject(trc, *singleton, "type_singleton");
if (newScript) {
js_TraceScript(trc, newScript);
gc::MarkShape(trc, newScriptShape, "new_shape");
}
}
/*
@ -4343,7 +4693,7 @@ TypeSet::CondenseSweepTypeSet(JSContext *cx, TypeCompartment *compartment,
while (constraint) {
TypeConstraint *next = constraint->next;
TypeObject *object = constraint->baseSubset();
TypeObject *object = constraint->persistentObject();
if (object) {
/*
* Constraint propagating data between objects. If the target

View File

@ -197,11 +197,12 @@ public:
virtual bool condensed() { return false; }
/*
* If this is a persistent subset constraint, the object being propagated
* into. Such constraints describe relationships between TypeObject
* properties which are independent of the analysis of any script.
* If this is a persistent constraint other than a condensed constraint,
* the target object of the constraint. Such constraints describe
* relationships between TypeObjects which are independent of the analysis
* of any script.
*/
virtual TypeObject * baseSubset() { return NULL; }
virtual TypeObject * persistentObject() { return NULL; }
};
/*
@ -241,19 +242,33 @@ enum {
TYPE_FLAG_UNKNOWN = 1 << TYPE_UNKNOWN,
/* Flag for type sets which are cleared on GC. */
TYPE_FLAG_INTERMEDIATE_SET = 0x1000,
TYPE_FLAG_INTERMEDIATE_SET = 0x0100,
/* For object property type sets, whether this property has been directly written. */
TYPE_FLAG_OWN_PROPERTY = 0x2000,
/* Flags for type sets which are on object properties. */
/* Whether this property has ever been directly written. */
TYPE_FLAG_OWN_PROPERTY = 0x0200,
/*
* For object property type sets, whether the property has ever been
* deleted or reconfigured as non-writable.
* Whether the property has ever been deleted or reconfigured to behave
* differently from a normal native property (e.g. made non-writable or
* given a scripted getter or setter).
*/
TYPE_FLAG_CONFIGURED_PROPERTY = 0x4000,
TYPE_FLAG_CONFIGURED_PROPERTY = 0x0400,
/*
* Whether the property is definitely in a particular inline slot on all
* objects from which it has not been deleted or reconfigured. Implies
* OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
*/
TYPE_FLAG_DEFINITE_PROPERTY = 0x0800,
/* If the property is definite, mask and shift storing the slot. */
TYPE_FLAG_DEFINITE_MASK = 0xf000,
TYPE_FLAG_DEFINITE_SHIFT = 12,
/* Mask of non-type flags on a type set. */
TYPE_FLAG_BASE_MASK = 0x7000
TYPE_FLAG_BASE_MASK = 0xffffff00
};
/* Vector of the above flags. */
@ -280,13 +295,6 @@ class TypeSet
void print(JSContext *cx);
void setIntermediate() { typeFlags |= TYPE_FLAG_INTERMEDIATE_SET; }
void setOwnProperty(bool configurable) {
typeFlags |= TYPE_FLAG_OWN_PROPERTY;
if (configurable)
typeFlags |= TYPE_FLAG_CONFIGURED_PROPERTY;
}
inline void destroy(JSContext *cx);
/* Whether this set contains a specific type. */
@ -296,6 +304,12 @@ class TypeSet
bool hasAnyFlag(TypeFlags flags) { return typeFlags & flags; }
bool unknown() { return typeFlags & TYPE_FLAG_UNKNOWN; }
bool isDefiniteProperty() { return typeFlags & TYPE_FLAG_DEFINITE_PROPERTY; }
unsigned definiteSlot() {
JS_ASSERT(isDefiniteProperty());
return typeFlags >> TYPE_FLAG_DEFINITE_SHIFT;
}
/*
* Add a type to this set, calling any constraint handlers if this is a new
* possible type.
@ -316,6 +330,17 @@ class TypeSet
inline unsigned getObjectCount();
inline TypeObject *getObject(unsigned i);
void setIntermediate() { typeFlags |= TYPE_FLAG_INTERMEDIATE_SET; }
void setOwnProperty(bool configurable) {
typeFlags |= TYPE_FLAG_OWN_PROPERTY;
if (configurable)
typeFlags |= TYPE_FLAG_CONFIGURED_PROPERTY;
}
void setDefinite(unsigned slot) {
JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
typeFlags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
}
/* Add specific kinds of constraints to this set. */
inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
void addSubset(JSContext *cx, JSScript *script, TypeSet *target);
@ -333,6 +358,7 @@ class TypeSet
void addMonitorRead(JSContext *cx, JSScript *script, TypeSet *target);
void addBaseSubset(JSContext *cx, TypeObject *object, TypeSet *target);
void addBaseClearDefinite(JSContext *cx, TypeObject *object);
void addCondensed(JSContext *cx, JSScript *script);
/*
@ -477,6 +503,15 @@ struct TypeObject
/* Mark bit for GC. */
bool marked;
/*
* If non-NULL, objects of this type have always been constructed using
* 'new' on the specified script. Moreover the given finalize kind and
* initial shape should also be used for the object.
*/
JSScript *newScript;
/* gc::FinalizeKind */ unsigned newScriptFinalizeKind;
Shape *newScriptShape;
/*
* Whether this is an Object or Array keyed to an offset in the script containing
* this in its objects list.
@ -564,9 +599,11 @@ struct TypeObject
/* Helpers */
bool addProperty(JSContext *cx, jsid id, Property **pprop);
bool addDefiniteProperties(JSContext *cx, JSObject *obj, bool clearUnknown);
void addPrototype(JSContext *cx, TypeObject *proto);
void setFlags(JSContext *cx, TypeObjectFlags flags);
void markUnknown(JSContext *cx);
void clearNewScript(JSContext *cx);
void storeToInstances(JSContext *cx, Property *base);
void getFromPrototypes(JSContext *cx, Property *base);

View File

@ -1094,7 +1094,7 @@ TypeSet::destroy(JSContext *cx)
::js_free(objectSet);
while (constraintList) {
TypeConstraint *next = constraintList->next;
if (constraintList->condensed() || constraintList->baseSubset())
if (constraintList->condensed() || constraintList->persistentObject())
::js_free(constraintList);
constraintList = next;
}
@ -1169,7 +1169,7 @@ TypeSet::addType(JSContext *cx, jstype type)
/* Propagate the type to all constraints. */
TypeConstraint *constraint = constraintList;
while (constraint) {
JS_ASSERT_IF(!constraint->baseSubset(),
JS_ASSERT_IF(!constraint->persistentObject(),
constraint->script->compartment == cx->compartment);
cx->compartment->types.addPending(cx, constraint, this, type);
constraint = constraint->next;
@ -1191,7 +1191,7 @@ TypeSet::setOwnProperty(JSContext *cx, bool configured)
/* Propagate the change to all constraints. */
TypeConstraint *constraint = constraintList;
while (constraint) {
JS_ASSERT_IF(!constraint->baseSubset(),
JS_ASSERT_IF(!constraint->persistentObject(),
constraint->script->compartment == cx->compartment);
constraint->newPropertyState(cx, this);
constraint = constraint->next;
@ -1368,6 +1368,7 @@ TypeObject::name()
inline TypeObject::TypeObject(jsid name, JSObject *proto)
: proto(proto), emptyShapes(NULL),
flags(0), isFunction(false), marked(false),
newScript(NULL), newScriptFinalizeKind(0), newScriptShape(NULL),
initializerObject(false), initializerArray(false), initializerOffset(0),
contribution(0), propertySet(NULL), propertyCount(0),
instanceList(NULL), instanceNext(NULL), next(NULL),
@ -1411,6 +1412,23 @@ SweepClonedTypes(ClonedTypeSet *types)
}
}
class AutoTypeRooter : private AutoGCRooter {
public:
AutoTypeRooter(JSContext *cx, TypeObject *type
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoGCRooter(cx, TYPE), type(type)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
friend void AutoGCRooter::trace(JSTracer *trc);
friend void MarkRuntime(JSTracer *trc);
private:
TypeObject *type;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} } /* namespace js::types */
#endif // jsinferinlines_h___

View File

@ -2892,6 +2892,41 @@ js_Object(JSContext *cx, uintN argc, Value *vp)
return JS_TRUE;
}
JSObject *
js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent,
gc::FinalizeKind kind, const Shape *shape)
{
JSObject *res = NewObjectWithType(cx, type, parent, kind);
if (!res)
return NULL;
if (JSID_IS_EMPTY(shape->id))
return res;
/* Get all the ids in the object, in order. */
js::AutoIdVector ids(cx);
for (unsigned i = 0; i <= shape->slot; i++) {
if (!ids.append(JSID_VOID))
return NULL;
}
const js::Shape *nshape = shape;
while (!JSID_IS_EMPTY(nshape->id)) {
ids[nshape->slot] = nshape->id;
nshape = nshape->previous();
}
/* Construct the new shape. */
for (unsigned i = 0; i < ids.length(); i++) {
if (!js_DefineNativeProperty(cx, res, ids[i], js::UndefinedValue(), NULL, NULL,
JSPROP_ENUMERATE, 0, 0, NULL, 0)) {
return NULL;
}
}
JS_ASSERT(!res->inDictionaryMode());
return res;
}
JSObject*
js_CreateThis(JSContext *cx, JSObject *callee)
{
@ -2920,7 +2955,20 @@ js_CreateThis(JSContext *cx, JSObject *callee)
JSObject *
js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
{
/* Caller must ensure that proto's new type is not marked as an array. */
if (proto) {
JSScript *calleeScript = callee->getFunctionPrivate()->script();
types::TypeObject *type = proto->getNewType(cx, calleeScript);
if (type && type->newScript) {
JS_ASSERT(type->newScript == calleeScript);
gc::FinalizeKind kind = gc::FinalizeKind(type->newScriptFinalizeKind);
JSObject *res = NewObjectWithType(cx, type, callee->getParent(), kind);
if (res)
res->setMap(type->newScriptShape);
return res;
}
}
gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
return NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, callee->getParent(), kind);
}
@ -2935,23 +2983,34 @@ js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType)
return NULL;
}
JSObject *proto;
if (protov.isObject()) {
if (protov.isObject())
proto = &protov.toObject();
TypeObject *type = proto->getNewType(cx);
if (!type)
return NULL;
} else {
else
proto = NULL;
}
JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
if (obj && newType) {
/*
* Make a new type and a new object with the type, reshaped according
* to any properties already added by CreateThisForFunctionWithProto.
*/
JS_ASSERT(cx->typeInferenceEnabled());
types::TypeObject *type = cx->newTypeObject("SpecializedThis", obj->getProto());
if (!type || !obj->setTypeAndUniqueShape(cx, type))
static unsigned count = 0;
char *name = (char *) alloca(30);
JS_snprintf(name, 30, "SpecializedThis:%u", ++count);
types::TypeObject *type = cx->newTypeObject(name, obj->getProto());
types::AutoTypeRooter root(cx, type);
obj = NewReshapedObject(cx, type, obj->getParent(), gc::FinalizeKind(obj->finalizeKind()),
(const Shape *) obj->lastProperty());
if (!obj)
return NULL;
if (!callee->getFunctionPrivate()->script()->typeSetThis(cx, (types::jstype) type))
return NULL;
}
return obj;
}
@ -3038,9 +3097,6 @@ js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot)
const Value &protov = ctor->getSlotRef(protoSlot);
if (protov.isObject()) {
proto = &protov.toObject();
TypeObject *type = proto->getNewType(cx);
if (!type)
return NULL;
} else {
/*
* GetInterpretedFunctionPrototype found that ctor.prototype is

View File

@ -654,9 +654,10 @@ struct JSObject : js::gc::Cell {
/* Index into the dynamic slots array to use for a dynamic slot. */
inline size_t dynamicSlotIndex(size_t slot);
inline size_t numFixedSlots() const;
private:
inline js::Value* fixedSlots() const;
inline size_t numFixedSlots() const;
inline bool hasSlotsArray() const;
public:
@ -789,8 +790,8 @@ struct JSObject : js::gc::Cell {
inline bool setTypeAndEmptyShape(JSContext *cx, js::types::TypeObject *newType);
inline void setTypeAndShape(js::types::TypeObject *newType, const js::Shape *newShape);
inline js::types::TypeObject *getNewType(JSContext *cx);
void makeNewType(JSContext *cx);
inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL);
void makeNewType(JSContext *cx, JSScript *script);
JSObject * getProto() const {
return type->proto;

View File

@ -858,12 +858,16 @@ JSObject::setWithThis(JSObject *thisp)
}
inline js::types::TypeObject *
JSObject::getNewType(JSContext *cx)
JSObject::getNewType(JSContext *cx, JSScript *script)
{
if (isDenseArray() && !makeDenseArraySlow(cx))
return NULL;
if (!newType)
makeNewType(cx);
if (newType) {
if (newType->newScript != script && newType->newScript)
newType->clearNewScript(cx);
} else {
makeNewType(cx, script);
}
return newType;
}
@ -1370,6 +1374,39 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
return NewObject<withProto>(cx, clasp, proto, parent, kind);
}
/*
* Create a plain object with the specified type. This bypasses getNewType to
* avoid losing creation site information for objects made by scripted 'new'.
*/
static JS_ALWAYS_INLINE JSObject *
NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::FinalizeKind kind)
{
JSObject* obj = js_NewGCObject(cx, kind);
if (!obj)
goto out;
/*
* Default parent to the parent of the prototype, which was set from
* the parent of the prototype's constructor.
*/
obj->init(cx, &js_ObjectClass, type,
(!parent && type->proto) ? type->proto->getParent() : parent,
NULL, false);
if (!InitScopeForObject(cx, obj, &js_ObjectClass, type, kind)) {
obj = NULL;
goto out;
}
out:
Probes::createObject(cx, obj);
return obj;
}
extern JSObject *
NewReshapedObject(JSContext *cx, types::TypeObject *type, JSObject *parent,
gc::FinalizeKind kind, const Shape *shape);
/*
* As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
* the object, zero if the final size is unknown. This should only be used for

View File

@ -1922,6 +1922,8 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_GETTHISPROP)
/* Push thisv onto stack. */
jsop_this();
if (cx->typeInferenceEnabled())
frame.extra(frame.peek(-1)).types = script->thisTypes();
if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)), knownPushedType(0)))
return Compile_Error;
END_CASE(JSOP_GETTHISPROP);
@ -1931,6 +1933,8 @@ mjit::Compiler::generateMethod()
/* Push arg onto stack. */
uint32 arg = GET_SLOTNO(PC);
frame.pushArg(arg, knownArgumentType(arg));
if (cx->typeInferenceEnabled())
frame.extra(frame.peek(-1)).types = script->argTypes(arg);
if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[ARGNO_LEN])), knownPushedType(0)))
return Compile_Error;
}
@ -1940,6 +1944,8 @@ mjit::Compiler::generateMethod()
{
uint32 local = GET_SLOTNO(PC);
frame.pushLocal(local, knownLocalType(local));
if (cx->typeInferenceEnabled() && local < script->nfixed)
frame.extra(frame.peek(-1)).types = script->localTypes(local);
if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[SLOTNO_LEN])), knownPushedType(0)))
return Compile_Error;
}
@ -2380,14 +2386,22 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_BINDNAME)
BEGIN_CASE(JSOP_SETPROP)
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true))
{
jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true, pop))
return Compile_Error;
}
END_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETMETHOD)
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true))
{
jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true, pop))
return Compile_Error;
}
END_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_THROW)
@ -4362,6 +4376,39 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType,
frame.forgetMismatchedObject(top);
/*
* Check if we are accessing a known type which always has the property
* in a particular inline slot. Get the property directly in this case,
* without using an IC.
*/
JSOp op = JSOp(*PC);
types::TypeSet *types = frame.extra(top).types;
if ((op == JSOP_GETPROP || op == JSOP_GETTHISPROP || op == JSOP_GETARGPROP || op == JSOP_GETLOCALPROP) &&
types && !types->unknown() && types->getObjectCount() == 1 &&
!types->getObject(0)->unknownProperties()) {
JS_ASSERT(usePropCache);
types::TypeObject *object = types->getObject(0);
types::TypeSet *propertyTypes = object->getProperty(cx, ATOM_TO_JSID(atom), false);
if (!propertyTypes)
return false;
if (propertyTypes->isDefiniteProperty() && !propertyTypes->isOwnProperty(cx, true)) {
types->addFreeze(cx);
uint32 slot = propertyTypes->definiteSlot();
if (!top->isTypeKnown()) {
Jump notObject = frame.testObject(Assembler::NotEqual, top);
stubcc.linkExit(notObject, Uses(1));
stubcc.leave();
OOL_STUBCALL(stubs::GetProp);
}
RegisterID reg = frame.tempRegForData(top);
frame.pop();
frame.push(Address(reg, JSObject::getFixedSlotOffset(slot)), knownType);
if (!top->isTypeKnown())
stubcc.rejoin(Changes(1));
return true;
}
}
/*
* These two must be loaded first. The objReg because the string path
* wants to read it, and the shapeReg because it could cause a spill that
@ -4867,7 +4914,7 @@ mjit::Compiler::jsop_callprop(JSAtom *atom)
}
bool
mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed)
{
REJOIN_SITE_2(usePropCache
? STRICT_VARIANT(stubs::SetName)
@ -4883,6 +4930,38 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
return true;
}
/*
* Set the property directly if we are accessing a known object which
* always has the property in a particular inline slot.
*/
types::TypeSet *types = frame.extra(lhs).types;
if (JSOp(*PC) == JSOP_SETPROP && types &&
!types->unknown() && types->getObjectCount() == 1 &&
!types->getObject(0)->unknownProperties()) {
JS_ASSERT(usePropCache);
types::TypeObject *object = types->getObject(0);
types::TypeSet *propertyTypes = object->getProperty(cx, ATOM_TO_JSID(atom), false);
if (!propertyTypes)
return false;
if (propertyTypes->isDefiniteProperty() && !propertyTypes->isOwnProperty(cx, true)) {
types->addFreeze(cx);
uint32 slot = propertyTypes->definiteSlot();
if (!lhs->isTypeKnown()) {
Jump notObject = frame.testObject(Assembler::NotEqual, lhs);
stubcc.linkExit(notObject, Uses(2));
stubcc.leave();
masm.move(ImmPtr(atom), Registers::ArgReg1);
OOL_STUBCALL(STRICT_VARIANT(stubs::SetName));
}
RegisterID reg = frame.tempRegForData(lhs);
frame.storeTo(rhs, Address(reg, JSObject::getFixedSlotOffset(slot)), popGuaranteed);
frame.shimmy(1);
if (!lhs->isTypeKnown())
stubcc.rejoin(Changes(1));
return true;
}
}
JSOp op = JSOp(*PC);
ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
@ -5410,7 +5489,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
return Compile_Retry;
// OBJ N+1
if (!jsop_setprop(atom, false))
if (!jsop_setprop(atom, false, pop))
return Compile_Error;
// N+1
@ -5439,7 +5518,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
return Compile_Retry;
// N OBJ N+1
if (!jsop_setprop(atom, false))
if (!jsop_setprop(atom, false, true))
return Compile_Error;
// N N+1
@ -5499,7 +5578,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.shimmy(1);
// OBJ V+1
if (!jsop_setprop(atom, false))
if (!jsop_setprop(atom, false, pop))
return Compile_Error;
// V+1
@ -5535,7 +5614,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.dupAt(-2);
// OBJ N N+1 OBJ N+1
if (!jsop_setprop(atom, false))
if (!jsop_setprop(atom, false, true))
return Compile_Error;
// OBJ N N+1 N+1
@ -6883,7 +6962,7 @@ mjit::Compiler::jsop_forprop(JSAtom *atom)
// Before: ITER OBJ VALUE
// After: ITER VALUE
jsop_setprop(atom, false);
jsop_setprop(atom, false, true);
// Before: ITER VALUE
// After: ITER

View File

@ -690,7 +690,7 @@ class Compiler : public BaseCompiler
bool jsop_getprop(JSAtom *atom, JSValueType type,
bool typeCheck = true, bool usePropCache = true);
bool jsop_length();
bool jsop_setprop(JSAtom *atom, bool usePropCache = true);
bool jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed);
void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true);
bool jsop_callprop_slow(JSAtom *atom);
bool jsop_callprop(JSAtom *atom);

View File

@ -320,8 +320,11 @@ FrameState::push(Address address, JSValueType knownType, bool reuseBase)
// Prevent us from clobbering this reg.
bool free = a->freeRegs.hasReg(address.base);
bool needsPin = !free && regstate(address.base).fe();
if (free)
a->freeRegs.takeReg(address.base);
if (needsPin)
pinReg(address.base);
RegisterID typeReg = allocReg();
@ -332,6 +335,8 @@ FrameState::push(Address address, JSValueType knownType, bool reuseBase)
// writes to the register.
if (free)
a->freeRegs.putReg(address.base);
if (needsPin)
unpinReg(address.base);
RegisterID dataReg = reuseBase ? address.base : allocReg();
masm.loadPayload(address, dataReg);