зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1160887 - Fix various unboxed object bugs, r=jandem,terrence.
This commit is contained in:
Родитель
f08653fb1d
Коммит
4bd04662c2
|
@ -1157,11 +1157,11 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
|
|||
|
||||
case SavedValueArrayTag: {
|
||||
MOZ_ASSERT(!(addr & CellMask));
|
||||
NativeObject* obj = reinterpret_cast<NativeObject*>(addr);
|
||||
JSObject* obj = reinterpret_cast<JSObject*>(addr);
|
||||
HeapValue* vp;
|
||||
HeapValue* end;
|
||||
if (restoreValueArray(obj, (void**)&vp, (void**)&end))
|
||||
pushValueArray(obj, vp, end);
|
||||
pushValueArray(&obj->as<NativeObject>(), vp, end);
|
||||
else
|
||||
repush(obj);
|
||||
return;
|
||||
|
@ -1385,11 +1385,15 @@ GCMarker::saveValueRanges()
|
|||
}
|
||||
|
||||
bool
|
||||
GCMarker::restoreValueArray(NativeObject* obj, void** vpp, void** endp)
|
||||
GCMarker::restoreValueArray(JSObject* objArg, void** vpp, void** endp)
|
||||
{
|
||||
uintptr_t start = stack.pop();
|
||||
HeapSlot::Kind kind = (HeapSlot::Kind) stack.pop();
|
||||
|
||||
if (!objArg->isNative())
|
||||
return false;
|
||||
NativeObject* obj = &objArg->as<NativeObject>();
|
||||
|
||||
if (kind == HeapSlot::Element) {
|
||||
if (!obj->is<ArrayObject>())
|
||||
return false;
|
||||
|
@ -1812,10 +1816,21 @@ StoreBuffer::WholeCellEdges::mark(TenuringTracer& mover) const
|
|||
JSGCTraceKind kind = GetGCThingTraceKind(edge);
|
||||
if (kind <= JSTRACE_OBJECT) {
|
||||
JSObject* object = static_cast<JSObject*>(edge);
|
||||
if (object->is<ArgumentsObject>())
|
||||
ArgumentsObject::trace(&mover, object);
|
||||
|
||||
// FIXME: bug 1161664 -- call the inline path below, now that it is accessable.
|
||||
object->traceChildren(&mover);
|
||||
|
||||
// Additionally trace the expando object attached to any unboxed plain
|
||||
// objects. Baseline and Ion can write properties to the expando while
|
||||
// only adding a post barrier to the owning unboxed object. Note that
|
||||
// it isn't possible for a nursery unboxed object to have a tenured
|
||||
// expando, so that adding a post barrier on the original object will
|
||||
// capture any tenured->nursery edges in the expando as well.
|
||||
if (object->is<UnboxedPlainObject>()) {
|
||||
if (UnboxedExpandoObject* expando = object->as<UnboxedPlainObject>().maybeExpando())
|
||||
expando->traceChildren(&mover);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(kind == JSTRACE_JITCODE);
|
||||
|
|
|
@ -277,7 +277,7 @@ class GCMarker : public JSTracer
|
|||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
bool restoreValueArray(NativeObject* obj, void** vpp, void** endp);
|
||||
bool restoreValueArray(JSObject* obj, void** vpp, void** endp);
|
||||
void saveValueRanges();
|
||||
inline void processMarkStackTop(SliceBudget& budget);
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@ function inline_notSoEmpty1(a, b, c, d) {
|
|||
}
|
||||
var uceFault_notSoEmpty1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty1'));
|
||||
function notSoEmpty1() {
|
||||
var a = { v: i };
|
||||
var b = { v: 1 + a.v };
|
||||
var c = { v: 2 + b.v };
|
||||
var d = { v: 3 + c.v };
|
||||
var unused = { v: 4 + d.v };
|
||||
var a = { v: i, notunboxed: undefined };
|
||||
var b = { v: 1 + a.v, notunboxed: undefined };
|
||||
var c = { v: 2 + b.v, notunboxed: undefined };
|
||||
var d = { v: 3 + c.v, notunboxed: undefined };
|
||||
var unused = { v: 4 + d.v, notunboxed: undefined };
|
||||
var res = inline_notSoEmpty1(a, b, c, d);
|
||||
if (uceFault_notSoEmpty1(i) || uceFault_notSoEmpty1(i))
|
||||
assertEq(i, res.v);
|
||||
|
@ -44,15 +44,15 @@ function notSoEmpty1() {
|
|||
// Check that we can recover objects with their content.
|
||||
function inline_notSoEmpty2(a, b, c, d) {
|
||||
"use strict";
|
||||
return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
|
||||
return { v: (a.v + b.v + c.v + d.v - 10) / 4, notunboxed: undefined };
|
||||
}
|
||||
var uceFault_notSoEmpty2 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty2'));
|
||||
function notSoEmpty2(i) {
|
||||
var a = { v: i };
|
||||
var b = { v: 1 + a.v };
|
||||
var c = { v: 2 + b.v };
|
||||
var d = { v: 3 + c.v };
|
||||
var unused = { v: 4 + d.v };
|
||||
var a = { v: i, notunboxed: undefined };
|
||||
var b = { v: 1 + a.v, notunboxed: undefined };
|
||||
var c = { v: 2 + b.v, notunboxed: undefined };
|
||||
var d = { v: 3 + c.v, notunboxed: undefined };
|
||||
var unused = { v: 4 + d.v, notunboxed: undefined };
|
||||
var res = inline_notSoEmpty2(a, b, c, d);
|
||||
if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i))
|
||||
assertEq(i, res.v);
|
||||
|
@ -70,13 +70,13 @@ function notSoEmpty2(i) {
|
|||
var argFault_observeArg = function (i) {
|
||||
if (i > 98)
|
||||
return inline_observeArg.arguments[0];
|
||||
return { test : i };
|
||||
return { test : i, notunboxed: undefined };
|
||||
};
|
||||
function inline_observeArg(obj, i) {
|
||||
return argFault_observeArg(i);
|
||||
}
|
||||
function observeArg(i) {
|
||||
var obj = { test: i };
|
||||
var obj = { test: i, notunboxed: undefined };
|
||||
var res = inline_observeArg(obj, i);
|
||||
assertEq(res.test, i);
|
||||
assertRecoveredOnBailout(obj, true);
|
||||
|
@ -84,7 +84,7 @@ function observeArg(i) {
|
|||
|
||||
// Check case where one successor can have multiple times the same predecessor.
|
||||
function complexPhi(i) {
|
||||
var obj = { test: i };
|
||||
var obj = { test: i, notunboxed: undefined };
|
||||
switch (i) { // TableSwitch
|
||||
case 0: obj.test = 0; break;
|
||||
case 1: obj.test = 1; break;
|
||||
|
@ -103,12 +103,12 @@ function complexPhi(i) {
|
|||
function withinIf(i) {
|
||||
var x = undefined;
|
||||
if (i % 2 == 0) {
|
||||
let obj = { foo: i };
|
||||
let obj = { foo: i, notunboxed: undefined };
|
||||
x = obj.foo;
|
||||
assertRecoveredOnBailout(obj, true);
|
||||
obj = undefined;
|
||||
} else {
|
||||
let obj = { bar: i };
|
||||
let obj = { bar: i, notunboxed: undefined };
|
||||
x = obj.bar;
|
||||
assertRecoveredOnBailout(obj, true);
|
||||
obj = undefined;
|
||||
|
@ -118,7 +118,7 @@ function withinIf(i) {
|
|||
|
||||
// Check case where one successor can have multiple times the same predecessor.
|
||||
function unknownLoad(i) {
|
||||
var obj = { foo: i };
|
||||
var obj = { foo: i, notunboxed: undefined };
|
||||
assertEq(obj.bar, undefined);
|
||||
// Unknown properties are using GetPropertyCache.
|
||||
assertRecoveredOnBailout(obj, false);
|
||||
|
@ -132,7 +132,8 @@ function dynamicSlots(i) {
|
|||
p11: i + 11, p12: i + 12, p13: i + 13, p14: i + 14, p15: i + 15, p16: i + 16, p17: i + 17, p18: i + 18, p19: i + 19, p20: i + 20,
|
||||
p21: i + 21, p22: i + 22, p23: i + 23, p24: i + 24, p25: i + 25, p26: i + 26, p27: i + 27, p28: i + 28, p29: i + 29, p30: i + 30,
|
||||
p31: i + 31, p32: i + 32, p33: i + 33, p34: i + 34, p35: i + 35, p36: i + 36, p37: i + 37, p38: i + 38, p39: i + 39, p40: i + 40,
|
||||
p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50
|
||||
p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50,
|
||||
notunboxed: undefined
|
||||
};
|
||||
// Add a function call to capture a resumepoint at the end of the call or
|
||||
// inside the inlined block, such as the bailout does not rewind to the
|
||||
|
@ -147,6 +148,7 @@ function Point(x, y)
|
|||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.notUnboxed = undefined;
|
||||
}
|
||||
|
||||
function createThisWithTemplate(i)
|
||||
|
|
|
@ -465,6 +465,8 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
|
|||
// unboxed plain object.
|
||||
MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>()));
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.defaultNewTable;
|
||||
|
||||
if (!table) {
|
||||
|
@ -498,6 +500,9 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
|
|||
clasp = &PlainObject::class_;
|
||||
}
|
||||
|
||||
if (proto.isObject() && !proto.toObject()->setDelegate(cx))
|
||||
return nullptr;
|
||||
|
||||
ObjectGroupCompartment::NewTable::AddPtr p =
|
||||
table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated));
|
||||
if (p) {
|
||||
|
@ -509,11 +514,6 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
|
|||
return group;
|
||||
}
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
if (proto.isObject() && !proto.toObject()->setDelegate(cx))
|
||||
return nullptr;
|
||||
|
||||
ObjectGroupFlags initialFlags = 0;
|
||||
if (!proto.isObject() || proto.toObject()->isNewGroupUnknown())
|
||||
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
|
||||
|
|
|
@ -71,6 +71,8 @@ static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
|
|||
/* static */ bool
|
||||
UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
|
||||
{
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
|
||||
using namespace jit;
|
||||
|
||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
||||
|
@ -107,8 +109,7 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
|
|||
Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
|
||||
|
||||
LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
|
||||
for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
|
||||
masm.Push(*iter);
|
||||
masm.PushRegsInMask(savedNonVolatileRegisters);
|
||||
|
||||
// The scratch double register might be used by MacroAssembler methods.
|
||||
if (ScratchDoubleReg.volatile_())
|
||||
|
@ -148,12 +149,20 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
|
|||
masm.jump(&allocated);
|
||||
masm.bind(&postBarrier);
|
||||
|
||||
LiveGeneralRegisterSet liveVolatileRegisters;
|
||||
liveVolatileRegisters.add(propertiesReg);
|
||||
if (object.volatile_())
|
||||
liveVolatileRegisters.add(object);
|
||||
masm.PushRegsInMask(liveVolatileRegisters);
|
||||
|
||||
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.PopRegsInMask(liveVolatileRegisters);
|
||||
|
||||
masm.bind(&allocated);
|
||||
|
||||
ValueOperand valueOperand;
|
||||
|
@ -213,8 +222,7 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
|
|||
// Restore non-volatile registers which were saved on entry.
|
||||
if (ScratchDoubleReg.volatile_())
|
||||
masm.pop(ScratchDoubleReg);
|
||||
for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
|
||||
masm.Pop(*iter);
|
||||
masm.PopRegsInMask(savedNonVolatileRegisters);
|
||||
|
||||
masm.abiret();
|
||||
|
||||
|
@ -327,7 +335,7 @@ SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id,
|
|||
}
|
||||
|
||||
static inline Value
|
||||
GetUnboxedValue(uint8_t* p, JSValueType type)
|
||||
GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized)
|
||||
{
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
|
@ -336,8 +344,16 @@ GetUnboxedValue(uint8_t* p, JSValueType type)
|
|||
case JSVAL_TYPE_INT32:
|
||||
return Int32Value(*reinterpret_cast<int32_t*>(p));
|
||||
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
return DoubleValue(*reinterpret_cast<double*>(p));
|
||||
case JSVAL_TYPE_DOUBLE: {
|
||||
// During unboxed plain object creation, non-GC thing properties are
|
||||
// left uninitialized. This is normally fine, since the properties will
|
||||
// be filled in shortly, but if they are read before that happens we
|
||||
// need to make sure that doubles are canonical.
|
||||
double d = *reinterpret_cast<double*>(p);
|
||||
if (maybeUninitialized)
|
||||
return DoubleValue(JS::CanonicalizeNaN(d));
|
||||
return DoubleValue(d);
|
||||
}
|
||||
|
||||
case JSVAL_TYPE_STRING:
|
||||
return StringValue(*reinterpret_cast<JSString**>(p));
|
||||
|
@ -363,10 +379,11 @@ UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property
|
|||
}
|
||||
|
||||
Value
|
||||
UnboxedPlainObject::getValue(const UnboxedLayout::Property& property)
|
||||
UnboxedPlainObject::getValue(const UnboxedLayout::Property& property,
|
||||
bool maybeUninitialized /* = false */)
|
||||
{
|
||||
uint8_t* p = &data_[property.offset];
|
||||
return GetUnboxedValue(p, property.type);
|
||||
return GetUnboxedValue(p, property.type, maybeUninitialized);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -411,6 +428,12 @@ UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj
|
|||
if (!expando)
|
||||
return nullptr;
|
||||
|
||||
// If the expando is tenured then the original object must also be tenured.
|
||||
// Otherwise barriers triggered on the original object for writes to the
|
||||
// expando (as can happen in the JIT) won't see the tenured->nursery edge.
|
||||
// See WholeCellEdges::mark.
|
||||
MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj));
|
||||
|
||||
// As with setValue(), we need to manually trigger post barriers on the
|
||||
// whole object. If we treat the field as a HeapPtrObject and later convert
|
||||
// the object to its native representation, we will end up with a corrupted
|
||||
|
@ -578,10 +601,15 @@ UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj)
|
|||
|
||||
AutoValueVector values(cx);
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i])))
|
||||
// We might be reading properties off the object which have not been
|
||||
// initialized yet. Make sure any double values we read here are
|
||||
// canonicalized.
|
||||
if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true)))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject::writeBarrierPre(expando);
|
||||
|
||||
obj->setGroup(layout.nativeGroup());
|
||||
obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
|
||||
|
||||
|
@ -935,7 +963,9 @@ const Class UnboxedExpandoObject::class_ = {
|
|||
|
||||
const Class UnboxedPlainObject::class_ = {
|
||||
js_Object_str,
|
||||
Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
Class::NON_NATIVE |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
|
@ -1099,7 +1129,7 @@ UnboxedArrayObject::getElement(size_t index)
|
|||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return GetUnboxedValue(p, elementType());
|
||||
return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
|
|
@ -265,7 +265,7 @@ class UnboxedPlainObject : public JSObject
|
|||
static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj);
|
||||
|
||||
bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v);
|
||||
Value getValue(const UnboxedLayout::Property& property);
|
||||
Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false);
|
||||
|
||||
static bool convertToNative(JSContext* cx, JSObject* obj);
|
||||
static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
|
|
Загрузка…
Ссылка в новой задаче