mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Direct instance property accesses, bug 649376.
This commit is contained in:
parent
793bec3ed6
commit
21fd2d8254
29
js/src/jit-test/tests/jaeger/propertyOptimize-1.js
Normal file
29
js/src/jit-test/tests/jaeger/propertyOptimize-1.js
Normal 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"));
|
16
js/src/jit-test/tests/jaeger/propertyOptimize-2.js
Normal file
16
js/src/jit-test/tests/jaeger/propertyOptimize-2.js
Normal 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);
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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,17 +4514,50 @@ JSObject::makeNewType(JSContext *cx)
|
||||
if (!type)
|
||||
return;
|
||||
|
||||
if (cx->typeInferenceEnabled() && !getType()->unknownProperties()) {
|
||||
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 (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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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___
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user