Bug 1135897 - Use unboxed objects for JSON objects and constant literals embedded in scripts, r=jandem.

This commit is contained in:
Brian Hackett 2015-03-12 10:58:19 -06:00
parent 5a8747a2a7
commit 890f1bdf16
33 changed files with 860 additions and 618 deletions

View File

@ -8,7 +8,7 @@
#define js_Id_h
// A jsid is an identifier for a property or method of an object which is
// either a 31-bit signed integer, interned string or object.
// either a 31-bit unsigned integer, interned string or symbol.
//
// Also, there is an additional jsid value, JSID_VOID, which does not occur in
// JS scripts but may be used to indicate the absence of a valid jsid. A void
@ -17,7 +17,7 @@
// entry points expecting a jsid and do not need to handle JSID_VOID in hooks
// receiving a jsid except when explicitly noted in the API contract.
//
// A jsid is not implicitly convertible to or from a jsval; JS_ValueToId or
// A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
// JS_IdToValue must be used instead.
#include "jstypes.h"

View File

@ -751,7 +751,7 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
// Call the per-exported-function trampoline created by GenerateEntry.
AsmJSModule::CodePtr enter = module.entryTrampoline(func);
if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData()))
if (!CALL_GENERATED_2(enter, coercedArgs.begin(), module.globalData()))
return false;
}

View File

@ -18,10 +18,28 @@ struct IdValuePair
jsid id;
Value value;
IdValuePair() {}
IdValuePair()
: id(JSID_EMPTY), value(UndefinedValue())
{}
explicit IdValuePair(jsid idArg)
: id(idArg), value(UndefinedValue())
{}
IdValuePair(jsid idArg, Value valueArg)
: id(idArg), value(valueArg)
{}
};
class MOZ_STACK_CLASS AutoIdValueVector : public AutoVectorRooter<IdValuePair>
{
public:
explicit AutoIdValueVector(ContextFriendFields *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoVectorRooter<IdValuePair>(cx, IDVALVECTOR)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} /* namespace js */

View File

@ -4319,11 +4319,7 @@ ParseNode::getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObje
if (allowObjects == DontAllowNestedObjects)
allowObjects = DontAllowObjects;
gc::AllocKind kind = GuessObjectGCKind(pn_count);
RootedPlainObject obj(cx,
NewBuiltinClassInstance<PlainObject>(cx, kind, MaybeSingletonObject));
if (!obj)
return false;
AutoIdValueVector properties(cx);
RootedValue value(cx), idvalue(cx);
for (ParseNode *pn = pn_head; pn; pn = pn->pn_next) {
@ -4343,31 +4339,19 @@ ParseNode::getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObje
idvalue = StringValue(pnid->pn_atom);
}
uint32_t index;
if (IsDefinitelyIndex(idvalue, &index)) {
if (!DefineElement(cx, obj, index, value, nullptr, nullptr, JSPROP_ENUMERATE))
return false;
continue;
}
JSAtom *name = ToAtom<CanGC>(cx, idvalue);
if (!name)
RootedId id(cx);
if (!ValueToId<CanGC>(cx, idvalue, &id))
return false;
if (name->isIndex(&index)) {
if (!DefineElement(cx, obj, index, value, nullptr, nullptr, JSPROP_ENUMERATE))
return false;
} else {
if (!DefineProperty(cx, obj, name->asPropertyName(), value,
nullptr, nullptr, JSPROP_ENUMERATE))
{
return false;
}
}
if (!properties.append(IdValuePair(id, value)))
return false;
}
ObjectGroup::fixPlainObjectGroup(cx, obj);
JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(),
TenuredObject);
if (!obj)
return false;
vp.setObject(*obj);
return true;
}
@ -7794,7 +7778,7 @@ CGObjectList::finish(ObjectArray *array)
MOZ_ASSERT(length <= INDEX_LIMIT);
MOZ_ASSERT(length == array->length);
js::HeapPtrNativeObject *cursor = array->vector + array->length;
js::HeapPtrObject *cursor = array->vector + array->length;
ObjectBox *objbox = lastbox;
do {
--cursor;

View File

@ -168,6 +168,15 @@ AutoGCRooter::trace(JSTracer *trc)
return;
}
case IDVALVECTOR: {
AutoIdValueVector::VectorImpl &vector = static_cast<AutoIdValueVector *>(this)->vector;
for (size_t i = 0; i < vector.length(); i++) {
MarkIdRoot(trc, &vector[i].id, "js::AutoIdValueVector id");
MarkValueRoot(trc, &vector[i].value, "js::AutoIdValueVector value");
}
return;
}
case SHAPEVECTOR: {
AutoShapeVector::VectorImpl &vector = static_cast<js::AutoShapeVector *>(this)->vector;
MarkShapeRootRange(trc, vector.length(), const_cast<Shape **>(vector.begin()),

View File

@ -89,19 +89,7 @@ NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(LifoAlloc *alloc, RegExpS
temp1.name(),
temp2.name());
// Determine the non-volatile registers which might be modified by jitcode.
for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) {
Register reg = *iter;
if (!regs.has(reg))
savedNonVolatileRegisters.add(reg);
}
#if defined(JS_CODEGEN_ARM)
// ARM additionally requires that the link register be saved.
savedNonVolatileRegisters.add(Register::FromCode(Registers::lr));
#elif defined(JS_CODEGEN_MIPS)
savedNonVolatileRegisters.add(Register::FromCode(Registers::ra));
#endif
savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
masm.jump(&entry_label_);
masm.bind(&start_label_);

View File

@ -1771,7 +1771,7 @@ irregexp::ExecuteCode(JSContext *cx, jit::JitCode *codeBlock, const CharT *chars
{
JS::AutoSuppressGCAnalysis nogc;
CALL_GENERATED_REGEXP(function, &data);
CALL_GENERATED_1(function, &data);
}
return (RegExpRunStatus) data.result;

View File

@ -1361,7 +1361,7 @@ BaselineCompiler::emit_JSOP_SYMBOL()
return true;
}
typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind);
typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
static const VMFunction DeepCloneObjectLiteralInfo =
FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);

View File

@ -1942,7 +1942,7 @@ CodeGenerator::visitTableSwitchV(LTableSwitchV *ins)
emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
}
typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind);
typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
static const VMFunction DeepCloneObjectLiteralInfo =
FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
@ -4383,9 +4383,9 @@ CodeGenerator::visitNewObject(LNewObject *lir)
OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
addOutOfLineCode(ool, lir->mir());
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
bool initContents = ShouldInitFixedSlots(lir, templateObject);
masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry(),
initFixedSlots);
initContents);
masm.bind(ool->rejoin());
}
@ -4560,9 +4560,9 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
Imm32(gc::DefaultHeap)),
StoreRegisterTo(objReg));
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
bool initContents = ShouldInitFixedSlots(lir, templateObj);
masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
initFixedSlots);
initContents);
masm.bind(ool->rejoin());
}
@ -4588,9 +4588,9 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir)
StoreRegisterTo(objReg));
// Inline call object creation, using the OOL path only for tricky cases.
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
bool initContents = ShouldInitFixedSlots(lir, templateObj);
masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
initFixedSlots);
initContents);
masm.bind(ool->rejoin());
}
@ -4807,9 +4807,9 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
// Initialize based on the templateObject.
masm.bind(ool->rejoin());
bool initFixedSlots = !templateObject->is<PlainObject>() ||
bool initContents = !templateObject->is<PlainObject>() ||
ShouldInitFixedSlots(lir, &templateObject->as<PlainObject>());
masm.initGCThing(objReg, tempReg, templateObject, initFixedSlots);
masm.initGCThing(objReg, tempReg, templateObject, initContents);
}
typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, JitFrameLayout *frame, HandleObject);

View File

@ -21,11 +21,13 @@
(js::jit::Simulator::Current()->call( \
JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 8, p0, p1, p2, p3, p4, p5, p6, p7) & 0xffffffff)
#define CALL_GENERATED_REGEXP(entry, p0) \
js::jit::Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 1, p0)
#define CALL_GENERATED_1(entry, p0) \
(js::jit::Simulator::Current()->call( \
JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 1, p0) & 0xffffffff)
#define CALL_GENERATED_ASMJS(entry, p0, p1) \
(Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 2, p0, p1) & 0xffffffff)
#define CALL_GENERATED_2(entry, p0, p1) \
(js::jit::Simulator::Current()->call( \
JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 2, p0, p1) & 0xffffffff)
#else
@ -33,11 +35,8 @@
#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7) \
entry(p0, p1, p2, p3, p4, p5, p6, p7)
#define CALL_GENERATED_REGEXP(entry, p0) \
entry(p0)
#define CALL_GENERATED_ASMJS(entry, p0, p1) \
entry(p0, p1)
#define CALL_GENERATED_1(entry, p0) entry(p0)
#define CALL_GENERATED_2(entry, p0, p1) entry(p0, p1)
#endif

View File

@ -1039,7 +1039,7 @@ MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj
void
MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots)
gc::InitialHeap initialHeap, Label *fail, bool initContents)
{
gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
@ -1056,7 +1056,7 @@ MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateOb
}
allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail);
initGCThing(obj, temp, templateObj, initFixedSlots);
initGCThing(obj, temp, templateObj, initContents);
}
@ -1156,7 +1156,7 @@ FindStartOfUndefinedAndUninitializedSlots(NativeObject *templateObj, uint32_t ns
void
MacroAssembler::initGCSlots(Register obj, Register temp, NativeObject *templateObj,
bool initFixedSlots)
bool initContents)
{
// Slots of non-array objects are required to be initialized.
// Use the values currently in the template object.
@ -1191,7 +1191,7 @@ MacroAssembler::initGCSlots(Register obj, Register temp, NativeObject *templateO
copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
// Fill the rest of the fixed slots with undefined and uninitialized.
if (initFixedSlots) {
if (initContents) {
fillSlotsWithUndefined(Address(obj, NativeObject::getFixedSlotOffset(startOfUndefined)), temp,
startOfUndefined, Min(startOfUninitialized, nfixed));
size_t offset = NativeObject::getFixedSlotOffset(startOfUninitialized);
@ -1217,7 +1217,7 @@ MacroAssembler::initGCSlots(Register obj, Register temp, NativeObject *templateO
void
MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
bool initFixedSlots)
bool initContents)
{
// Fast initialization of an empty object returned by allocateObject().
@ -1259,7 +1259,7 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
} else {
storePtr(ImmPtr(emptyObjectElements), Address(obj, NativeObject::offsetOfElements()));
initGCSlots(obj, temp, ntemplate, initFixedSlots);
initGCSlots(obj, temp, ntemplate, initContents);
if (ntemplate->hasPrivate()) {
uint32_t nfixed = ntemplate->numFixedSlots();
@ -1281,26 +1281,10 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
offset += sizeof(uintptr_t);
}
} else if (templateObj->is<UnboxedPlainObject>()) {
const UnboxedLayout &layout = templateObj->as<UnboxedPlainObject>().layout();
storePtr(ImmWord(0), Address(obj, JSObject::offsetOfShape()));
// Initialize reference fields of the object, per UnboxedPlainObject::create.
if (const int32_t *list = layout.traceList()) {
while (*list != -1) {
storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
Address(obj, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
list++;
while (*list != -1) {
storePtr(ImmWord(0),
Address(obj, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
// Unboxed objects don't have Values to initialize.
MOZ_ASSERT(*(list + 1) == -1);
}
if (initContents)
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
} else {
MOZ_CRASH("Unknown object");
}
@ -1321,6 +1305,29 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
#endif
}
void
MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject *templateObject)
{
const UnboxedLayout &layout = templateObject->layout();
// Initialize reference fields of the object, per UnboxedPlainObject::create.
if (const int32_t *list = layout.traceList()) {
while (*list != -1) {
storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
Address(object, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
list++;
while (*list != -1) {
storePtr(ImmWord(0),
Address(object, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
// Unboxed objects don't have Values to initialize.
MOZ_ASSERT(*(list + 1) == -1);
}
}
void
MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
Label *fail)

View File

@ -30,6 +30,7 @@
#include "jit/VMFunctions.h"
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
#include "vm/UnboxedObject.h"
#ifdef IS_LITTLE_ENDIAN
#define IMM32_16ADJ(X) X << 16
@ -829,18 +830,20 @@ class MacroAssembler : public MacroAssemblerSpecific
const Value &v);
void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
void fillSlotsWithUninitialized(Address addr, Register temp, uint32_t start, uint32_t end);
void initGCSlots(Register obj, Register temp, NativeObject *templateObj, bool initFixedSlots);
void initGCSlots(Register obj, Register temp, NativeObject *templateObj, bool initContents);
public:
void callMallocStub(size_t nbytes, Register result, Label *fail);
void callFreeStub(Register slots);
void createGCObject(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);
gc::InitialHeap initialHeap, Label *fail, bool initContents = true);
void newGCThing(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail);
void initGCThing(Register obj, Register temp, JSObject *templateObj,
bool initFixedSlots = true);
bool initContents = true);
void initUnboxedObjectContents(Register object, UnboxedPlainObject *templateObject);
void newGCString(Register result, Register temp, Label *fail);
void newGCFatInlineString(Register result, Register temp, Label *fail);

View File

@ -889,6 +889,30 @@ class ABIArg
AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); }
};
// Get the set of registers which should be saved by a block of code which
// clobbers all registers besides |unused|, but does not clobber floating point
// registers.
inline GeneralRegisterSet
SavedNonVolatileRegisters(GeneralRegisterSet unused)
{
GeneralRegisterSet result;
for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) {
Register reg = *iter;
if (!unused.has(reg))
result.add(reg);
}
// ARM and MIPS require an additional register to be saved, if calls can be made.
#if defined(JS_CODEGEN_ARM)
result.add(Register::FromCode(Registers::lr));
#elif defined(JS_CODEGEN_MIPS)
result.add(Register::FromCode(Registers::ra));
#endif
return result;
}
} // namespace jit
} // namespace js

View File

@ -122,6 +122,7 @@ class AutoVectorRooter : protected AutoGCRooter
bool empty() const { return vector.empty(); }
bool append(const T &v) { return vector.append(v); }
bool appendN(const T &v, size_t len) { return vector.appendN(v, len); }
bool append(const T *ptr, size_t len) { return vector.append(ptr, len); }
bool appendAll(const AutoVectorRooter<T> &other) {
return vector.appendAll(other.vector);

View File

@ -63,7 +63,7 @@ ValueToIdPure(const Value &v, jsid *id)
template <AllowGC allowGC>
inline bool
ValueToId(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
ValueToId(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
{
int32_t i;

View File

@ -1432,7 +1432,7 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp,
}
static bool
NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectKind newKind)
{
return group->proto().isObject() &&
@ -1440,7 +1440,8 @@ NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObjec
newKind == GenericObject &&
group->clasp()->isNative() &&
(!group->newScript() || group->newScript()->analyzed()) &&
!cx->compartment()->hasObjectMetadataCallback();
!cx->compartment()->hasObjectMetadataCallback() &&
cx->isJSContext();
}
/*
@ -1448,7 +1449,7 @@ NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObjec
* avoid losing creation site information for objects made by scripted 'new'.
*/
JSObject *
js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObject parent,
js::NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
gc::AllocKind allocKind, NewObjectKind newKind)
{
MOZ_ASSERT(parent);
@ -1459,10 +1460,10 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
bool isCachable = NewObjectWithGroupIsCachable(cx, group, parent, newKind);
if (isCachable) {
NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGroup(group, allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry,
JSObject *obj = cache.newObjectFromHit(cx->asJSContext(), entry,
GetInitialHeap(newKind, group->clasp()));
if (obj)
return obj;
@ -1474,7 +1475,7 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
return nullptr;
if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
cache.lookupGroup(group, allocKind, &entry);
cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
@ -1779,90 +1780,162 @@ js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto)
return clone;
}
NativeObject *
js::DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind)
static bool
GetScriptArrayObjectElements(JSContext *cx, HandleArrayObject obj, AutoValueVector &values)
{
MOZ_ASSERT(!obj->isSingleton());
if (obj->nonProxyIsExtensible()) {
MOZ_ASSERT(obj->slotSpan() == 0);
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), obj->getDenseInitializedLength()))
return false;
for (size_t i = 0; i < obj->getDenseInitializedLength(); i++)
values[i].set(obj->getDenseElement(i));
} else {
// Call site objects are frozen before they escape to script, which
// converts their dense elements into data properties.
for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
Shape &shape = r.front();
if (shape.propid() == NameToId(cx->names().length))
continue;
MOZ_ASSERT(shape.isDataDescriptor());
// The 'raw' property is added before freezing call site objects.
// After an XDR or deep clone the script object will no longer be
// frozen, and the two objects will be connected again the first
// time the JSOP_CALLSITEOBJ executes.
if (shape.propid() == NameToId(cx->names().raw))
continue;
uint32_t index = JSID_TO_INT(shape.propid());
while (index >= values.length()) {
if (!values.append(MagicValue(JS_ELEMENTS_HOLE)))
return false;
}
values[index].set(obj->getSlot(shape.slot()));
}
}
return true;
}
static bool
GetScriptPlainObjectProperties(JSContext *cx, HandleObject obj, AutoIdValueVector &properties)
{
if (obj->is<PlainObject>()) {
PlainObject *nobj = &obj->as<PlainObject>();
if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
return false;
for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
Shape &shape = r.front();
MOZ_ASSERT(shape.isDataDescriptor());
uint32_t slot = shape.slot();
properties[slot].get().id = shape.propid();
properties[slot].get().value = nobj->getSlot(slot);
}
for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
Value v = nobj->getDenseElement(i);
if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
return false;
}
return true;
}
if (obj->is<UnboxedPlainObject>()) {
UnboxedPlainObject *nobj = &obj->as<UnboxedPlainObject>();
const UnboxedLayout &layout = nobj->layout();
if (!properties.appendN(IdValuePair(), layout.properties().length()))
return false;
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property &property = layout.properties()[i];
properties[i].get().id = NameToId(property.name);
properties[i].get().value = nobj->getValue(property);
}
return true;
}
MOZ_CRASH("Bad object kind");
}
static bool
DeepCloneValue(JSContext *cx, Value *vp, NewObjectKind newKind)
{
if (vp->isObject()) {
RootedObject obj(cx, &vp->toObject());
obj = DeepCloneObjectLiteral(cx, obj, newKind);
if (!obj)
return false;
vp->setObject(*obj);
}
return true;
}
JSObject *
js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind)
{
/* NB: Keep this in sync with XDRObjectLiteral. */
MOZ_ASSERT_IF(obj->isSingleton(),
JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
// Result of the clone function.
RootedNativeObject clone(cx);
// Temporary element/slot which would be stored in the cloned object.
RootedValue v(cx);
RootedNativeObject deepObj(cx);
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<ArrayObject>());
MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
MOZ_ASSERT(newKind != SingletonObject);
if (obj->is<ArrayObject>()) {
clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), NullPtr(), newKind);
} else {
// Object literals are tenured by default as holded by the JSScript.
MOZ_ASSERT(obj->isTenured());
AllocKind kind = obj->asTenured().getAllocKind();
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
HandleArrayObject aobj = obj.as<ArrayObject>();
AutoValueVector values(cx);
if (!GetScriptArrayObjectElements(cx, aobj, values))
return nullptr;
RootedObject proto(cx, group->proto().toObject());
obj->assertParentIs(cx->global());
clone = NewNativeObjectWithGivenProto(cx, &PlainObject::class_, proto,
kind, newKind);
// Deep clone any elements.
uint32_t initialized = aobj->getDenseInitializedLength();
for (uint32_t i = 0; i < initialized; ++i) {
if (!DeepCloneValue(cx, values[i].address(), newKind))
return nullptr;
}
RootedArrayObject clone(cx, NewDenseUnallocatedArray(cx, aobj->length(),
NullPtr(), newKind));
if (!clone || !clone->ensureElements(cx, values.length()))
return nullptr;
clone->setDenseInitializedLength(values.length());
clone->initDenseElements(0, values.begin(), values.length());
if (aobj->denseElementsAreCopyOnWrite()) {
if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
return nullptr;
} else {
ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>());
}
return clone;
}
// Allocate the same number of slots.
if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
AutoIdValueVector properties(cx);
if (!GetScriptPlainObjectProperties(cx, obj, properties))
return nullptr;
// Recursive copy of dense element.
uint32_t initialized = obj->getDenseInitializedLength();
for (uint32_t i = 0; i < initialized; ++i) {
v = obj->getDenseElement(i);
if (v.isObject()) {
deepObj = &v.toObject().as<NativeObject>();
deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
if (!deepObj) {
JS_ReportOutOfMemory(cx);
return nullptr;
}
v.setObject(*deepObj);
}
clone->setDenseInitializedLength(i + 1);
clone->initDenseElement(i, v);
}
MOZ_ASSERT(obj->compartment() == clone->compartment());
MOZ_ASSERT(!obj->hasPrivate());
RootedShape shape(cx, obj->lastProperty());
size_t span = shape->slotSpan();
if (!clone->setLastProperty(cx, shape))
return nullptr;
for (size_t i = 0; i < span; i++) {
v = obj->getSlot(i);
if (v.isObject()) {
deepObj = &v.toObject().as<NativeObject>();
deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
if (!deepObj)
return nullptr;
v.setObject(*deepObj);
}
clone->setSlot(i, v);
}
if (obj->isSingleton()) {
if (!JSObject::setSingleton(cx, clone))
return nullptr;
} else if (obj->is<ArrayObject>()) {
ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>());
} else {
ObjectGroup::fixPlainObjectGroup(cx, &clone->as<PlainObject>());
}
if (obj->is<ArrayObject>() && obj->denseElementsAreCopyOnWrite()) {
if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
for (size_t i = 0; i < properties.length(); i++) {
if (!DeepCloneValue(cx, &properties[i].get().value, newKind))
return nullptr;
}
return clone;
if (obj->isSingleton())
newKind = SingletonObject;
return ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind);
}
static bool
@ -1919,7 +1992,7 @@ JS_InitializePropertiesFromCompatibleNativeObject(JSContext *cx,
template<XDRMode mode>
bool
js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj)
js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
{
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
@ -1931,231 +2004,139 @@ js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj)
uint32_t isArray = 0;
{
if (mode == XDR_ENCODE) {
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
MOZ_ASSERT(obj->is<PlainObject>() ||
obj->is<UnboxedPlainObject>() ||
obj->is<ArrayObject>());
isArray = obj->is<ArrayObject>() ? 1 : 0;
}
if (!xdr->codeUint32(&isArray))
return false;
}
RootedValue tmpValue(cx), tmpIdValue(cx);
RootedId tmpId(cx);
if (isArray) {
uint32_t length;
RootedArrayObject aobj(cx);
if (mode == XDR_ENCODE)
length = obj->as<ArrayObject>().length();
if (mode == XDR_ENCODE) {
aobj = &obj->as<ArrayObject>();
length = aobj->length();
}
if (!xdr->codeUint32(&length))
return false;
if (mode == XDR_DECODE)
obj.set(NewDenseUnallocatedArray(cx, length, NullPtr(), js::MaybeSingletonObject));
if (mode == XDR_DECODE) {
obj.set(NewDenseUnallocatedArray(cx, length, NullPtr(), TenuredObject));
if (!obj)
return false;
aobj = &obj->as<ArrayObject>();
}
} else {
// Code the alloc kind of the object.
AllocKind kind;
AutoValueVector values(cx);
if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, aobj, values))
return nullptr;
uint32_t initialized;
{
if (mode == XDR_ENCODE) {
MOZ_ASSERT(obj->is<PlainObject>());
MOZ_ASSERT(obj->isTenured());
kind = obj->asTenured().getAllocKind();
}
if (!xdr->codeEnum32(&kind))
if (mode == XDR_ENCODE)
initialized = values.length();
if (!xdr->codeUint32(&initialized))
return false;
if (mode == XDR_DECODE) {
obj.set(NewBuiltinClassInstance<PlainObject>(cx, kind, MaybeSingletonObject));
if (!obj)
return false;
if (initialized) {
if (!aobj->ensureElements(cx, initialized))
return false;
}
}
}
}
{
uint32_t capacity;
if (mode == XDR_ENCODE)
capacity = obj->getDenseCapacity();
if (!xdr->codeUint32(&capacity))
return false;
if (mode == XDR_DECODE) {
if (!obj->ensureElements(cx, capacity)) {
JS_ReportOutOfMemory(cx);
return false;
}
}
}
uint32_t initialized;
{
if (mode == XDR_ENCODE)
initialized = obj->getDenseInitializedLength();
if (!xdr->codeUint32(&initialized))
return false;
if (mode == XDR_DECODE) {
if (initialized)
obj->setDenseInitializedLength(initialized);
}
}
RootedValue tmpValue(cx);
// Recursively copy dense elements.
{
// Recursively copy dense elements.
for (unsigned i = 0; i < initialized; i++) {
if (mode == XDR_ENCODE)
tmpValue = obj->getDenseElement(i);
if (!xdr->codeConstValue(&tmpValue))
return false;
if (mode == XDR_DECODE)
obj->initDenseElement(i, tmpValue);
}
}
MOZ_ASSERT(!obj->hasPrivate());
RootedShape shape(cx, obj->lastProperty());
// Code the number of slots in the vector.
unsigned nslot = 0;
// Code ids of the object in order. As opposed to DeepCloneObjectLiteral we
// cannot just re-use the shape of the original bytecode value and we have
// to write down the shape as well as the corresponding values. Ideally we
// would have a mechanism to serialize the shape too.
js::AutoIdVector ids(cx);
{
if (mode == XDR_ENCODE && !shape->isEmptyShape()) {
nslot = shape->slotSpan();
if (!ids.reserve(nslot))
return false;
for (unsigned i = 0; i < nslot; i++)
ids.infallibleAppend(JSID_VOID);
for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) {
// If we have reached the native property of the array class, we
// exit as the remaining would only be reserved slots.
if (!it.front().hasSlot()) {
MOZ_ASSERT(isArray);
break;
}
MOZ_ASSERT(it.front().hasDefaultGetter());
ids[it.front().slot()].set(it.front().propid());
}
}
if (!xdr->codeUint32(&nslot))
return false;
RootedAtom atom(cx);
RootedId id(cx);
uint32_t idType = 0;
for (unsigned i = 0; i < nslot; i++) {
if (mode == XDR_ENCODE) {
id = ids[i];
if (JSID_IS_INT(id))
idType = JSID_TYPE_INT;
else if (JSID_IS_ATOM(id))
idType = JSID_TYPE_STRING;
else
MOZ_CRASH("Symbol property is not yet supported by XDR.");
tmpValue = obj->getSlot(i);
}
if (!xdr->codeUint32(&idType))
return false;
if (idType == JSID_TYPE_STRING) {
if (mode == XDR_ENCODE)
atom = JSID_TO_ATOM(id);
if (!XDRAtom(xdr, &atom))
return false;
if (mode == XDR_DECODE)
id = AtomToId(atom);
} else {
MOZ_ASSERT(idType == JSID_TYPE_INT);
uint32_t indexVal;
if (mode == XDR_ENCODE)
indexVal = uint32_t(JSID_TO_INT(id));
if (!xdr->codeUint32(&indexVal))
return false;
if (mode == XDR_DECODE)
id = INT_TO_JSID(int32_t(indexVal));
}
tmpValue = values[i];
if (!xdr->codeConstValue(&tmpValue))
return false;
if (mode == XDR_DECODE) {
if (!NativeDefineProperty(cx, obj, id, tmpValue, nullptr, nullptr,
JSPROP_ENUMERATE))
{
return false;
}
aobj->setDenseInitializedLength(i + 1);
aobj->initDenseElement(i, tmpValue);
}
}
MOZ_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
uint32_t copyOnWrite;
if (mode == XDR_ENCODE)
copyOnWrite = aobj->denseElementsAreCopyOnWrite();
if (!xdr->codeUint32(&copyOnWrite))
return false;
if (mode == XDR_DECODE) {
if (copyOnWrite) {
if (!ObjectElements::MakeElementsCopyOnWrite(cx, aobj))
return false;
} else {
ObjectGroup::fixArrayGroup(cx, aobj);
}
}
return true;
}
// Fix up types, distinguishing singleton-typed objects.
uint32_t isSingletonTyped;
// Code the properties in the object.
AutoIdValueVector properties(cx);
if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(cx, obj, properties))
return false;
uint32_t nproperties = properties.length();
if (!xdr->codeUint32(&nproperties))
return false;
if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties))
return false;
for (size_t i = 0; i < nproperties; i++) {
if (mode == XDR_ENCODE) {
tmpIdValue = IdToValue(properties[i].get().id);
tmpValue = properties[i].get().value;
}
if (!xdr->codeConstValue(&tmpIdValue) || !xdr->codeConstValue(&tmpValue))
return false;
if (mode == XDR_DECODE) {
if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId))
return false;
properties[i].get().id = tmpId;
properties[i].get().value = tmpValue;
}
}
// Code whether the object is a singleton.
uint32_t isSingleton;
if (mode == XDR_ENCODE)
isSingletonTyped = obj->isSingleton() ? 1 : 0;
if (!xdr->codeUint32(&isSingletonTyped))
isSingleton = obj->isSingleton() ? 1 : 0;
if (!xdr->codeUint32(&isSingleton))
return false;
if (mode == XDR_DECODE) {
if (isSingletonTyped) {
if (!JSObject::setSingleton(cx, obj))
return false;
} else if (isArray) {
ObjectGroup::fixArrayGroup(cx, &obj->as<ArrayObject>());
} else {
ObjectGroup::fixPlainObjectGroup(cx, &obj->as<PlainObject>());
}
}
{
uint32_t frozen;
bool extensible;
if (mode == XDR_ENCODE) {
if (!IsExtensible(cx, obj, &extensible))
return false;
frozen = extensible ? 0 : 1;
}
if (!xdr->codeUint32(&frozen))
NewObjectKind newKind = isSingleton ? SingletonObject : TenuredObject;
obj.set(ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind));
if (!obj)
return false;
if (mode == XDR_DECODE && frozen == 1) {
if (!FreezeObject(cx, obj))
return false;
}
}
if (isArray) {
uint32_t copyOnWrite;
if (mode == XDR_ENCODE)
copyOnWrite = obj->denseElementsAreCopyOnWrite();
if (!xdr->codeUint32(&copyOnWrite))
return false;
if (mode == XDR_DECODE && copyOnWrite) {
if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj))
return false;
}
}
return true;
}
template bool
js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleNativeObject obj);
js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
template bool
js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleNativeObject obj);
js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
JSObject *
js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)

View File

@ -1117,36 +1117,6 @@ extern const char js_lookupSetter_str[];
namespace js {
/*
* The NewObjectKind allows an allocation site to specify the type properties
* and lifetime requirements that must be fixed at allocation time.
*/
enum NewObjectKind {
/* This is the default. Most objects are generic. */
GenericObject,
/*
* Singleton objects are treated specially by the type system. This flag
* ensures that the new object is automatically set up correctly as a
* singleton and is allocated in the correct heap.
*/
SingletonObject,
/*
* Objects which may be marked as a singleton after allocation must still
* be allocated on the correct heap, but are not automatically setup as a
* singleton after allocation.
*/
MaybeSingletonObject,
/*
* Objects which will not benefit from being allocated in the nursery
* (e.g. because they are known to have a long lifetime) may be allocated
* with this kind to place them immediately into the tenured generation.
*/
TenuredObject
};
inline gc::InitialHeap
GetInitialHeap(NewObjectKind newKind, const Class *clasp)
{
@ -1174,8 +1144,8 @@ CreateThis(JSContext *cx, const js::Class *clasp, js::HandleObject callee);
extern JSObject *
CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto);
extern NativeObject *
DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind = GenericObject);
extern JSObject *
DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject);
extern bool
DefineProperties(JSContext *cx, HandleObject obj, HandleObject props);
@ -1271,7 +1241,7 @@ ToObjectFromStack(JSContext *cx, HandleValue vp)
template<XDRMode mode>
bool
XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj);
XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj);
extern JSObject *
CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj);

View File

@ -739,12 +739,12 @@ bool
NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj);
JSObject *
NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
gc::AllocKind allocKind, NewObjectKind newKind);
template <typename T>
inline T *
NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectWithGroup(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
{
JSObject *obj = NewObjectWithGroupCommon(cx, group, parent, allocKind, newKind);
@ -753,7 +753,7 @@ NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent,
template <typename T>
inline T *
NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectWithGroup(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectKind newKind = GenericObject)
{
gc::AllocKind allocKind = gc::GetGCObjectKind(group->clasp());

View File

@ -251,7 +251,8 @@ class JS_PUBLIC_API(AutoGCRooter)
IDARRAY = -6, /* js::AutoIdArray */
DESCVECTOR = -7, /* js::AutoPropDescVector */
VALVECTOR = -10, /* js::AutoValueVector */
IDVECTOR = -13, /* js::AutoIdVector */
IDVECTOR = -11, /* js::AutoIdVector */
IDVALVECTOR = -12, /* js::AutoIdValueVector */
OBJVECTOR = -14, /* js::AutoObjectVector */
STRINGVECTOR =-15, /* js::AutoStringVector */
SCRIPTVECTOR =-16, /* js::AutoScriptVector */

View File

@ -420,9 +420,9 @@ js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp)
vp.set(NullValue());
break;
case SCRIPT_OBJECT: {
RootedNativeObject obj(cx);
RootedObject obj(cx);
if (mode == XDR_ENCODE)
obj = &vp.toObject().as<NativeObject>();
obj = &vp.toObject();
if (!XDRObjectLiteral(xdr, &obj))
return false;
@ -530,7 +530,7 @@ static inline uint32_t
FindScopeObjectIndex(JSScript *script, NestedScopeObject &scope)
{
ObjectArray *objects = script->objects();
HeapPtrNativeObject *vector = objects->vector;
HeapPtrObject *vector = objects->vector;
unsigned length = objects->length;
for (unsigned i = 0; i < length; ++i) {
if (vector[i] == &scope)
@ -902,7 +902,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
* after the enclosing block has been XDR'd.
*/
for (i = 0; i != nobjects; ++i) {
HeapPtrNativeObject *objp = &script->objects()->vector[i];
HeapPtrObject *objp = &script->objects()->vector[i];
XDRClassKind classk;
if (mode == XDR_ENCODE) {
@ -1015,7 +1015,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
case CK_JSObject: {
/* Code object literal. */
RootedNativeObject tmp(cx, *objp);
RootedObject tmp(cx, *objp);
if (!XDRObjectLiteral(xdr, &tmp))
return false;
*objp = tmp;
@ -2483,13 +2483,13 @@ JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t ncon
if (nobjects != 0) {
script->objects()->length = nobjects;
script->objects()->vector = (HeapPtrNativeObject *)cursor;
script->objects()->vector = (HeapPtrObject *)cursor;
cursor += nobjects * sizeof(script->objects()->vector[0]);
}
if (nregexps != 0) {
script->regexps()->length = nregexps;
script->regexps()->vector = (HeapPtrNativeObject *)cursor;
script->regexps()->vector = (HeapPtrObject *)cursor;
cursor += nregexps * sizeof(script->regexps()->vector[0]);
}
@ -2985,7 +2985,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
AutoObjectVector objects(cx);
if (nobjects != 0) {
HeapPtrNativeObject *vector = src->objects()->vector;
HeapPtrObject *vector = src->objects()->vector;
for (unsigned i = 0; i < nobjects; i++) {
RootedObject obj(cx, vector[i]);
RootedObject clone(cx);
@ -3040,7 +3040,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
AutoObjectVector regexps(cx);
for (unsigned i = 0; i < nregexps; i++) {
HeapPtrNativeObject *vector = src->regexps()->vector;
HeapPtrObject *vector = src->regexps()->vector;
for (unsigned i = 0; i < nregexps; i++) {
JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>());
if (!clone || !regexps.append(clone))
@ -3130,13 +3130,13 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
}
if (nobjects != 0) {
HeapPtrNativeObject *vector = Rebase<HeapPtrNativeObject>(dst, src, src->objects()->vector);
HeapPtrObject *vector = Rebase<HeapPtrObject>(dst, src, src->objects()->vector);
dst->objects()->vector = vector;
for (unsigned i = 0; i < nobjects; ++i)
vector[i].init(&objects[i]->as<NativeObject>());
}
if (nregexps != 0) {
HeapPtrNativeObject *vector = Rebase<HeapPtrNativeObject>(dst, src, src->regexps()->vector);
HeapPtrObject *vector = Rebase<HeapPtrObject>(dst, src, src->regexps()->vector);
dst->regexps()->vector = vector;
for (unsigned i = 0; i < nregexps; ++i)
vector[i].init(&regexps[i]->as<NativeObject>());

View File

@ -114,8 +114,8 @@ struct ConstArray {
};
struct ObjectArray {
js::HeapPtrNativeObject *vector; // Array of indexed objects.
uint32_t length; // Count of indexed objects.
js::HeapPtrObject *vector; // Array of indexed objects.
uint32_t length; // Count of indexed objects.
};
struct TryNoteArray {
@ -1551,7 +1551,7 @@ class JSScript : public js::gc::TenuredCell
return getAtom(GET_UINT32_INDEX(pc))->asPropertyName();
}
js::NativeObject *getObject(size_t index) {
JSObject *getObject(size_t index) {
js::ObjectArray *arr = objects();
MOZ_ASSERT(index < arr->length);
return arr->vector[index];
@ -1562,7 +1562,7 @@ class JSScript : public js::gc::TenuredCell
return savedCallerFun() ? 1 : 0;
}
js::NativeObject *getObject(jsbytecode *pc) {
JSObject *getObject(jsbytecode *pc) {
MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
return getObject(GET_UINT32_INDEX(pc));
}

View File

@ -2781,10 +2781,10 @@ END_CASE(JSOP_SYMBOL)
CASE(JSOP_OBJECT)
{
RootedNativeObject &ref = rootNativeObject0;
RootedObject &ref = rootObject0;
ref = script->getObject(REGS.pc);
if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
JSObject *obj = js::DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
JSObject *obj = DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
if (!obj)
goto error;
PUSH_OBJECT(*obj);

View File

@ -577,55 +577,12 @@ JSONParser<CharT>::advanceAfterProperty()
return token(Error);
}
JSObject *
JSONParserBase::createFinishedObject(PropertyVector &properties)
{
/*
* Look for an existing cached group and shape for objects with this set of
* properties.
*/
{
JSObject *obj = ObjectGroup::newPlainObject(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());
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
if (!obj)
return nullptr;
RootedId propid(cx);
RootedValue value(cx);
for (size_t i = 0; i < properties.length(); i++) {
propid = properties[i].id;
value = properties[i].value;
if (!NativeDefineProperty(cx, obj, propid, value, nullptr, nullptr, JSPROP_ENUMERATE))
return nullptr;
}
/*
* Try to assign a new group to the object with type information for its
* properties, and update the initializer object group cache with this
* object's final shape.
*/
ObjectGroup::fixPlainObjectGroup(cx, obj);
return obj;
}
inline bool
JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector &properties)
{
MOZ_ASSERT(&properties == &stack.back().properties());
JSObject *obj = createFinishedObject(properties);
JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), GenericObject);
if (!obj)
return false;

View File

@ -173,8 +173,6 @@ class MOZ_STACK_CLASS JSONParserBase : private JS::AutoGCRooter
friend void AutoGCRooter::trace(JSTracer *trc);
void trace(JSTracer *trc);
JSObject *createFinishedObject(PropertyVector &properties);
JSONParserBase(const JSONParserBase &other) = delete;
void operator=(const JSONParserBase &other) = delete;
};

View File

@ -696,7 +696,7 @@ class NativeObject : public JSObject
bool removeProperty(ExclusiveContext *cx, jsid id);
/* Clear the scope, making it empty. */
static void clear(JSContext *cx, HandleNativeObject obj);
static void clear(ExclusiveContext *cx, HandleNativeObject obj);
protected:
/*

View File

@ -848,36 +848,27 @@ ObjectGroup::setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj,
// ObjectGroupCompartment PlainObjectTable
/////////////////////////////////////////////////////////////////////
/*
* 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 ObjectGroupCompartment::PlainObjectKey
{
jsid *properties;
uint32_t nproperties;
uint32_t nfixed;
struct Lookup {
IdValuePair *properties;
uint32_t nproperties;
uint32_t nfixed;
Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed)
: properties(properties), nproperties(nproperties), nfixed(nfixed)
Lookup(IdValuePair *properties, uint32_t nproperties)
: properties(properties), nproperties(nproperties)
{}
};
static inline HashNumber hash(const Lookup &lookup) {
return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
lookup.nproperties ^
lookup.nfixed);
lookup.nproperties);
}
static inline bool match(const PlainObjectKey &v, const Lookup &lookup) {
if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
if (lookup.nproperties != v.nproperties)
return false;
for (size_t i = 0; i < lookup.nproperties; i++) {
if (lookup.properties[i].id != v.properties[i])
@ -894,37 +885,54 @@ struct ObjectGroupCompartment::PlainObjectEntry
TypeSet::Type *types;
};
/* static */ void
ObjectGroupCompartment::updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry,
IdValuePair *properties, size_t nproperties)
static bool
CanShareObjectGroup(IdValuePair *properties, size_t nproperties)
{
if (entry.group->unknownProperties())
return;
// Don't reuse groups for objects containing indexed properties, which
// might end up as dense elements.
for (size_t i = 0; i < nproperties; i++) {
TypeSet::Type type = entry.types[i];
TypeSet::Type ntype = GetValueTypeForTable(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] = TypeSet::DoubleType();
}
AddTypePropertyId(cx, entry.group, nullptr, IdToTypeId(properties[i].id), ntype);
}
uint32_t index;
if (IdIsIndex(properties[i].id, &index))
return false;
}
return true;
}
/* static */ void
ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj)
static bool
AddPlainObjectProperties(ExclusiveContext *cx, HandlePlainObject obj,
IdValuePair *properties, size_t nproperties)
{
AutoEnterAnalysis enter(cx);
RootedId propid(cx);
RootedValue value(cx);
for (size_t i = 0; i < nproperties; i++) {
propid = properties[i].id;
value = properties[i].value;
if (!NativeDefineProperty(cx, obj, propid, value, nullptr, nullptr, JSPROP_ENUMERATE))
return false;
}
return true;
}
PlainObject *
js::NewPlainObjectWithProperties(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties,
NewObjectKind newKind)
{
gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
return nullptr;
return obj;
}
/* static */ JSObject *
ObjectGroup::newPlainObject(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties,
NewObjectKind newKind)
{
// Watch for simple cases where we don't try to reuse plain object groups.
if (newKind == SingletonObject || nproperties == 0 || nproperties >= PropertyTree::MAX_HEIGHT)
return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
ObjectGroupCompartment::PlainObjectTable *&table =
cx->compartment()->objectGroups.plainObjectTable;
@ -934,147 +942,144 @@ ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj)
if (!table || !table->init()) {
js_delete(table);
table = nullptr;
return;
return nullptr;
}
}
/*
* Use the same group for all singleton/JSON objects with the same
* base shape, i.e. the same fields written in the same order.
*
* Exclude some objects we can't readily associate common types for based on their
* shape. Objects with metadata are excluded so that the metadata does not need to
* be included in the table lookup (the metadata object might be in the nursery).
*/
if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata())
return;
ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties);
ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookup(lookup);
Vector<IdValuePair> properties(cx);
if (!properties.resize(obj->slotSpan()))
return;
if (!p) {
if (!CanShareObjectGroup(properties, nproperties))
return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
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();
}
RootedObject proto(cx);
if (!GetBuiltinPrototype(cx, JSProto_Object, &proto))
return nullptr;
ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties.begin(), properties.length(),
obj->numFixedSlots());
ObjectGroupCompartment::PlainObjectTable::AddPtr p = table->lookupForAdd(lookup);
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_,
tagged));
if (!group)
return nullptr;
if (p) {
MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject());
MOZ_ASSERT(obj->lastProperty() == p->value().shape);
gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group, cx->global(),
allocKind, TenuredObject));
if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
return nullptr;
ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(),
properties.begin(),
properties.length());
obj->setGroup(p->value().group);
return;
}
// Don't make entries with duplicate property names, which will show up
// here as objects with fewer properties than we thought we were
// adding to the object. In this case, reset the object's group to the
// default (which will have unknown properties) so that the group we
// just created will be collected by the GC.
if (obj->slotSpan() != nproperties) {
ObjectGroup *group = defaultNewGroup(cx, obj->getClass(), obj->getTaggedProto());
if (!group)
return nullptr;
obj->setGroup(group);
return obj;
}
/* Make a new type to use for the object and similar future ones. */
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, objProto);
if (!group || !group->addDefiniteProperties(cx, obj->lastProperty()))
return;
// Keep track of the initial objects we create with this type.
// If the initial ones have a consistent shape and property types, we
// will try to use an unboxed layout for the group.
PreliminaryObjectArrayWithTemplate *preliminaryObjects =
cx->new_<PreliminaryObjectArrayWithTemplate>(obj->lastProperty());
if (!preliminaryObjects)
return nullptr;
group->setPreliminaryObjects(preliminaryObjects);
preliminaryObjects->registerNewObject(obj);
if (obj->isIndexed())
group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(nproperties));
if (!ids)
return nullptr;
ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(properties.length()));
if (!ids)
return;
ScopedJSFreePtr<TypeSet::Type> types(
group->zone()->pod_calloc<TypeSet::Type>(nproperties));
if (!types)
return nullptr;
ScopedJSFreePtr<TypeSet::Type> types(
group->zone()->pod_calloc<TypeSet::Type>(properties.length()));
if (!types)
return;
for (size_t i = 0; i < nproperties; i++) {
ids[i] = properties[i].id;
types[i] = GetValueTypeForTable(obj->getSlot(i));
AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
}
for (size_t i = 0; i < properties.length(); i++) {
ids[i] = properties[i].id;
types[i] = GetValueTypeForTable(obj->getSlot(i));
AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
}
ObjectGroupCompartment::PlainObjectKey key;
key.properties = ids;
key.nproperties = nproperties;
MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
ObjectGroupCompartment::PlainObjectKey key;
key.properties = ids;
key.nproperties = properties.length();
key.nfixed = obj->numFixedSlots();
MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
ObjectGroupCompartment::PlainObjectEntry entry;
entry.group.set(group);
entry.shape.set(obj->lastProperty());
entry.types = types;
ObjectGroupCompartment::PlainObjectEntry entry;
entry.group.set(group);
entry.shape.set(obj->lastProperty());
entry.types = types;
ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
if (!table->add(np, key, entry))
return nullptr;
obj->setGroup(group);
p = table->lookupForAdd(lookup);
if (table->add(p, key, entry)) {
ids.forget();
types.forget();
return obj;
}
}
/* static */ PlainObject *
ObjectGroup::newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
{
AutoEnterAnalysis enter(cx);
RootedObjectGroup group(cx, p->value().group);
ObjectGroupCompartment::PlainObjectTable *table =
cx->compartment()->objectGroups.plainObjectTable;
// Watch for existing groups which now use an unboxed layout.
if (group->maybeUnboxedLayout()) {
MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
}
if (!table)
return nullptr;
// Update property types according to the properties we are about to add.
// Do this before we do anything which can GC, which might move or remove
// this table entry.
if (!group->unknownProperties()) {
for (size_t i = 0; i < nproperties; i++) {
TypeSet::Type type = p->value().types[i];
TypeSet::Type ntype = GetValueTypeForTable(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.
p->value().types[i] = TypeSet::DoubleType();
}
AddTypePropertyId(cx, group, nullptr, IdToTypeId(properties[i].id), ntype);
}
}
}
/*
* Use the object group table to allocate an object with the specified
* properties, filling in its final group and shape and failing if no table
* entry could be found for the properties.
*/
RootedShape shape(cx, p->value().shape);
/*
* Filter out a few cases where we don't want to use the object group 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 group table --- fixObjectGroup 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 nullptr;
if (group->maybePreliminaryObjects())
newKind = TenuredObject;
gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_);
RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group, cx->global(), allocKind,
newKind));
ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties, nfixed);
ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookupForAdd(lookup);
if (!p)
if (!obj->setLastProperty(cx, shape))
return nullptr;
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
if (!obj) {
cx->clearPendingException();
return nullptr;
}
MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject());
if (!obj->setLastProperty(cx, p->value().shape)) {
cx->clearPendingException();
return nullptr;
}
ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), properties, nproperties);
for (size_t i = 0; i < nproperties; i++)
obj->setSlot(i, properties[i].value);
obj->setGroup(p->value().group);
if (group->maybePreliminaryObjects()) {
group->maybePreliminaryObjects()->registerNewObject(obj);
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
}
return obj;
}
@ -1148,7 +1153,7 @@ ObjectGroup::allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc
if (JSOp(*pc) == JSOP_NEWOBJECT) {
// Keep track of the preliminary objects with this group, so we can try
// to use an unboxed layout for the object once some are allocated.
Shape *shape = script->getObject(pc)->lastProperty();
Shape *shape = script->getObject(pc)->as<PlainObject>().lastProperty();
if (!shape->isEmptyShape()) {
PreliminaryObjectArrayWithTemplate *preliminaryObjects =
cx->new_<PreliminaryObjectArrayWithTemplate>(shape);

View File

@ -131,6 +131,36 @@ namespace gc {
void MergeCompartments(JSCompartment *source, JSCompartment *target);
}
/*
* The NewObjectKind allows an allocation site to specify the type properties
* and lifetime requirements that must be fixed at allocation time.
*/
enum NewObjectKind {
/* This is the default. Most objects are generic. */
GenericObject,
/*
* Singleton objects are treated specially by the type system. This flag
* ensures that the new object is automatically set up correctly as a
* singleton and is allocated in the correct heap.
*/
SingletonObject,
/*
* Objects which may be marked as a singleton after allocation must still
* be allocated on the correct heap, but are not automatically setup as a
* singleton after allocation.
*/
MaybeSingletonObject,
/*
* Objects which will not benefit from being allocated in the nursery
* (e.g. because they are known to have a long lifetime) may be allocated
* with this kind to place them immediately into the tenured generation.
*/
TenuredObject
};
/*
* Lazy object groups overview.
*
@ -558,6 +588,10 @@ class ObjectGroup : public gc::TenuredCell
return offsetof(ObjectGroup, flags_);
}
const ObjectGroupFlags *addressOfFlags() const {
return &flags_;
}
// Get the bit pattern stored in an object's addendum when it has an
// original unboxed group.
static inline int32_t addendumOriginalUnboxedGroupValue() {
@ -602,15 +636,17 @@ class ObjectGroup : public gc::TenuredCell
// Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable.
// Update the group of a freshly created array or plain object according to
// Update the group of a freshly created array according to
// the object's current contents.
static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj);
static void fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj);
// Update the group of a freshly created 'rest' arguments object.
static void fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj);
static PlainObject *newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
// Create a PlainObject or UnboxedPlainObject with the specified properties.
static JSObject *newPlainObject(ExclusiveContext *cx,
IdValuePair *properties, size_t nproperties,
NewObjectKind newKind);
// Static accessors for ObjectGroupCompartment AllocationSiteTable.
@ -732,10 +768,12 @@ class ObjectGroupCompartment
static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table,
const Class *clasp, TaggedProto proto, JSObject *associated);
static void updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry,
IdValuePair *properties, size_t nproperties);
};
PlainObject *
NewPlainObjectWithProperties(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties,
NewObjectKind newKind);
} // namespace js
#endif /* vm_ObjectGroup_h */

View File

@ -1022,7 +1022,7 @@ NativeObject::removeProperty(ExclusiveContext *cx, jsid id_)
}
/* static */ void
NativeObject::clear(JSContext *cx, HandleNativeObject obj)
NativeObject::clear(ExclusiveContext *cx, HandleNativeObject obj)
{
Shape *shape = obj->lastProperty();
MOZ_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
@ -1038,7 +1038,8 @@ NativeObject::clear(JSContext *cx, HandleNativeObject obj)
JS_ALWAYS_TRUE(obj->setLastProperty(cx, shape));
++cx->runtime()->propertyRemovals;
if (cx->isJSContext())
++cx->asJSContext()->runtime()->propertyRemovals;
obj->checkShapeConsistency();
}

View File

@ -3361,7 +3361,7 @@ CommonPrefix(Shape *first, Shape *second)
}
void
PreliminaryObjectArrayWithTemplate::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool force)
PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext *cx, ObjectGroup *group, bool force)
{
// Don't perform the analyses until sufficient preliminary objects have
// been allocated.

View File

@ -807,7 +807,7 @@ class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray
return shape_;
}
void maybeAnalyze(JSContext *cx, ObjectGroup *group, bool force = false);
void maybeAnalyze(ExclusiveContext *cx, ObjectGroup *group, bool force = false);
void trace(JSTracer *trc);

View File

@ -6,6 +6,9 @@
#include "vm/UnboxedObject.h"
#include "jit/JitCommon.h"
#include "jit/Linker.h"
#include "jsobjinlines.h"
#include "vm/Shape-inl.h"
@ -38,6 +41,9 @@ UnboxedLayout::trace(JSTracer *trc)
if (replacementNewGroup_)
MarkObjectGroup(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup");
if (constructorCode_)
MarkJitCode(trc, &constructorCode_, "unboxed_layout_constructorCode");
}
size_t
@ -57,6 +63,190 @@ UnboxedLayout::setNewScript(TypeNewScript *newScript, bool writeBarrier /* = tru
newScript_ = newScript;
}
// Constructor code returns a 0x1 value to indicate the constructor code should
// be cleared.
static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
/* static */ bool
UnboxedLayout::makeConstructorCode(JSContext *cx, HandleObjectGroup group)
{
using namespace jit;
UnboxedLayout &layout = group->unboxedLayout();
MOZ_ASSERT(!layout.constructorCode());
UnboxedPlainObject *templateObject = UnboxedPlainObject::create(cx, group, TenuredObject);
if (!templateObject)
return false;
JitContext jitContext(cx, nullptr);
MacroAssembler masm;
Register propertiesReg, newKindReg;
#ifdef JS_CODEGEN_X86
propertiesReg = eax;
newKindReg = ecx;
masm.loadPtr(Address(StackPointer, sizeof(void*)), propertiesReg);
masm.loadPtr(Address(StackPointer, 2 * sizeof(void*)), newKindReg);
#else
propertiesReg = IntArgReg0;
newKindReg = IntArgReg1;
#endif
MOZ_ASSERT(propertiesReg.volatile_());
MOZ_ASSERT(newKindReg.volatile_());
GeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(propertiesReg);
regs.take(newKindReg);
Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
GeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
masm.Push(*iter);
Label failure, tenuredObject, allocated;
masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject);
masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()),
Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject);
// Allocate an object in the nursery
masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure,
/* initFixedSlots = */ false);
masm.jump(&allocated);
masm.bind(&tenuredObject);
// Allocate an object in the tenured heap.
masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure,
/* initFixedSlots = */ false);
// If any of the properties being stored are in the nursery, add a store
// buffer entry for the new object.
Label postBarrier;
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property &property = layout.properties()[i];
if (property.type == JSVAL_TYPE_OBJECT) {
Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
Label notObject;
masm.branchTestObject(Assembler::NotEqual, valueAddress, &notObject);
Register valueObject = masm.extractObject(valueAddress, scratch1);
masm.branchPtrInNurseryRange(Assembler::Equal, valueObject, scratch2, &postBarrier);
masm.bind(&notObject);
}
}
masm.jump(&allocated);
masm.bind(&postBarrier);
masm.mov(ImmPtr(cx->runtime()), scratch1);
masm.setupUnalignedABICall(2, scratch2);
masm.passABIArg(scratch1);
masm.passABIArg(object);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier));
masm.bind(&allocated);
ValueOperand valueOperand;
#ifdef JS_NUNBOX32
valueOperand = ValueOperand(scratch1, scratch2);
#else
valueOperand = ValueOperand(scratch1);
#endif
Label failureStoreOther, failureStoreObject;
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property &property = layout.properties()[i];
Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset);
masm.loadValue(valueAddress, valueOperand);
if (property.type == JSVAL_TYPE_OBJECT) {
HeapTypeSet *types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
Label notObject;
masm.branchTestObject(Assembler::NotEqual, valueOperand,
types->mightBeMIRType(MIRType_Null) ? &notObject : &failureStoreObject);
Register payloadReg = masm.extractObject(valueOperand, scratch1);
if (!types->hasType(TypeSet::AnyObjectType()))
masm.guardObjectType(payloadReg, types, scratch2, &failureStoreObject);
masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
TypedOrValueRegister(MIRType_Object,
AnyRegister(payloadReg)), nullptr);
if (notObject.used()) {
Label done;
masm.jump(&done);
masm.bind(&notObject);
masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr);
masm.bind(&done);
}
} else {
masm.storeUnboxedProperty(targetAddress, property.type,
ConstantOrRegister(valueOperand), &failureStoreOther);
}
}
Label done;
masm.bind(&done);
if (object != ReturnReg)
masm.movePtr(object, ReturnReg);
// Restore non-volatile registers which were saved on entry.
for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
masm.Pop(*iter);
masm.ret();
masm.bind(&failureStoreOther);
// There was a failure while storing a value which cannot be stored at all
// in the unboxed object. Initialize the object so it is safe for GC and
// return null.
masm.initUnboxedObjectContents(object, templateObject);
masm.bind(&failure);
masm.movePtr(ImmWord(0), object);
masm.jump(&done);
masm.bind(&failureStoreObject);
// There was a failure while storing a value to an object slot of the
// unboxed object. If the value is storable, the failure occurred due to
// incomplete type information in the object, so return a token to trigger
// regeneration of the jitcode after a new object is created in the VM.
{
Label isObject;
masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
masm.bind(&isObject);
}
// Initialize the object so it is safe for GC.
masm.initUnboxedObjectContents(object, templateObject);
masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
masm.jump(&done);
Linker linker(masm);
AutoFlushICache afc("RegExp");
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
if (!code)
return false;
layout.setConstructorCode(code);
return true;
}
void
UnboxedLayout::detachFromCompartment()
{
@ -68,7 +258,8 @@ UnboxedLayout::detachFromCompartment()
/////////////////////////////////////////////////////////////////////
bool
UnboxedPlainObject::setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v)
UnboxedPlainObject::setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property,
const Value &v)
{
uint8_t *p = &data_[property.offset];
@ -114,7 +305,7 @@ UnboxedPlainObject::setValue(JSContext *cx, const UnboxedLayout::Property &prope
// object is converted to its native representation.
JSObject *obj = v.toObjectOrNull();
if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(this))
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this);
cx->asJSContext()->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this);
*reinterpret_cast<PreBarrieredObject*>(p) = obj;
return true;
@ -307,7 +498,7 @@ UnboxedPlainObject::convertToNative(JSContext *cx, JSObject *obj)
/* static */
UnboxedPlainObject *
UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind)
UnboxedPlainObject::create(ExclusiveContext *cx, HandleObjectGroup group, NewObjectKind newKind)
{
MOZ_ASSERT(group->clasp() == &class_);
gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
@ -343,6 +534,52 @@ UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind
return res;
}
/* static */ JSObject *
UnboxedPlainObject::createWithProperties(ExclusiveContext *cx, HandleObjectGroup group,
NewObjectKind newKind, IdValuePair *properties)
{
MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
UnboxedLayout &layout = group->unboxedLayout();
if (layout.constructorCode()) {
MOZ_ASSERT(cx->isJSContext());
typedef JSObject *(*ConstructorCodeSignature)(IdValuePair *, NewObjectKind);
ConstructorCodeSignature function =
reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
JSObject *obj;
{
JS::AutoSuppressGCAnalysis nogc;
obj = reinterpret_cast<JSObject *>(CALL_GENERATED_2(function, properties, newKind));
}
if (obj > reinterpret_cast<JSObject *>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
return obj;
if (obj == reinterpret_cast<JSObject *>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
layout.setConstructorCode(nullptr);
}
UnboxedPlainObject *obj = UnboxedPlainObject::create(cx, group, newKind);
if (!obj)
return nullptr;
for (size_t i = 0; i < layout.properties().length(); i++) {
if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
}
#ifndef JS_CODEGEN_NONE
if (cx->isJSContext() && !layout.constructorCode()) {
if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
return nullptr;
}
#endif
return obj;
}
/* static */ bool
UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
@ -547,10 +784,13 @@ PropertiesAreSuperset(const UnboxedLayout::PropertyVector &properties, UnboxedLa
}
bool
js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
js::TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
ObjectGroup *group, PreliminaryObjectArray *objects)
{
if (!cx->runtime()->options().unboxedObjects())
if (!templateShape->runtimeFromAnyThread()->options().unboxedObjects())
return true;
if (templateShape->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
return true;
if (templateShape->slotSpan() == 0)
@ -727,7 +967,7 @@ js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
templateShape->getObjectMetadata(),
templateShape->getObjectFlags());
if (!newShape) {
cx->clearPendingException();
cx->recoverFromOutOfMemory();
return false;
}

View File

@ -79,10 +79,16 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
// kind from this group.
HeapPtrObjectGroup replacementNewGroup_;
// If this layout has been used to construct script or JSON constant
// objects, this code might be filled in to more quickly fill in objects
// from an array of values.
HeapPtrJitCode constructorCode_;
public:
UnboxedLayout(const PropertyVector &properties, size_t size)
: size_(size), newScript_(nullptr), traceList_(nullptr),
nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr)
nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr),
constructorCode_(nullptr)
{
properties_.appendAll(properties);
}
@ -138,6 +144,14 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
return nativeShape_;
}
jit::JitCode *constructorCode() const {
return constructorCode_;
}
void setConstructorCode(jit::JitCode *code) {
constructorCode_ = code;
}
inline gc::AllocKind getAllocKind() const;
void trace(JSTracer *trc);
@ -145,6 +159,7 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
static bool makeNativeGroup(JSContext *cx, ObjectGroup *group);
static bool makeConstructorCode(JSContext *cx, HandleObjectGroup group);
};
// Class for a plain object using an unboxed representation. The physical
@ -199,11 +214,14 @@ class UnboxedPlainObject : public JSObject
return &data_[0];
}
bool setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v);
bool setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property, const Value &v);
Value getValue(const UnboxedLayout::Property &property);
static bool convertToNative(JSContext *cx, JSObject *obj);
static UnboxedPlainObject *create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind);
static UnboxedPlainObject *create(ExclusiveContext *cx, HandleObjectGroup group,
NewObjectKind newKind);
static JSObject *createWithProperties(ExclusiveContext *cx, HandleObjectGroup group,
NewObjectKind newKind, IdValuePair *properties);
static void trace(JSTracer *trc, JSObject *object);
@ -216,7 +234,7 @@ class UnboxedPlainObject : public JSObject
// provided they all match the template shape. If successful, converts the
// preliminary objects and their group to the new unboxed representation.
bool
TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
ObjectGroup *group, PreliminaryObjectArray *objects);
inline gc::AllocKind

View File

@ -29,7 +29,7 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 258;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 259;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);