зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1135897 - Use unboxed objects for JSON objects and constant literals embedded in scripts, r=jandem.
This commit is contained in:
Родитель
f5ae8deae6
Коммит
89a333a739
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
495
js/src/jsobj.cpp
495
js/src/jsobj.cpp
|
@ -1437,7 +1437,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() &&
|
||||
|
@ -1445,7 +1445,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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1453,7 +1454,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);
|
||||
|
@ -1464,10 +1465,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;
|
||||
|
@ -1479,7 +1480,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>());
|
||||
|
@ -1784,90 +1785,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
|
||||
|
@ -1924,7 +1997,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. */
|
||||
|
||||
|
@ -1936,231 +2009,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(©OnWrite))
|
||||
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(©OnWrite))
|
||||
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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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(®exps[i]->as<NativeObject>());
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
@ -1372,8 +1377,15 @@ ObjectGroupCompartment::clearTables()
|
|||
allocationSiteTable->clear();
|
||||
if (arrayObjectTable && arrayObjectTable->initialized())
|
||||
arrayObjectTable->clear();
|
||||
if (plainObjectTable && plainObjectTable->initialized())
|
||||
if (plainObjectTable && plainObjectTable->initialized()) {
|
||||
for (PlainObjectTable::Enum e(*plainObjectTable); !e.empty(); e.popFront()) {
|
||||
const PlainObjectKey &key = e.front().key();
|
||||
PlainObjectEntry &entry = e.front().value();
|
||||
js_free(key.properties);
|
||||
js_free(entry.types);
|
||||
}
|
||||
plainObjectTable->clear();
|
||||
}
|
||||
if (defaultNewTable && defaultNewTable->initialized())
|
||||
defaultNewTable->clear();
|
||||
if (lazyTable && lazyTable->initialized())
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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, ¬Object);
|
||||
Register valueObject = masm.extractObject(valueAddress, scratch1);
|
||||
masm.branchPtrInNurseryRange(Assembler::Equal, valueObject, scratch2, &postBarrier);
|
||||
masm.bind(¬Object);
|
||||
}
|
||||
}
|
||||
|
||||
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) ? ¬Object : &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(¬Object);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче