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 #define js_Id_h
// A jsid is an identifier for a property or method of an object which is // 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 // 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 // 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 // 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. // 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. // JS_IdToValue must be used instead.
#include "jstypes.h" #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. // Call the per-exported-function trampoline created by GenerateEntry.
AsmJSModule::CodePtr enter = module.entryTrampoline(func); 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; return false;
} }

View File

@ -18,10 +18,28 @@ struct IdValuePair
jsid id; jsid id;
Value value; Value value;
IdValuePair() {} IdValuePair()
: id(JSID_EMPTY), value(UndefinedValue())
{}
explicit IdValuePair(jsid idArg) explicit IdValuePair(jsid idArg)
: id(idArg), value(UndefinedValue()) : 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 */ } /* namespace js */

View File

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

View File

@ -168,6 +168,15 @@ AutoGCRooter::trace(JSTracer *trc)
return; 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: { case SHAPEVECTOR: {
AutoShapeVector::VectorImpl &vector = static_cast<js::AutoShapeVector *>(this)->vector; AutoShapeVector::VectorImpl &vector = static_cast<js::AutoShapeVector *>(this)->vector;
MarkShapeRootRange(trc, vector.length(), const_cast<Shape **>(vector.begin()), MarkShapeRootRange(trc, vector.length(), const_cast<Shape **>(vector.begin()),

View File

@ -89,19 +89,7 @@ NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(LifoAlloc *alloc, RegExpS
temp1.name(), temp1.name(),
temp2.name()); temp2.name());
// Determine the non-volatile registers which might be modified by jitcode. savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
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
masm.jump(&entry_label_); masm.jump(&entry_label_);
masm.bind(&start_label_); masm.bind(&start_label_);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,7 @@
#include "jit/VMFunctions.h" #include "jit/VMFunctions.h"
#include "vm/ProxyObject.h" #include "vm/ProxyObject.h"
#include "vm/Shape.h" #include "vm/Shape.h"
#include "vm/UnboxedObject.h"
#ifdef IS_LITTLE_ENDIAN #ifdef IS_LITTLE_ENDIAN
#define IMM32_16ADJ(X) X << 16 #define IMM32_16ADJ(X) X << 16
@ -829,18 +830,20 @@ class MacroAssembler : public MacroAssemblerSpecific
const Value &v); const Value &v);
void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end); 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 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: public:
void callMallocStub(size_t nbytes, Register result, Label *fail); void callMallocStub(size_t nbytes, Register result, Label *fail);
void callFreeStub(Register slots); void callFreeStub(Register slots);
void createGCObject(Register result, Register temp, JSObject *templateObj, 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, void newGCThing(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail); gc::InitialHeap initialHeap, Label *fail);
void initGCThing(Register obj, Register temp, JSObject *templateObj, 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 newGCString(Register result, Register temp, Label *fail);
void newGCFatInlineString(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()); } 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 jit
} // namespace js } // namespace js

View File

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

View File

@ -63,7 +63,7 @@ ValueToIdPure(const Value &v, jsid *id)
template <AllowGC allowGC> template <AllowGC allowGC>
inline bool 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) typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
{ {
int32_t i; int32_t i;

View File

@ -1432,7 +1432,7 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp,
} }
static bool static bool
NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObject parent, NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectKind newKind) NewObjectKind newKind)
{ {
return group->proto().isObject() && return group->proto().isObject() &&
@ -1440,7 +1440,8 @@ NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObjec
newKind == GenericObject && newKind == GenericObject &&
group->clasp()->isNative() && group->clasp()->isNative() &&
(!group->newScript() || group->newScript()->analyzed()) && (!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'. * avoid losing creation site information for objects made by scripted 'new'.
*/ */
JSObject * JSObject *
js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObject parent, js::NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
gc::AllocKind allocKind, NewObjectKind newKind) gc::AllocKind allocKind, NewObjectKind newKind)
{ {
MOZ_ASSERT(parent); MOZ_ASSERT(parent);
@ -1459,10 +1460,10 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
bool isCachable = NewObjectWithGroupIsCachable(cx, group, parent, newKind); bool isCachable = NewObjectWithGroupIsCachable(cx, group, parent, newKind);
if (isCachable) { if (isCachable) {
NewObjectCache &cache = cx->runtime()->newObjectCache; NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1; NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGroup(group, allocKind, &entry)) { if (cache.lookupGroup(group, allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, JSObject *obj = cache.newObjectFromHit(cx->asJSContext(), entry,
GetInitialHeap(newKind, group->clasp())); GetInitialHeap(newKind, group->clasp()));
if (obj) if (obj)
return obj; return obj;
@ -1474,7 +1475,7 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
return nullptr; return nullptr;
if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) { if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
NewObjectCache &cache = cx->runtime()->newObjectCache; NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1; NewObjectCache::EntryIndex entry = -1;
cache.lookupGroup(group, allocKind, &entry); cache.lookupGroup(group, allocKind, &entry);
cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>()); cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
@ -1779,90 +1780,162 @@ js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto)
return clone; return clone;
} }
NativeObject * static bool
js::DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind) 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. */ /* NB: Keep this in sync with XDRObjectLiteral. */
MOZ_ASSERT_IF(obj->isSingleton(), MOZ_ASSERT_IF(obj->isSingleton(),
JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>()); MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<ArrayObject>());
MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
// Result of the clone function. MOZ_ASSERT(newKind != SingletonObject);
RootedNativeObject clone(cx);
// Temporary element/slot which would be stored in the cloned object.
RootedValue v(cx);
RootedNativeObject deepObj(cx);
if (obj->is<ArrayObject>()) { if (obj->is<ArrayObject>()) {
clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), NullPtr(), newKind); HandleArrayObject aobj = obj.as<ArrayObject>();
} else {
// Object literals are tenured by default as holded by the JSScript. AutoValueVector values(cx);
MOZ_ASSERT(obj->isTenured()); if (!GetScriptArrayObjectElements(cx, aobj, values))
AllocKind kind = obj->asTenured().getAllocKind();
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
return nullptr; return nullptr;
RootedObject proto(cx, group->proto().toObject());
obj->assertParentIs(cx->global()); // Deep clone any elements.
clone = NewNativeObjectWithGivenProto(cx, &PlainObject::class_, proto, uint32_t initialized = aobj->getDenseInitializedLength();
kind, newKind); 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. AutoIdValueVector properties(cx);
if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity())) if (!GetScriptPlainObjectProperties(cx, obj, properties))
return nullptr; return nullptr;
// Recursive copy of dense element. for (size_t i = 0; i < properties.length(); i++) {
uint32_t initialized = obj->getDenseInitializedLength(); if (!DeepCloneValue(cx, &properties[i].get().value, newKind))
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))
return nullptr; return nullptr;
} }
return clone; if (obj->isSingleton())
newKind = SingletonObject;
return ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind);
} }
static bool static bool
@ -1919,7 +1992,7 @@ JS_InitializePropertiesFromCompatibleNativeObject(JSContext *cx,
template<XDRMode mode> template<XDRMode mode>
bool bool
js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj) js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
{ {
/* NB: Keep this in sync with DeepCloneObjectLiteral. */ /* NB: Keep this in sync with DeepCloneObjectLiteral. */
@ -1931,231 +2004,139 @@ js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj)
uint32_t isArray = 0; uint32_t isArray = 0;
{ {
if (mode == XDR_ENCODE) { if (mode == XDR_ENCODE) {
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>()); MOZ_ASSERT(obj->is<PlainObject>() ||
isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0; obj->is<UnboxedPlainObject>() ||
obj->is<ArrayObject>());
isArray = obj->is<ArrayObject>() ? 1 : 0;
} }
if (!xdr->codeUint32(&isArray)) if (!xdr->codeUint32(&isArray))
return false; return false;
} }
RootedValue tmpValue(cx), tmpIdValue(cx);
RootedId tmpId(cx);
if (isArray) { if (isArray) {
uint32_t length; uint32_t length;
RootedArrayObject aobj(cx);
if (mode == XDR_ENCODE) if (mode == XDR_ENCODE) {
length = obj->as<ArrayObject>().length(); aobj = &obj->as<ArrayObject>();
length = aobj->length();
}
if (!xdr->codeUint32(&length)) if (!xdr->codeUint32(&length))
return false; return false;
if (mode == XDR_DECODE) if (mode == XDR_DECODE) {
obj.set(NewDenseUnallocatedArray(cx, length, NullPtr(), js::MaybeSingletonObject)); obj.set(NewDenseUnallocatedArray(cx, length, NullPtr(), TenuredObject));
if (!obj)
return false;
aobj = &obj->as<ArrayObject>();
}
} else { AutoValueVector values(cx);
// Code the alloc kind of the object. if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, aobj, values))
AllocKind kind; return nullptr;
uint32_t initialized;
{ {
if (mode == XDR_ENCODE) { if (mode == XDR_ENCODE)
MOZ_ASSERT(obj->is<PlainObject>()); initialized = values.length();
MOZ_ASSERT(obj->isTenured()); if (!xdr->codeUint32(&initialized))
kind = obj->asTenured().getAllocKind();
}
if (!xdr->codeEnum32(&kind))
return false; return false;
if (mode == XDR_DECODE) { if (mode == XDR_DECODE) {
obj.set(NewBuiltinClassInstance<PlainObject>(cx, kind, MaybeSingletonObject)); if (initialized) {
if (!obj) if (!aobj->ensureElements(cx, initialized))
return false; return false;
}
} }
} }
}
{ // Recursively copy dense elements.
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.
{
for (unsigned i = 0; i < initialized; i++) { for (unsigned i = 0; i < initialized; i++) {
if (mode == XDR_ENCODE) if (mode == XDR_ENCODE)
tmpValue = obj->getDenseElement(i); tmpValue = values[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));
}
if (!xdr->codeConstValue(&tmpValue)) if (!xdr->codeConstValue(&tmpValue))
return false; return false;
if (mode == XDR_DECODE) { if (mode == XDR_DECODE) {
if (!NativeDefineProperty(cx, obj, id, tmpValue, nullptr, nullptr, aobj->setDenseInitializedLength(i + 1);
JSPROP_ENUMERATE)) aobj->initDenseElement(i, tmpValue);
{
return false;
}
} }
} }
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. // Code the properties in the object.
uint32_t isSingletonTyped; 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) if (mode == XDR_ENCODE)
isSingletonTyped = obj->isSingleton() ? 1 : 0; isSingleton = obj->isSingleton() ? 1 : 0;
if (!xdr->codeUint32(&isSingletonTyped)) if (!xdr->codeUint32(&isSingleton))
return false; return false;
if (mode == XDR_DECODE) { if (mode == XDR_DECODE) {
if (isSingletonTyped) { NewObjectKind newKind = isSingleton ? SingletonObject : TenuredObject;
if (!JSObject::setSingleton(cx, obj)) obj.set(ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind));
return false; if (!obj)
} 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))
return false; 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; return true;
} }
template bool template bool
js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleNativeObject obj); js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
template bool template bool
js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleNativeObject obj); js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
JSObject * JSObject *
js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj) js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)

View File

@ -1117,36 +1117,6 @@ extern const char js_lookupSetter_str[];
namespace js { 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 inline gc::InitialHeap
GetInitialHeap(NewObjectKind newKind, const Class *clasp) GetInitialHeap(NewObjectKind newKind, const Class *clasp)
{ {
@ -1174,8 +1144,8 @@ CreateThis(JSContext *cx, const js::Class *clasp, js::HandleObject callee);
extern JSObject * extern JSObject *
CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto); CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto);
extern NativeObject * extern JSObject *
DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind = GenericObject); DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject);
extern bool extern bool
DefineProperties(JSContext *cx, HandleObject obj, HandleObject props); DefineProperties(JSContext *cx, HandleObject obj, HandleObject props);
@ -1271,7 +1241,7 @@ ToObjectFromStack(JSContext *cx, HandleValue vp)
template<XDRMode mode> template<XDRMode mode>
bool bool
XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj); XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj);
extern JSObject * extern JSObject *
CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj); CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -577,55 +577,12 @@ JSONParser<CharT>::advanceAfterProperty()
return token(Error); 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 inline bool
JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector &properties) JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector &properties)
{ {
MOZ_ASSERT(&properties == &stack.back().properties()); MOZ_ASSERT(&properties == &stack.back().properties());
JSObject *obj = createFinishedObject(properties); JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), GenericObject);
if (!obj) if (!obj)
return false; return false;

View File

@ -173,8 +173,6 @@ class MOZ_STACK_CLASS JSONParserBase : private JS::AutoGCRooter
friend void AutoGCRooter::trace(JSTracer *trc); friend void AutoGCRooter::trace(JSTracer *trc);
void trace(JSTracer *trc); void trace(JSTracer *trc);
JSObject *createFinishedObject(PropertyVector &properties);
JSONParserBase(const JSONParserBase &other) = delete; JSONParserBase(const JSONParserBase &other) = delete;
void operator=(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); bool removeProperty(ExclusiveContext *cx, jsid id);
/* Clear the scope, making it empty. */ /* Clear the scope, making it empty. */
static void clear(JSContext *cx, HandleNativeObject obj); static void clear(ExclusiveContext *cx, HandleNativeObject obj);
protected: protected:
/* /*

View File

@ -848,36 +848,27 @@ ObjectGroup::setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj,
// ObjectGroupCompartment PlainObjectTable // 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 struct ObjectGroupCompartment::PlainObjectKey
{ {
jsid *properties; jsid *properties;
uint32_t nproperties; uint32_t nproperties;
uint32_t nfixed;
struct Lookup { struct Lookup {
IdValuePair *properties; IdValuePair *properties;
uint32_t nproperties; uint32_t nproperties;
uint32_t nfixed;
Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed) Lookup(IdValuePair *properties, uint32_t nproperties)
: properties(properties), nproperties(nproperties), nfixed(nfixed) : properties(properties), nproperties(nproperties)
{} {}
}; };
static inline HashNumber hash(const Lookup &lookup) { static inline HashNumber hash(const Lookup &lookup) {
return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^ return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
lookup.nproperties ^ lookup.nproperties);
lookup.nfixed);
} }
static inline bool match(const PlainObjectKey &v, const Lookup &lookup) { 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; return false;
for (size_t i = 0; i < lookup.nproperties; i++) { for (size_t i = 0; i < lookup.nproperties; i++) {
if (lookup.properties[i].id != v.properties[i]) if (lookup.properties[i].id != v.properties[i])
@ -894,37 +885,54 @@ struct ObjectGroupCompartment::PlainObjectEntry
TypeSet::Type *types; TypeSet::Type *types;
}; };
/* static */ void static bool
ObjectGroupCompartment::updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry, CanShareObjectGroup(IdValuePair *properties, size_t nproperties)
IdValuePair *properties, size_t nproperties)
{ {
if (entry.group->unknownProperties()) // Don't reuse groups for objects containing indexed properties, which
return; // might end up as dense elements.
for (size_t i = 0; i < nproperties; i++) { for (size_t i = 0; i < nproperties; i++) {
TypeSet::Type type = entry.types[i]; uint32_t index;
TypeSet::Type ntype = GetValueTypeForTable(properties[i].value); if (IdIsIndex(properties[i].id, &index))
if (ntype == type) return false;
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);
}
} }
return true;
} }
/* static */ void static bool
ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj) 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 = ObjectGroupCompartment::PlainObjectTable *&table =
cx->compartment()->objectGroups.plainObjectTable; cx->compartment()->objectGroups.plainObjectTable;
@ -934,147 +942,144 @@ ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj)
if (!table || !table->init()) { if (!table || !table->init()) {
js_delete(table); js_delete(table);
table = nullptr; table = nullptr;
return; return nullptr;
} }
} }
/* ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties);
* Use the same group for all singleton/JSON objects with the same ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookup(lookup);
* 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;
Vector<IdValuePair> properties(cx); if (!p) {
if (!properties.resize(obj->slotSpan())) if (!CanShareObjectGroup(properties, nproperties))
return; return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
Shape *shape = obj->lastProperty(); RootedObject proto(cx);
while (!shape->isEmptyShape()) { if (!GetBuiltinPrototype(cx, JSProto_Object, &proto))
IdValuePair &entry = properties[shape->slot()]; return nullptr;
entry.id = shape->propid();
entry.value = obj->getSlot(shape->slot());
shape = shape->previous();
}
ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties.begin(), properties.length(), Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
obj->numFixedSlots()); RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_,
ObjectGroupCompartment::PlainObjectTable::AddPtr p = table->lookupForAdd(lookup); tagged));
if (!group)
return nullptr;
if (p) { gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group, cx->global(),
MOZ_ASSERT(obj->lastProperty() == p->value().shape); allocKind, TenuredObject));
if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
return nullptr;
ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), // Don't make entries with duplicate property names, which will show up
properties.begin(), // here as objects with fewer properties than we thought we were
properties.length()); // adding to the object. In this case, reset the object's group to the
obj->setGroup(p->value().group); // default (which will have unknown properties) so that the group we
return; // 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. */ // Keep track of the initial objects we create with this type.
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto()); // If the initial ones have a consistent shape and property types, we
ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, objProto); // will try to use an unboxed layout for the group.
if (!group || !group->addDefiniteProperties(cx, obj->lastProperty())) PreliminaryObjectArrayWithTemplate *preliminaryObjects =
return; cx->new_<PreliminaryObjectArrayWithTemplate>(obj->lastProperty());
if (!preliminaryObjects)
return nullptr;
group->setPreliminaryObjects(preliminaryObjects);
preliminaryObjects->registerNewObject(obj);
if (obj->isIndexed()) ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(nproperties));
group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); if (!ids)
return nullptr;
ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(properties.length())); ScopedJSFreePtr<TypeSet::Type> types(
if (!ids) group->zone()->pod_calloc<TypeSet::Type>(nproperties));
return; if (!types)
return nullptr;
ScopedJSFreePtr<TypeSet::Type> types( for (size_t i = 0; i < nproperties; i++) {
group->zone()->pod_calloc<TypeSet::Type>(properties.length())); ids[i] = properties[i].id;
if (!types) types[i] = GetValueTypeForTable(obj->getSlot(i));
return; AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
}
for (size_t i = 0; i < properties.length(); i++) { ObjectGroupCompartment::PlainObjectKey key;
ids[i] = properties[i].id; key.properties = ids;
types[i] = GetValueTypeForTable(obj->getSlot(i)); key.nproperties = nproperties;
AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]); MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
}
ObjectGroupCompartment::PlainObjectKey key; ObjectGroupCompartment::PlainObjectEntry entry;
key.properties = ids; entry.group.set(group);
key.nproperties = properties.length(); entry.shape.set(obj->lastProperty());
key.nfixed = obj->numFixedSlots(); entry.types = types;
MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
ObjectGroupCompartment::PlainObjectEntry entry; ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
entry.group.set(group); if (!table->add(np, key, entry))
entry.shape.set(obj->lastProperty()); return nullptr;
entry.types = types;
obj->setGroup(group);
p = table->lookupForAdd(lookup);
if (table->add(p, key, entry)) {
ids.forget(); ids.forget();
types.forget(); types.forget();
return obj;
} }
}
/* static */ PlainObject * RootedObjectGroup group(cx, p->value().group);
ObjectGroup::newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
{
AutoEnterAnalysis enter(cx);
ObjectGroupCompartment::PlainObjectTable *table = // Watch for existing groups which now use an unboxed layout.
cx->compartment()->objectGroups.plainObjectTable; if (group->maybeUnboxedLayout()) {
MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
}
if (!table) // Update property types according to the properties we are about to add.
return nullptr; // 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);
}
}
}
/* RootedShape shape(cx, p->value().shape);
* 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.
*/
/* if (group->maybePreliminaryObjects())
* Filter out a few cases where we don't want to use the object group table. newKind = TenuredObject;
* 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;
gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); 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); if (!obj->setLastProperty(cx, shape))
ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookupForAdd(lookup);
if (!p)
return nullptr; 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++) for (size_t i = 0; i < nproperties; i++)
obj->setSlot(i, properties[i].value); 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; return obj;
} }
@ -1148,7 +1153,7 @@ ObjectGroup::allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc
if (JSOp(*pc) == JSOP_NEWOBJECT) { if (JSOp(*pc) == JSOP_NEWOBJECT) {
// Keep track of the preliminary objects with this group, so we can try // 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. // 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()) { if (!shape->isEmptyShape()) {
PreliminaryObjectArrayWithTemplate *preliminaryObjects = PreliminaryObjectArrayWithTemplate *preliminaryObjects =
cx->new_<PreliminaryObjectArrayWithTemplate>(shape); cx->new_<PreliminaryObjectArrayWithTemplate>(shape);

View File

@ -131,6 +131,36 @@ namespace gc {
void MergeCompartments(JSCompartment *source, JSCompartment *target); 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. * Lazy object groups overview.
* *
@ -558,6 +588,10 @@ class ObjectGroup : public gc::TenuredCell
return offsetof(ObjectGroup, flags_); return offsetof(ObjectGroup, flags_);
} }
const ObjectGroupFlags *addressOfFlags() const {
return &flags_;
}
// Get the bit pattern stored in an object's addendum when it has an // Get the bit pattern stored in an object's addendum when it has an
// original unboxed group. // original unboxed group.
static inline int32_t addendumOriginalUnboxedGroupValue() { static inline int32_t addendumOriginalUnboxedGroupValue() {
@ -602,15 +636,17 @@ class ObjectGroup : public gc::TenuredCell
// Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable. // 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. // the object's current contents.
static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj);
static void fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj);
// Update the group of a freshly created 'rest' arguments object. // Update the group of a freshly created 'rest' arguments object.
static void fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj); 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. // Static accessors for ObjectGroupCompartment AllocationSiteTable.
@ -732,10 +768,12 @@ class ObjectGroupCompartment
static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table, static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table,
const Class *clasp, TaggedProto proto, JSObject *associated); 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 } // namespace js
#endif /* vm_ObjectGroup_h */ #endif /* vm_ObjectGroup_h */

View File

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

View File

@ -3361,7 +3361,7 @@ CommonPrefix(Shape *first, Shape *second)
} }
void 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 // Don't perform the analyses until sufficient preliminary objects have
// been allocated. // been allocated.

View File

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

View File

@ -6,6 +6,9 @@
#include "vm/UnboxedObject.h" #include "vm/UnboxedObject.h"
#include "jit/JitCommon.h"
#include "jit/Linker.h"
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "vm/Shape-inl.h" #include "vm/Shape-inl.h"
@ -38,6 +41,9 @@ UnboxedLayout::trace(JSTracer *trc)
if (replacementNewGroup_) if (replacementNewGroup_)
MarkObjectGroup(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup"); MarkObjectGroup(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup");
if (constructorCode_)
MarkJitCode(trc, &constructorCode_, "unboxed_layout_constructorCode");
} }
size_t size_t
@ -57,6 +63,190 @@ UnboxedLayout::setNewScript(TypeNewScript *newScript, bool writeBarrier /* = tru
newScript_ = newScript; 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 void
UnboxedLayout::detachFromCompartment() UnboxedLayout::detachFromCompartment()
{ {
@ -68,7 +258,8 @@ UnboxedLayout::detachFromCompartment()
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
bool 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]; 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. // object is converted to its native representation.
JSObject *obj = v.toObjectOrNull(); JSObject *obj = v.toObjectOrNull();
if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(this)) if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(this))
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this); cx->asJSContext()->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this);
*reinterpret_cast<PreBarrieredObject*>(p) = obj; *reinterpret_cast<PreBarrieredObject*>(p) = obj;
return true; return true;
@ -307,7 +498,7 @@ UnboxedPlainObject::convertToNative(JSContext *cx, JSObject *obj)
/* static */ /* static */
UnboxedPlainObject * UnboxedPlainObject *
UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind) UnboxedPlainObject::create(ExclusiveContext *cx, HandleObjectGroup group, NewObjectKind newKind)
{ {
MOZ_ASSERT(group->clasp() == &class_); MOZ_ASSERT(group->clasp() == &class_);
gc::AllocKind allocKind = group->unboxedLayout().getAllocKind(); gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
@ -343,6 +534,52 @@ UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind
return res; 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 /* static */ bool
UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj, UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj,
HandleId id, MutableHandleObject objp, HandleId id, MutableHandleObject objp,
@ -547,10 +784,13 @@ PropertiesAreSuperset(const UnboxedLayout::PropertyVector &properties, UnboxedLa
} }
bool bool
js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, js::TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
ObjectGroup *group, PreliminaryObjectArray *objects) ObjectGroup *group, PreliminaryObjectArray *objects)
{ {
if (!cx->runtime()->options().unboxedObjects()) if (!templateShape->runtimeFromAnyThread()->options().unboxedObjects())
return true;
if (templateShape->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
return true; return true;
if (templateShape->slotSpan() == 0) if (templateShape->slotSpan() == 0)
@ -727,7 +967,7 @@ js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
templateShape->getObjectMetadata(), templateShape->getObjectMetadata(),
templateShape->getObjectFlags()); templateShape->getObjectFlags());
if (!newShape) { if (!newShape) {
cx->clearPendingException(); cx->recoverFromOutOfMemory();
return false; return false;
} }

View File

@ -79,10 +79,16 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
// kind from this group. // kind from this group.
HeapPtrObjectGroup replacementNewGroup_; 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: public:
UnboxedLayout(const PropertyVector &properties, size_t size) UnboxedLayout(const PropertyVector &properties, size_t size)
: size_(size), newScript_(nullptr), traceList_(nullptr), : size_(size), newScript_(nullptr), traceList_(nullptr),
nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr) nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr),
constructorCode_(nullptr)
{ {
properties_.appendAll(properties); properties_.appendAll(properties);
} }
@ -138,6 +144,14 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
return nativeShape_; return nativeShape_;
} }
jit::JitCode *constructorCode() const {
return constructorCode_;
}
void setConstructorCode(jit::JitCode *code) {
constructorCode_ = code;
}
inline gc::AllocKind getAllocKind() const; inline gc::AllocKind getAllocKind() const;
void trace(JSTracer *trc); void trace(JSTracer *trc);
@ -145,6 +159,7 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
static bool makeNativeGroup(JSContext *cx, ObjectGroup *group); 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 // Class for a plain object using an unboxed representation. The physical
@ -199,11 +214,14 @@ class UnboxedPlainObject : public JSObject
return &data_[0]; 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); Value getValue(const UnboxedLayout::Property &property);
static bool convertToNative(JSContext *cx, JSObject *obj); 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); 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 // provided they all match the template shape. If successful, converts the
// preliminary objects and their group to the new unboxed representation. // preliminary objects and their group to the new unboxed representation.
bool bool
TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
ObjectGroup *group, PreliminaryObjectArray *objects); ObjectGroup *group, PreliminaryObjectArray *objects);
inline gc::AllocKind inline gc::AllocKind

View File

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