зеркало из https://github.com/mozilla/gecko-dev.git
Back out 7d1510d544d9 (bug 836968) on suspicion of causing bug 850547
CLOSED TREE
This commit is contained in:
Родитель
1c6d64ac73
Коммит
6a5097a65f
|
@ -12,7 +12,6 @@
|
|||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsonparser.h"
|
||||
#include "jsprf.h"
|
||||
#include "jswatchpoint.h"
|
||||
|
||||
|
@ -621,10 +620,6 @@ AutoGCRooter::trace(JSTracer *trc)
|
|||
MarkValueUnbarriered(trc, &p->get(), "js::AutoWrapperVector.vector");
|
||||
return;
|
||||
}
|
||||
|
||||
case JSONPARSER:
|
||||
static_cast<js::JSONParser *>(this)->trace(trc);
|
||||
return;
|
||||
}
|
||||
|
||||
JS_ASSERT(tag_ >= 0);
|
||||
|
|
|
@ -141,8 +141,7 @@ class JS_PUBLIC_API(AutoGCRooter) {
|
|||
WRAPPER = -31, /* js::AutoWrapperRooter */
|
||||
OBJOBJHASHMAP=-32, /* js::AutoObjectObjectHashMap */
|
||||
OBJU32HASHMAP=-33, /* js::AutoObjectUnsigned32HashMap */
|
||||
OBJHASHSET = -34, /* js::AutoObjectHashSet */
|
||||
JSONPARSER = -35 /* js::JSONParser */
|
||||
OBJHASHSET = -34 /* js::AutoObjectHashSet */
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
@ -3196,7 +3196,7 @@ struct types::ArrayTableKey
|
|||
};
|
||||
|
||||
void
|
||||
TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
|
||||
TypeCompartment::fixArrayType(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
|
@ -3279,32 +3279,31 @@ TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
|
|||
*/
|
||||
struct types::ObjectTableKey
|
||||
{
|
||||
jsid *properties;
|
||||
uint32_t nproperties;
|
||||
jsid *ids;
|
||||
uint32_t nslots;
|
||||
uint32_t nfixed;
|
||||
TaggedProto proto;
|
||||
|
||||
struct Lookup {
|
||||
IdValuePair *properties;
|
||||
uint32_t nproperties;
|
||||
uint32_t nfixed;
|
||||
typedef JSObject * Lookup;
|
||||
|
||||
Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed)
|
||||
: properties(properties), nproperties(nproperties), nfixed(nfixed)
|
||||
{}
|
||||
};
|
||||
|
||||
static inline HashNumber hash(const Lookup &lookup) {
|
||||
return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
|
||||
lookup.nproperties ^
|
||||
lookup.nfixed);
|
||||
static inline uint32_t hash(JSObject *obj) {
|
||||
return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^
|
||||
obj->slotSpan() ^ obj->numFixedSlots() ^
|
||||
((uint32_t)obj->getTaggedProto().toWord() >> 2));
|
||||
}
|
||||
|
||||
static inline bool match(const ObjectTableKey &v, const Lookup &lookup) {
|
||||
if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
|
||||
static inline bool match(const ObjectTableKey &v, RawObject obj) {
|
||||
if (obj->slotSpan() != v.nslots ||
|
||||
obj->numFixedSlots() != v.nfixed ||
|
||||
obj->getTaggedProto() != v.proto) {
|
||||
return false;
|
||||
for (size_t i = 0; i < lookup.nproperties; i++) {
|
||||
if (lookup.properties[i].id != v.properties[i])
|
||||
}
|
||||
RawShape shape = obj->lastProperty();
|
||||
obj = NULL;
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->propid() != v.ids[shape->slot()])
|
||||
return false;
|
||||
shape = shape->previous();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -3313,37 +3312,11 @@ struct types::ObjectTableKey
|
|||
struct types::ObjectTableEntry
|
||||
{
|
||||
ReadBarriered<TypeObject> object;
|
||||
ReadBarriered<Shape> shape;
|
||||
Type *types;
|
||||
};
|
||||
|
||||
static inline void
|
||||
UpdateObjectTableEntryTypes(JSContext *cx, ObjectTableEntry &entry,
|
||||
IdValuePair *properties, size_t nproperties)
|
||||
{
|
||||
for (size_t i = 0; i < nproperties; i++) {
|
||||
Type type = entry.types[i];
|
||||
Type ntype = GetValueTypeForTable(cx, properties[i].value);
|
||||
if (ntype == type)
|
||||
continue;
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
|
||||
type.isPrimitive(JSVAL_TYPE_DOUBLE))
|
||||
{
|
||||
/* The property types already reflect 'int32'. */
|
||||
} else {
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
|
||||
type.isPrimitive(JSVAL_TYPE_INT32))
|
||||
{
|
||||
/* Include 'double' in the property types to avoid the update below later. */
|
||||
entry.types[i] = Type::DoubleType();
|
||||
}
|
||||
entry.object->addPropertyType(cx, IdToTypeId(properties[i].id), ntype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
|
||||
TypeCompartment::fixObjectType(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
|
@ -3357,150 +3330,102 @@ TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
/*
|
||||
* Use the same type object for all singleton/JSON objects with the same
|
||||
* base shape, i.e. the same fields written in the same order.
|
||||
* 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() || !obj->hasEmptyElements())
|
||||
return;
|
||||
|
||||
Vector<IdValuePair> properties(cx);
|
||||
if (!properties.resize(obj->slotSpan())) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
Shape *shape = obj->lastProperty();
|
||||
while (!shape->isEmptyShape()) {
|
||||
IdValuePair &entry = properties[shape->slot()];
|
||||
entry.id = shape->propid();
|
||||
entry.value = obj->getSlot(shape->slot());
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots());
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj.get());
|
||||
RootedShape baseShape(cx, obj->lastProperty());
|
||||
|
||||
if (p) {
|
||||
JS_ASSERT(obj->getProto() == p->value.object->proto);
|
||||
JS_ASSERT(obj->lastProperty() == p->value.shape);
|
||||
|
||||
UpdateObjectTableEntryTypes(cx, p->value, properties.begin(), properties.length());
|
||||
obj->setType(p->value.object);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a new type to use for the object and similar future ones. */
|
||||
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
|
||||
TypeObject *objType = newTypeObject(cx, &ObjectClass, objProto);
|
||||
if (!objType || !objType->addDefiniteProperties(cx, obj)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->isIndexed())
|
||||
objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
|
||||
|
||||
jsid *ids = cx->pod_calloc<jsid>(properties.length());
|
||||
if (!ids) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
Type *types = cx->pod_calloc<Type>(properties.length());
|
||||
if (!types) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < properties.length(); i++) {
|
||||
ids[i] = properties[i].id;
|
||||
types[i] = GetValueTypeForTable(cx, obj->getSlot(i));
|
||||
if (!objType->unknownProperties())
|
||||
objType->addPropertyType(cx, IdToTypeId(ids[i]), types[i]);
|
||||
}
|
||||
|
||||
ObjectTableKey key;
|
||||
key.properties = ids;
|
||||
key.nproperties = properties.length();
|
||||
key.nfixed = obj->numFixedSlots();
|
||||
JS_ASSERT(ObjectTableKey::match(key, lookup));
|
||||
|
||||
ObjectTableEntry entry;
|
||||
entry.object = objType;
|
||||
entry.shape = obj->lastProperty();
|
||||
entry.types = types;
|
||||
|
||||
p = objectTypeTable->lookupForAdd(lookup);
|
||||
if (!objectTypeTable->add(p, key, entry)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
obj->setType(objType);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
if (!objectTypeTable) {
|
||||
objectTypeTable = cx->new_<ObjectTypeTable>();
|
||||
if (!objectTypeTable || !objectTypeTable->init()) {
|
||||
objectTypeTable = NULL;
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return NULL;
|
||||
/* The lookup ensures the shape matches, now check that the types match. */
|
||||
Type *types = p->value.types;
|
||||
for (unsigned i = 0; i < obj->slotSpan(); i++) {
|
||||
Type ntype = GetValueTypeForTable(cx, obj->getSlot(i));
|
||||
if (ntype != types[i]) {
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
|
||||
types[i].isPrimitive(JSVAL_TYPE_DOUBLE))
|
||||
{
|
||||
/* The property types already reflect 'int32'. */
|
||||
} else {
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
|
||||
types[i].isPrimitive(JSVAL_TYPE_INT32))
|
||||
{
|
||||
/* Include 'double' in the property types to avoid the walk below later. */
|
||||
types[i] = Type::DoubleType();
|
||||
}
|
||||
Shape *shape = baseShape;
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->slot() == i) {
|
||||
if (!p->value.object->unknownProperties())
|
||||
p->value.object->addPropertyType(cx, IdToTypeId(shape->propid()), ntype);
|
||||
break;
|
||||
}
|
||||
shape = shape->previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj->setType(p->value.object);
|
||||
} else {
|
||||
/* Make a new type to use for the object and similar future ones. */
|
||||
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
|
||||
TypeObject *objType = newTypeObject(cx, &ObjectClass, objProto);
|
||||
if (!objType || !objType->addDefiniteProperties(cx, obj)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->isIndexed())
|
||||
objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
|
||||
|
||||
jsid *ids = cx->pod_calloc<jsid>(obj->slotSpan());
|
||||
if (!ids) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
Type *types = cx->pod_calloc<Type>(obj->slotSpan());
|
||||
if (!types) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
RootedShape shape(cx, baseShape);
|
||||
while (!shape->isEmptyShape()) {
|
||||
ids[shape->slot()] = shape->propid();
|
||||
types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot()));
|
||||
if (!objType->unknownProperties())
|
||||
objType->addPropertyType(cx, IdToTypeId(shape->propid()), types[shape->slot()]);
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
ObjectTableKey key;
|
||||
key.ids = ids;
|
||||
key.nslots = obj->slotSpan();
|
||||
key.nfixed = obj->numFixedSlots();
|
||||
key.proto = obj->getTaggedProto();
|
||||
JS_ASSERT(ObjectTableKey::match(key, obj.get()));
|
||||
|
||||
ObjectTableEntry entry;
|
||||
entry.object = objType;
|
||||
entry.types = types;
|
||||
|
||||
p = objectTypeTable->lookupForAdd(obj.get());
|
||||
if (!objectTypeTable->add(p, key, entry)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
obj->setType(objType);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the object type table to allocate an object with the specified
|
||||
* properties, filling in its final type and shape and failing if no cache
|
||||
* entry could be found for the properties.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Filter out a few cases where we don't want to use the object type table.
|
||||
* Note that if the properties contain any duplicates or dense indexes,
|
||||
* the lookup below will fail as such arrays of properties cannot be stored
|
||||
* in the object type table --- fixObjectType populates the table with
|
||||
* properties read off its input object, which cannot be duplicates, and
|
||||
* ignores objects with dense indexes.
|
||||
*/
|
||||
if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT)
|
||||
return NULL;
|
||||
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
|
||||
size_t nfixed = gc::GetGCKindSlots(allocKind, &ObjectClass);
|
||||
|
||||
ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, allocKind));
|
||||
if (!obj) {
|
||||
cx->clearPendingException();
|
||||
return NULL;
|
||||
}
|
||||
JS_ASSERT(obj->getProto() == p->value.object->proto);
|
||||
|
||||
RootedShape shape(cx, p->value.shape);
|
||||
if (!JSObject::setLastProperty(cx, obj, shape)) {
|
||||
cx->clearPendingException();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UpdateObjectTableEntryTypes(cx, p->value, properties, nproperties);
|
||||
|
||||
for (size_t i = 0; i < nproperties; i++)
|
||||
obj->setSlot(i, properties[i].value);
|
||||
|
||||
obj->setType(p->value.object);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -3626,7 +3551,7 @@ TypeObject::addProperty(JSContext *cx, RawId id, Property **pprop)
|
|||
}
|
||||
|
||||
bool
|
||||
TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj)
|
||||
TypeObject::addDefiniteProperties(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
if (unknownProperties())
|
||||
return true;
|
||||
|
@ -6540,16 +6465,17 @@ TypeCompartment::sweep(FreeOp *fop)
|
|||
for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
|
||||
const ObjectTableKey &key = e.front().key;
|
||||
ObjectTableEntry &entry = e.front().value;
|
||||
JS_ASSERT(uintptr_t(entry.object->proto.get()) == key.proto.toWord());
|
||||
|
||||
bool remove = false;
|
||||
if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()))
|
||||
remove = true;
|
||||
for (unsigned i = 0; !remove && i < key.nproperties; i++) {
|
||||
if (JSID_IS_STRING(key.properties[i])) {
|
||||
JSString *str = JSID_TO_STRING(key.properties[i]);
|
||||
for (unsigned i = 0; !remove && i < key.nslots; i++) {
|
||||
if (JSID_IS_STRING(key.ids[i])) {
|
||||
JSString *str = JSID_TO_STRING(key.ids[i]);
|
||||
if (IsStringAboutToBeFinalized(&str))
|
||||
remove = true;
|
||||
JS_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
|
||||
JS_ASSERT(AtomToId((JSAtom *)str) == key.ids[i]);
|
||||
}
|
||||
JS_ASSERT(!entry.types[i].isSingleObject());
|
||||
TypeObject *typeObject = NULL;
|
||||
|
@ -6563,7 +6489,7 @@ TypeCompartment::sweep(FreeOp *fop)
|
|||
}
|
||||
|
||||
if (remove) {
|
||||
js_free(key.properties);
|
||||
js_free(key.ids);
|
||||
js_free(entry.types);
|
||||
e.removeFront();
|
||||
}
|
||||
|
@ -6893,7 +6819,7 @@ JSCompartment::sizeOfTypeInferenceData(TypeInferenceSizes *sizes, JSMallocSizeOf
|
|||
const ObjectTableEntry &value = e.front().value;
|
||||
|
||||
/* key.ids and values.types have the same length. */
|
||||
sizes->objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
|
||||
sizes->objectTypeTables += mallocSizeOf(key.ids) + mallocSizeOf(value.types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1054,7 +1054,7 @@ struct TypeObject : gc::Cell
|
|||
/* Helpers */
|
||||
|
||||
bool addProperty(JSContext *cx, RawId id, Property **pprop);
|
||||
bool addDefiniteProperties(JSContext *cx, JSObject *obj);
|
||||
bool addDefiniteProperties(JSContext *cx, HandleObject obj);
|
||||
bool matchDefiniteProperties(HandleObject obj);
|
||||
void addPrototype(JSContext *cx, TypeObject *proto);
|
||||
void addPropertyType(JSContext *cx, jsid id, Type type);
|
||||
|
@ -1382,10 +1382,8 @@ struct TypeCompartment
|
|||
ArrayTypeTable *arrayTypeTable;
|
||||
ObjectTypeTable *objectTypeTable;
|
||||
|
||||
void fixArrayType(JSContext *cx, JSObject *obj);
|
||||
void fixObjectType(JSContext *cx, JSObject *obj);
|
||||
|
||||
JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
|
||||
void fixArrayType(JSContext *cx, HandleObject obj);
|
||||
void fixObjectType(JSContext *cx, HandleObject obj);
|
||||
|
||||
/* Logging fields */
|
||||
|
||||
|
|
|
@ -17,40 +17,6 @@ using namespace js;
|
|||
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
JSONParser::~JSONParser()
|
||||
{
|
||||
for (size_t i = 0; i < stack.length(); i++) {
|
||||
if (stack[i].state == FinishArrayElement)
|
||||
js_delete(&stack[i].elements());
|
||||
else
|
||||
js_delete(&stack[i].properties());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < freeElements.length(); i++)
|
||||
js_delete(freeElements[i]);
|
||||
|
||||
for (size_t i = 0; i < freeProperties.length(); i++)
|
||||
js_delete(freeProperties[i]);
|
||||
}
|
||||
|
||||
void
|
||||
JSONParser::trace(JSTracer *trc)
|
||||
{
|
||||
for (size_t i = 0; i < stack.length(); i++) {
|
||||
if (stack[i].state == FinishArrayElement) {
|
||||
ElementVector &elements = stack[i].elements();
|
||||
for (size_t j = 0; j < elements.length(); j++)
|
||||
gc::MarkValueRoot(trc, &elements[j], "JSONParser element");
|
||||
} else {
|
||||
PropertyVector &properties = stack[i].properties();
|
||||
for (size_t j = 0; j < properties.length(); j++) {
|
||||
gc::MarkValueRoot(trc, &properties[j].value, "JSONParser property value");
|
||||
gc::MarkIdRoot(trc, &properties[j].id, "JSONParser property id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSONParser::error(const char *msg)
|
||||
{
|
||||
|
@ -517,95 +483,17 @@ JSONParser::advanceAfterProperty()
|
|||
return token(Error);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
JSONParser::createFinishedObject(PropertyVector &properties)
|
||||
{
|
||||
/*
|
||||
* Look for an existing cached type and shape for objects with this set of
|
||||
* properties.
|
||||
*/
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
JSObject *obj = cx->compartment->types.newTypedObject(cx, properties.begin(),
|
||||
properties.length());
|
||||
if (obj)
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a new object sized for the given number of properties and fill its
|
||||
* shape in manually.
|
||||
*/
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length());
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, allocKind));
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
RootedId propid(cx);
|
||||
RootedValue value(cx);
|
||||
|
||||
for (size_t i = 0; i < properties.length(); i++) {
|
||||
propid = properties[i].id;
|
||||
value = properties[i].value;
|
||||
if (!DefineNativeProperty(cx, obj, propid, value,
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
|
||||
0, 0))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to assign a new type to the object with type information for its
|
||||
* properties, and update the initializer type object cache with this
|
||||
* object's final shape.
|
||||
*/
|
||||
if (cx->typeInferenceEnabled())
|
||||
cx->compartment->types.fixObjectType(cx, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSONParser::finishObject(MutableHandleValue vp, PropertyVector &properties)
|
||||
{
|
||||
JS_ASSERT(&properties == &stack.back().properties());
|
||||
|
||||
JSObject *obj = createFinishedObject(properties);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
vp.setObject(*obj);
|
||||
if (!freeProperties.append(&properties))
|
||||
return false;
|
||||
stack.popBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSONParser::finishArray(MutableHandleValue vp, ElementVector &elements)
|
||||
{
|
||||
JS_ASSERT(&elements == &stack.back().elements());
|
||||
|
||||
JSObject *obj = NewDenseCopiedArray(cx, elements.length(), elements.begin());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Try to assign a new type to the array according to its elements. */
|
||||
if (cx->typeInferenceEnabled())
|
||||
cx->compartment->types.fixArrayType(cx, obj);
|
||||
|
||||
vp.setObject(*obj);
|
||||
if (!freeElements.append(&elements))
|
||||
return false;
|
||||
stack.popBack();
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* This enum is local to JSONParser::parse, below, but ISO C++98 doesn't allow
|
||||
* templates to depend on local types. Boo-urns!
|
||||
*/
|
||||
enum ParserState { FinishArrayElement, FinishObjectMember, JSONValue };
|
||||
|
||||
bool
|
||||
JSONParser::parse(MutableHandleValue vp)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
JS_ASSERT(stack.empty());
|
||||
Vector<ParserState> stateStack(cx);
|
||||
AutoValueVector valueStack(cx);
|
||||
|
||||
vp.setUndefined();
|
||||
|
||||
|
@ -614,15 +502,18 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
while (true) {
|
||||
switch (state) {
|
||||
case FinishObjectMember: {
|
||||
PropertyVector &properties = stack.back().properties();
|
||||
properties.back().value = value;
|
||||
|
||||
token = advanceAfterProperty();
|
||||
if (token == ObjectClose) {
|
||||
if (!finishObject(&value, properties))
|
||||
return false;
|
||||
break;
|
||||
RootedValue v(cx, valueStack.popCopy());
|
||||
RootedId propid(cx, AtomToId(&valueStack.popCopy().toString()->asAtom()));
|
||||
RootedObject obj(cx, &valueStack.back().toObject());
|
||||
if (!DefineNativeProperty(cx, obj, propid, v,
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
|
||||
0, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
token = advanceAfterProperty();
|
||||
if (token == ObjectClose)
|
||||
break;
|
||||
if (token != Comma) {
|
||||
if (token == OOM)
|
||||
return false;
|
||||
|
@ -636,22 +527,20 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
|
||||
JSONMember:
|
||||
if (token == String) {
|
||||
jsid id = AtomToId(atomValue());
|
||||
PropertyVector &properties = stack.back().properties();
|
||||
if (!properties.append(IdValuePair(id)))
|
||||
if (!valueStack.append(atomValue()))
|
||||
return false;
|
||||
token = advancePropertyColon();
|
||||
if (token != Colon) {
|
||||
JS_ASSERT(token == Error);
|
||||
return errorReturn();
|
||||
}
|
||||
if (!stateStack.append(FinishObjectMember))
|
||||
return false;
|
||||
goto JSONValue;
|
||||
}
|
||||
if (token == ObjectClose) {
|
||||
JS_ASSERT(state == FinishObjectMember);
|
||||
JS_ASSERT(parsingMode == LegacyJSON);
|
||||
if (!finishObject(&value, stack.back().properties()))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (token == OOM)
|
||||
|
@ -661,17 +550,18 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
return errorReturn();
|
||||
|
||||
case FinishArrayElement: {
|
||||
ElementVector &elements = stack.back().elements();
|
||||
if (!elements.append(value.get()))
|
||||
Value v = valueStack.popCopy();
|
||||
Rooted<JSObject*> obj(cx, &valueStack.back().toObject());
|
||||
if (!js_NewbornArrayPush(cx, obj, v))
|
||||
return false;
|
||||
token = advanceAfterArrayElement();
|
||||
if (token == Comma)
|
||||
goto JSONValue;
|
||||
if (token == ArrayClose) {
|
||||
if (!finishArray(&value, elements))
|
||||
if (token == Comma) {
|
||||
if (!stateStack.append(FinishArrayElement))
|
||||
return false;
|
||||
break;
|
||||
goto JSONValue;
|
||||
}
|
||||
if (token == ArrayClose)
|
||||
break;
|
||||
JS_ASSERT(token == Error);
|
||||
return errorReturn();
|
||||
}
|
||||
|
@ -682,69 +572,49 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
JSONValueSwitch:
|
||||
switch (token) {
|
||||
case String:
|
||||
value = stringValue();
|
||||
break;
|
||||
case Number:
|
||||
value = numberValue();
|
||||
if (!valueStack.append(token == String ? stringValue() : numberValue()))
|
||||
return false;
|
||||
break;
|
||||
case True:
|
||||
value = BooleanValue(true);
|
||||
if (!valueStack.append(BooleanValue(true)))
|
||||
return false;
|
||||
break;
|
||||
case False:
|
||||
value = BooleanValue(false);
|
||||
if (!valueStack.append(BooleanValue(false)))
|
||||
return false;
|
||||
break;
|
||||
case Null:
|
||||
value = NullValue();
|
||||
if (!valueStack.append(NullValue()))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case ArrayOpen: {
|
||||
ElementVector *elements;
|
||||
if (!freeElements.empty()) {
|
||||
elements = freeElements.popCopy();
|
||||
elements->clear();
|
||||
} else {
|
||||
elements = cx->new_<ElementVector>(cx);
|
||||
if (!elements)
|
||||
return false;
|
||||
}
|
||||
if (!stack.append(elements))
|
||||
JSObject *obj = NewDenseEmptyArray(cx);
|
||||
if (!obj || !valueStack.append(ObjectValue(*obj)))
|
||||
return false;
|
||||
|
||||
token = advance();
|
||||
if (token == ArrayClose) {
|
||||
if (!finishArray(&value, *elements))
|
||||
return false;
|
||||
if (token == ArrayClose)
|
||||
break;
|
||||
}
|
||||
if (!stateStack.append(FinishArrayElement))
|
||||
return false;
|
||||
goto JSONValueSwitch;
|
||||
}
|
||||
|
||||
case ObjectOpen: {
|
||||
PropertyVector *properties;
|
||||
if (!freeProperties.empty()) {
|
||||
properties = freeProperties.popCopy();
|
||||
properties->clear();
|
||||
} else {
|
||||
properties = cx->new_<PropertyVector>(cx);
|
||||
if (!properties)
|
||||
return false;
|
||||
}
|
||||
if (!stack.append(properties))
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
|
||||
if (!obj || !valueStack.append(ObjectValue(*obj)))
|
||||
return false;
|
||||
|
||||
token = advanceAfterObjectOpen();
|
||||
if (token == ObjectClose) {
|
||||
if (!finishObject(&value, *properties))
|
||||
return false;
|
||||
if (token == ObjectClose)
|
||||
break;
|
||||
}
|
||||
goto JSONMember;
|
||||
}
|
||||
|
||||
case ArrayClose:
|
||||
if (parsingMode == LegacyJSON &&
|
||||
!stack.empty() &&
|
||||
stack.back().state == FinishArrayElement) {
|
||||
!stateStack.empty() &&
|
||||
stateStack.back() == FinishArrayElement) {
|
||||
/*
|
||||
* Previous JSON parsing accepted trailing commas in
|
||||
* non-empty array syntax, and some users depend on this.
|
||||
|
@ -754,8 +624,7 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
* such trailing commas only when specifically
|
||||
* instructed to do so.
|
||||
*/
|
||||
if (!finishArray(&value, stack.back().elements()))
|
||||
return false;
|
||||
stateStack.popBack();
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
|
@ -775,9 +644,9 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
break;
|
||||
}
|
||||
|
||||
if (stack.empty())
|
||||
if (stateStack.empty())
|
||||
break;
|
||||
state = stack.back().state;
|
||||
state = stateStack.popCopy();
|
||||
}
|
||||
|
||||
for (; current < end; current++) {
|
||||
|
@ -788,8 +657,7 @@ JSONParser::parse(MutableHandleValue vp)
|
|||
}
|
||||
|
||||
JS_ASSERT(end == current);
|
||||
JS_ASSERT(stack.empty());
|
||||
|
||||
vp.set(value);
|
||||
JS_ASSERT(valueStack.length() == 1);
|
||||
vp.set(valueStack[0]);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -14,12 +14,10 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* NB: This class must only be used on the stack.
|
||||
* NB: This class must only be used on the stack as it contains a js::Value.
|
||||
*/
|
||||
class JSONParser : private AutoGCRooter
|
||||
class JSONParser
|
||||
{
|
||||
public:
|
||||
enum ErrorHandling { RaiseError, NoError };
|
||||
|
@ -29,10 +27,10 @@ class JSONParser : private AutoGCRooter
|
|||
/* Data members */
|
||||
|
||||
JSContext * const cx;
|
||||
StableCharPtr current;
|
||||
const StableCharPtr end;
|
||||
JS::StableCharPtr current;
|
||||
const JS::StableCharPtr end;
|
||||
|
||||
Value v;
|
||||
js::Value v;
|
||||
|
||||
const ParsingMode parsingMode;
|
||||
const ErrorHandling errorHandling;
|
||||
|
@ -42,70 +40,6 @@ class JSONParser : private AutoGCRooter
|
|||
ObjectOpen, ObjectClose,
|
||||
Colon, Comma,
|
||||
OOM, Error };
|
||||
|
||||
// State related to the parser's current position. At all points in the
|
||||
// parse this keeps track of the stack of arrays and objects which have
|
||||
// been started but not finished yet. The actual JS object is not
|
||||
// allocated until the literal is closed, so that the result can be sized
|
||||
// according to its contents and have its type and shape filled in using
|
||||
// caches.
|
||||
|
||||
// State for an array that is currently being parsed. This includes all
|
||||
// elements that have been seen so far.
|
||||
typedef Vector<Value, 20> ElementVector;
|
||||
|
||||
// State for an object that is currently being parsed. This includes all
|
||||
// the key/value pairs that have been seen so far.
|
||||
typedef Vector<IdValuePair, 10> PropertyVector;
|
||||
|
||||
// Possible states the parser can be in between values.
|
||||
enum ParserState {
|
||||
// An array element has just being parsed.
|
||||
FinishArrayElement,
|
||||
|
||||
// An object property has just been parsed.
|
||||
FinishObjectMember,
|
||||
|
||||
// At the start of the parse, before any values have been processed.
|
||||
JSONValue
|
||||
};
|
||||
|
||||
// Stack element for an in progress array or object.
|
||||
struct StackEntry {
|
||||
ElementVector &elements() {
|
||||
JS_ASSERT(state == FinishArrayElement);
|
||||
return * static_cast<ElementVector *>(vector);
|
||||
}
|
||||
|
||||
PropertyVector &properties() {
|
||||
JS_ASSERT(state == FinishObjectMember);
|
||||
return * static_cast<PropertyVector *>(vector);
|
||||
}
|
||||
|
||||
StackEntry(ElementVector *elements)
|
||||
: state(FinishArrayElement), vector(elements)
|
||||
{}
|
||||
|
||||
StackEntry(PropertyVector *properties)
|
||||
: state(FinishObjectMember), vector(properties)
|
||||
{}
|
||||
|
||||
ParserState state;
|
||||
|
||||
private:
|
||||
void *vector;
|
||||
};
|
||||
|
||||
// All in progress arrays and objects being parsed, in order from outermost
|
||||
// to innermost.
|
||||
Vector<StackEntry, 10> stack;
|
||||
|
||||
// Unused element and property vectors for previous in progress arrays and
|
||||
// objects. These vectors are not freed until the end of the parse to avoid
|
||||
// unnecessary freeing and allocation.
|
||||
Vector<ElementVector*, 5> freeElements;
|
||||
Vector<PropertyVector*, 5> freeProperties;
|
||||
|
||||
#ifdef DEBUG
|
||||
Token lastToken;
|
||||
#endif
|
||||
|
@ -124,15 +58,11 @@ class JSONParser : private AutoGCRooter
|
|||
JSONParser(JSContext *cx, JS::StableCharPtr data, size_t length,
|
||||
ParsingMode parsingMode = StrictJSON,
|
||||
ErrorHandling errorHandling = RaiseError)
|
||||
: AutoGCRooter(cx, JSONPARSER),
|
||||
cx(cx),
|
||||
: cx(cx),
|
||||
current(data),
|
||||
end((data + length).get(), data.get(), length),
|
||||
parsingMode(parsingMode),
|
||||
errorHandling(errorHandling),
|
||||
stack(cx),
|
||||
freeElements(cx),
|
||||
freeProperties(cx)
|
||||
errorHandling(errorHandling)
|
||||
#ifdef DEBUG
|
||||
, lastToken(Error)
|
||||
#endif
|
||||
|
@ -140,8 +70,6 @@ class JSONParser : private AutoGCRooter
|
|||
JS_ASSERT(current <= end);
|
||||
}
|
||||
|
||||
~JSONParser();
|
||||
|
||||
/*
|
||||
* Parse the JSON data specified at construction time. If it parses
|
||||
* successfully, store the prescribed value in *vp and return true. If an
|
||||
|
@ -167,9 +95,10 @@ class JSONParser : private AutoGCRooter
|
|||
return v;
|
||||
}
|
||||
|
||||
JSAtom *atomValue() const {
|
||||
js::Value atomValue() const {
|
||||
js::Value strval = stringValue();
|
||||
return &strval.toString()->asAtom();
|
||||
JS_ASSERT(strval.toString()->isAtom());
|
||||
return strval;
|
||||
}
|
||||
|
||||
Token token(Token t) {
|
||||
|
@ -212,18 +141,9 @@ class JSONParser : private AutoGCRooter
|
|||
void error(const char *msg);
|
||||
bool errorReturn();
|
||||
|
||||
JSObject *createFinishedObject(PropertyVector &properties);
|
||||
bool finishObject(MutableHandleValue vp, PropertyVector &properties);
|
||||
bool finishArray(MutableHandleValue vp, ElementVector &elements);
|
||||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
private:
|
||||
JSONParser(const JSONParser &other) MOZ_DELETE;
|
||||
void operator=(const JSONParser &other) MOZ_DELETE;
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsonparser_h___ */
|
||||
|
|
|
@ -211,17 +211,6 @@ class XDRState;
|
|||
|
||||
class FreeOp;
|
||||
|
||||
struct IdValuePair
|
||||
{
|
||||
jsid id;
|
||||
Value value;
|
||||
|
||||
IdValuePair() {}
|
||||
IdValuePair(jsid idArg)
|
||||
: id(idArg)
|
||||
{}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JSC {
|
||||
|
|
Загрузка…
Ссылка в новой задаче