mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
[INFER] Compute types for singleton/JSON arrays and objects, bug 639263.
This commit is contained in:
parent
af409eac97
commit
9bdadcc044
12
js/src/jit-test/tests/basic/singleton.js
Normal file
12
js/src/jit-test/tests/basic/singleton.js
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
var a = [1,2,3,4];
|
||||
var b = [{a:0,b:1},{a:0,b:1},{a:0,b:1}];
|
||||
var c = {a:0,b:4.5};
|
||||
var d = [1,2,3,true];
|
||||
var e = {a:0,b:1,c:2};
|
||||
var f = {a:0,b:1,c:true};
|
||||
|
||||
var w = JSON.parse('[1,2,3,4]');
|
||||
var x = JSON.parse('{"a":0,"b":true,"c":4.5}');
|
||||
var y = JSON.parse('{"d":true,"b":true,"c":4.5}');
|
||||
var z = JSON.parse('[{"a":0,"b":1},{"a":0,"b":1},{"a":0,"b":1}]');
|
@ -1062,7 +1062,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
JSCompartment **compartment = rt->compartments.begin();
|
||||
JSCompartment **end = rt->compartments.end();
|
||||
while (compartment < end) {
|
||||
(*compartment)->types.finish(cx, *compartment);
|
||||
(*compartment)->types.print(cx, *compartment);
|
||||
compartment++;
|
||||
}
|
||||
|
||||
|
@ -2185,6 +2185,14 @@ public:
|
||||
|
||||
/* Monitor all properties of a type object as unknown. */
|
||||
inline bool markTypeObjectUnknownProperties(js::types::TypeObject *obj);
|
||||
|
||||
/*
|
||||
* For an array or object which has not yet escaped and been referenced elsewhere,
|
||||
* pick a new type based on the object's current contents.
|
||||
*/
|
||||
inline bool fixArrayType(JSObject *obj);
|
||||
inline bool fixObjectType(JSObject *obj);
|
||||
|
||||
}; /* struct JSContext */
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
@ -4453,6 +4453,8 @@ JSParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp)
|
||||
}
|
||||
JS_ASSERT(idx == pn_count);
|
||||
|
||||
if (!cx->fixArrayType(obj))
|
||||
return false;
|
||||
vp->setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
@ -4492,6 +4494,8 @@ JSParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!cx->fixObjectType(obj))
|
||||
return false;
|
||||
vp->setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
@ -6840,7 +6844,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
}
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
if (!cg->hasSharps() && !(pn->pn_xflags & PNX_NONCONST) && cg->checkSingletonContext()) {
|
||||
if (!cg->hasSharps() && !(pn->pn_xflags & PNX_NONCONST) && pn->pn_head &&
|
||||
cg->checkSingletonContext()) {
|
||||
if (!EmitSingletonInitialiser(cx, cg, pn))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
@ -6900,7 +6905,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!cg->hasSharps() && !(pn->pn_xflags & PNX_NONCONST) && cg->checkSingletonContext()) {
|
||||
if (!cg->hasSharps() && !(pn->pn_xflags & PNX_NONCONST) && pn->pn_head &&
|
||||
cg->checkSingletonContext()) {
|
||||
if (!EmitSingletonInitialiser(cx, cg, pn))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
@ -284,7 +284,7 @@ void TypeFailure(JSContext *cx, const char *fmt, ...)
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
|
||||
cx->compartment->types.finish(cx, cx->compartment);
|
||||
cx->compartment->types.print(cx, cx->compartment);
|
||||
|
||||
fflush(stderr);
|
||||
*((int*)NULL) = 0; /* Type warnings */
|
||||
@ -1559,6 +1559,18 @@ TypeCompartment::init(JSContext *cx)
|
||||
JS_InitArenaPool(&pool, "typeinfer", 512, 8, NULL);
|
||||
}
|
||||
|
||||
TypeCompartment::~TypeCompartment()
|
||||
{
|
||||
if (pendingArray)
|
||||
js_free(pendingArray);
|
||||
|
||||
if (arrayTypeTable)
|
||||
js_delete<ArrayTypeTable>(arrayTypeTable);
|
||||
|
||||
if (objectTypeTable)
|
||||
js_delete<ObjectTypeTable>(objectTypeTable);
|
||||
}
|
||||
|
||||
TypeObject *
|
||||
TypeCompartment::newTypeObject(JSContext *cx, JSScript *script, const char *name,
|
||||
bool isFunction, JSObject *proto)
|
||||
@ -1660,14 +1672,14 @@ void
|
||||
TypeCompartment::growPendingArray(JSContext *cx)
|
||||
{
|
||||
unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
|
||||
PendingWork *newArray = (PendingWork *) cx->calloc(newCapacity * sizeof(PendingWork));
|
||||
PendingWork *newArray = (PendingWork *) js_calloc(newCapacity * sizeof(PendingWork));
|
||||
if (!newArray) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(newArray, pendingArray, pendingCount * sizeof(PendingWork));
|
||||
cx->free(pendingArray);
|
||||
js_free(pendingArray);
|
||||
|
||||
pendingArray = newArray;
|
||||
pendingCapacity = newCapacity;
|
||||
@ -2010,13 +2022,10 @@ TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::finish(JSContext *cx, JSCompartment *compartment)
|
||||
TypeCompartment::print(JSContext *cx, JSCompartment *compartment)
|
||||
{
|
||||
JS_ASSERT(this == &compartment->types);
|
||||
|
||||
if (pendingArray)
|
||||
cx->free(pendingArray);
|
||||
|
||||
if (!InferSpewActive(ISpewResult) || JS_CLIST_IS_EMPTY(&compartment->scripts))
|
||||
return;
|
||||
|
||||
@ -2024,7 +2033,7 @@ TypeCompartment::finish(JSContext *cx, JSCompartment *compartment)
|
||||
&script->links != &compartment->scripts;
|
||||
script = (JSScript *)script->links.next) {
|
||||
if (script->types)
|
||||
script->types->finish(cx, script);
|
||||
script->types->print(cx, script);
|
||||
TypeObject *object = script->typeObjects;
|
||||
while (object) {
|
||||
object->print(cx);
|
||||
@ -2054,6 +2063,270 @@ TypeCompartment::finish(JSContext *cx, JSCompartment *compartment)
|
||||
printf("Time: %.2f ms\n", millis);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// TypeCompartment tables
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* The arrayTypeTable and objectTypeTable are per-compartment tables for making
|
||||
* common type objects to model the contents of large script singletons and
|
||||
* JSON objects. These are vanilla Arrays and native Objects, so we distinguish
|
||||
* the types of different ones by looking at the types of their properties.
|
||||
*
|
||||
* All singleton/JSON arrays which have the same prototype, are homogenous and
|
||||
* of the same type will share a type object. All singleton/JSON objects which
|
||||
* have the same shape and property types will also share a type object. We
|
||||
* don't try to collate arrays or objects that have type mismatches.
|
||||
*/
|
||||
|
||||
static inline bool
|
||||
NumberTypes(jstype a, jstype b)
|
||||
{
|
||||
return (a == TYPE_INT32 || a == TYPE_DOUBLE) && (b == TYPE_INT32 || b == TYPE_DOUBLE);
|
||||
}
|
||||
|
||||
struct ArrayTableKey
|
||||
{
|
||||
jstype type;
|
||||
JSObject *proto;
|
||||
|
||||
typedef ArrayTableKey Lookup;
|
||||
|
||||
static inline uint32 hash(const ArrayTableKey &v) {
|
||||
return (uint32) (v.type ^ ((uint32)v.proto >> 2));
|
||||
}
|
||||
|
||||
static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) {
|
||||
return v1.type == v2.type && v1.proto == v2.proto;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (!arrayTypeTable) {
|
||||
arrayTypeTable = js_new<ArrayTypeTable>(cx);
|
||||
if (!arrayTypeTable || !arrayTypeTable->init()) {
|
||||
arrayTypeTable = NULL;
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the array is of homogenous type, pick a type object which will be
|
||||
* shared with all other singleton/JSON arrays of the same type.
|
||||
* If the array is heterogenous, keep the existing type object, which has
|
||||
* unknown properties.
|
||||
*/
|
||||
JS_ASSERT(obj->isPackedDenseArray());
|
||||
|
||||
unsigned len = obj->getDenseArrayInitializedLength();
|
||||
if (len == 0)
|
||||
return true;
|
||||
|
||||
jstype type = GetValueType(cx, obj->getDenseArrayElement(0));
|
||||
|
||||
for (unsigned i = 1; i < len; i++) {
|
||||
jstype ntype = GetValueType(cx, obj->getDenseArrayElement(i));
|
||||
if (ntype != type) {
|
||||
if (NumberTypes(type, ntype))
|
||||
type = TYPE_DOUBLE;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayTableKey key;
|
||||
key.type = type;
|
||||
key.proto = obj->getProto();
|
||||
ArrayTypeTable::AddPtr p = arrayTypeTable->lookupForAdd(key);
|
||||
|
||||
if (p) {
|
||||
obj->setType(p->value);
|
||||
} else {
|
||||
TypeObject *objType = newTypeObject(cx, NULL, "TableArray", false, obj->getProto());
|
||||
if (!objType) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
obj->setType(objType);
|
||||
|
||||
if (!cx->addTypePropertyId(objType, JSID_VOID, type))
|
||||
return false;
|
||||
|
||||
if (!arrayTypeTable->relookupOrAdd(p, key, objType)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* N.B. We could also use the initial shape of the object (before its type is
|
||||
* fixed) as the key in the object table, but since all references in the table
|
||||
* are weak the hash entries would usually be collected on GC even if objects
|
||||
* with the new type/shape are still live.
|
||||
*/
|
||||
struct ObjectTableKey
|
||||
{
|
||||
jsid *ids;
|
||||
uint32 nslots;
|
||||
JSObject *proto;
|
||||
|
||||
typedef JSObject * Lookup;
|
||||
|
||||
static inline uint32 hash(JSObject *obj) {
|
||||
return (uint32) (JSID_BITS(obj->lastProperty()->id) ^
|
||||
obj->slotSpan() ^
|
||||
((uint32)obj->getProto() >> 2));
|
||||
}
|
||||
|
||||
static inline bool match(const ObjectTableKey &v, JSObject *obj) {
|
||||
if (obj->slotSpan() != v.nslots || obj->getProto() != v.proto)
|
||||
return false;
|
||||
const Shape *shape = obj->lastProperty();
|
||||
while (!JSID_IS_EMPTY(shape->id)) {
|
||||
if (shape->id != v.ids[shape->slot])
|
||||
return false;
|
||||
shape = shape->previous();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjectTableEntry
|
||||
{
|
||||
TypeObject *object;
|
||||
Shape *newShape;
|
||||
jstype *types;
|
||||
};
|
||||
|
||||
bool
|
||||
TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (!objectTypeTable) {
|
||||
objectTypeTable = js_new<ObjectTypeTable>(cx);
|
||||
if (!objectTypeTable || !objectTypeTable->init()) {
|
||||
objectTypeTable = NULL;
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the same type object for all singleton/JSON arrays with the same
|
||||
* base shape, i.e. the same fields written in the same order. If there
|
||||
* is a type mismatch with previous objects of the same shape, use the
|
||||
* generic unknown type.
|
||||
*/
|
||||
JS_ASSERT(obj->isObject());
|
||||
|
||||
if (obj->slotSpan() == 0 || obj->inDictionaryMode())
|
||||
return true;
|
||||
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj);
|
||||
const Shape *baseShape = obj->lastProperty();
|
||||
|
||||
if (p) {
|
||||
/* The lookup ensures the shape matches, now check that the types match. */
|
||||
jstype *types = p->value.types;
|
||||
for (unsigned i = 0; i < obj->slotSpan(); i++) {
|
||||
jstype ntype = GetValueType(cx, obj->getSlot(i));
|
||||
if (ntype != types[i]) {
|
||||
if (NumberTypes(ntype, types[i])) {
|
||||
if (types[i] == TYPE_INT32) {
|
||||
types[i] = TYPE_DOUBLE;
|
||||
const Shape *shape = baseShape;
|
||||
while (!JSID_IS_EMPTY(shape->id)) {
|
||||
if (shape->slot == i) {
|
||||
if (!cx->addTypePropertyId(p->value.object, shape->id, TYPE_DOUBLE))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
shape = shape->previous();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj->setTypeAndShape(p->value.object, p->value.newShape);
|
||||
} else {
|
||||
/*
|
||||
* Make a new type to use, and regenerate a new shape to go with it.
|
||||
* Shapes are rooted at the empty shape for the object's type, so we
|
||||
* can't change the type without changing the shape.
|
||||
*/
|
||||
JSObject *xobj = NewBuiltinClassInstance(cx, &js_ObjectClass,
|
||||
(gc::FinalizeKind) obj->finalizeKind());
|
||||
if (!xobj) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
AutoObjectRooter xvr(cx, xobj);
|
||||
|
||||
TypeObject *objType = newTypeObject(cx, NULL, "TableObject", false, obj->getProto());
|
||||
if (!objType) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
xobj->setType(objType);
|
||||
|
||||
jsid *ids = (jsid *) cx->calloc(obj->slotSpan() * sizeof(jsid));
|
||||
if (!ids)
|
||||
return false;
|
||||
|
||||
jstype *types = (jstype *) cx->calloc(obj->slotSpan() * sizeof(jstype));
|
||||
if (!types)
|
||||
return false;
|
||||
|
||||
const Shape *shape = baseShape;
|
||||
while (!JSID_IS_EMPTY(shape->id)) {
|
||||
ids[shape->slot] = shape->id;
|
||||
types[shape->slot] = GetValueType(cx, obj->getSlot(shape->slot));
|
||||
if (!cx->addTypePropertyId(objType, shape->id, types[shape->slot]))
|
||||
return false;
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
/* Construct the new shape. */
|
||||
for (unsigned i = 0; i < obj->slotSpan(); i++) {
|
||||
if (!js_DefineNativeProperty(cx, xobj, ids[i], UndefinedValue(), NULL, NULL,
|
||||
JSPROP_ENUMERATE, 0, 0, NULL, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
JS_ASSERT(!xobj->inDictionaryMode());
|
||||
const Shape *newShape = xobj->lastProperty();
|
||||
|
||||
ObjectTableKey key;
|
||||
key.ids = ids;
|
||||
key.nslots = obj->slotSpan();
|
||||
key.proto = obj->getProto();
|
||||
JS_ASSERT(ObjectTableKey::match(key, obj));
|
||||
|
||||
ObjectTableEntry entry;
|
||||
entry.object = objType;
|
||||
entry.newShape = (Shape *) newShape;
|
||||
entry.types = types;
|
||||
|
||||
p = objectTypeTable->lookupForAdd(obj);
|
||||
if (!objectTypeTable->add(p, key, entry)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
obj->setTypeAndShape(objType, newShape);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// TypeObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -3450,7 +3723,7 @@ PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
#endif
|
||||
|
||||
void
|
||||
TypeScript::finish(JSContext *cx, JSScript *script)
|
||||
TypeScript::print(JSContext *cx, JSScript *script)
|
||||
{
|
||||
TypeCompartment *compartment = &script->compartment->types;
|
||||
|
||||
@ -4076,6 +4349,52 @@ TypeCompartment::sweep(JSContext *cx)
|
||||
typeEmpty.emptyShapes = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate through the array/object type tables and remove all entries
|
||||
* referencing collected data. These tables only hold weak references.
|
||||
*/
|
||||
|
||||
if (arrayTypeTable) {
|
||||
for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
|
||||
const ArrayTableKey &key = e.front().key;
|
||||
TypeObject *obj = e.front().value;
|
||||
JS_ASSERT(obj->proto == key.proto);
|
||||
|
||||
bool remove = false;
|
||||
if (TypeIsObject(key.type) && !((TypeObject *)key.type)->marked)
|
||||
remove = true;
|
||||
if (!obj->marked)
|
||||
remove = true;
|
||||
|
||||
if (remove)
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
|
||||
if (objectTypeTable) {
|
||||
for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
|
||||
const ObjectTableKey &key = e.front().key;
|
||||
const ObjectTableEntry &entry = e.front().value;
|
||||
JS_ASSERT(entry.object->proto == key.proto);
|
||||
|
||||
bool remove = false;
|
||||
if (!entry.object->marked || !entry.newShape->marked())
|
||||
remove = true;
|
||||
for (unsigned i = 0; !remove && i < key.nslots; i++) {
|
||||
if (JSID_IS_STRING(key.ids[i]) && !JSID_TO_STRING(key.ids[i])->asCell()->isMarked())
|
||||
remove = true;
|
||||
if (TypeIsObject(entry.types[i]) && !((TypeObject *)entry.types[i])->marked)
|
||||
remove = true;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
cx->free(key.ids);
|
||||
cx->free(entry.types);
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SweepTypeObjectList(cx, objects);
|
||||
}
|
||||
|
||||
|
@ -556,7 +556,7 @@ struct TypeScript
|
||||
TypeSet **pushedArray;
|
||||
|
||||
/* Gather statistics off this script and print it if necessary. */
|
||||
void finish(JSContext *cx, JSScript *script);
|
||||
void print(JSContext *cx, JSScript *script);
|
||||
|
||||
inline bool monitored(uint32 offset);
|
||||
inline void setMonitored(uint32 offset);
|
||||
@ -573,6 +573,13 @@ void AnalyzeScriptTypes(JSContext *cx, JSScript *script);
|
||||
/* Analyze the effect of invoking 'new' on script. */
|
||||
void AnalyzeScriptNew(JSContext *cx, JSScript *script);
|
||||
|
||||
struct ArrayTableKey;
|
||||
typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey> ArrayTypeTable;
|
||||
|
||||
struct ObjectTableKey;
|
||||
struct ObjectTableEntry;
|
||||
typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey> ObjectTypeTable;
|
||||
|
||||
/* Type information for a compartment. */
|
||||
struct TypeCompartment
|
||||
{
|
||||
@ -611,6 +618,14 @@ struct TypeCompartment
|
||||
/* Pending recompilations to perform before execution of JIT code can resume. */
|
||||
Vector<JSScript*> *pendingRecompiles;
|
||||
|
||||
/* Tables for determining types of singleton/JSON objects. */
|
||||
|
||||
ArrayTypeTable *arrayTypeTable;
|
||||
ObjectTypeTable *objectTypeTable;
|
||||
|
||||
bool fixArrayType(JSContext *cx, JSObject *obj);
|
||||
bool fixObjectType(JSContext *cx, JSObject *obj);
|
||||
|
||||
/* Constraint solving worklist structures. */
|
||||
|
||||
/* A type that needs to be registered with a constraint. */
|
||||
@ -649,6 +664,7 @@ struct TypeCompartment
|
||||
unsigned recompilations;
|
||||
|
||||
void init(JSContext *cx);
|
||||
~TypeCompartment();
|
||||
|
||||
uint64 currentTime()
|
||||
{
|
||||
@ -670,7 +686,7 @@ struct TypeCompartment
|
||||
inline void resolvePending(JSContext *cx);
|
||||
|
||||
/* Prints results of this compartment if spew is enabled, checks for warnings. */
|
||||
void finish(JSContext *cx, JSCompartment *compartment);
|
||||
void print(JSContext *cx, JSCompartment *compartment);
|
||||
|
||||
/* Make a function or non-function object associated with an optional script. */
|
||||
TypeObject *newTypeObject(JSContext *cx, JSScript *script,
|
||||
|
@ -369,6 +369,18 @@ JSContext::typeMonitorCall(const js::CallArgs &args, bool constructing)
|
||||
return compartment->types.dynamicCall(this, callee, args, constructing);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSContext::fixArrayType(JSObject *obj)
|
||||
{
|
||||
return !typeInferenceEnabled() || compartment->types.fixArrayType(this, obj);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSContext::fixObjectType(JSObject *obj)
|
||||
{
|
||||
return !typeInferenceEnabled() || compartment->types.fixObjectType(this, obj);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// JSScript
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -714,6 +714,7 @@ struct JSObject : js::gc::Cell {
|
||||
|
||||
inline bool clearType(JSContext *cx);
|
||||
inline void setType(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);
|
||||
|
@ -760,6 +760,14 @@ JSObject::setType(js::types::TypeObject *newType)
|
||||
type = newType;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setTypeAndShape(js::types::TypeObject *newType, const js::Shape *newShape)
|
||||
{
|
||||
JS_ASSERT(newShape->slot == lastProperty()->slot);
|
||||
setType(newType);
|
||||
setLastProperty(newShape);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::init(JSContext *cx, js::Class *aclasp, js::types::TypeObject *type,
|
||||
JSObject *parent, void *priv, bool useHoles)
|
||||
|
@ -900,6 +900,15 @@ CloseObject(JSContext *cx, JSONParser *jp)
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
|
||||
Value p;
|
||||
if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &p))
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *obj = &p.toObject();
|
||||
if (obj->isArray() ? !cx->fixArrayType(obj) : !cx->fixObjectType(obj))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!js_SetLengthProperty(cx, jp->objectStack, len - 1))
|
||||
return JS_FALSE;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user