diff --git a/js/src/jit-test/tests/ion/bug1092833.js b/js/src/jit-test/tests/ion/bug1092833.js new file mode 100644 index 00000000000..64a74fb28fd --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1092833.js @@ -0,0 +1,49 @@ +// Test that lexicals work with functions with many bindings. + +(function() { + var a01 + var b02 + var c03 + var d04 + var e05 + var f06 + var g07 + var h08 + let i09 + var j10 + var k11 + var l12 + var m13 + var n14 + var o15 + (function n14() { + assertEq(i09, undefined); + })() +})(); + +try { + (function() { + var a01 + var b02 + var c03 + var d04 + var e05 + var f06 + var g07 + var h08 + let i09 + var j10 + var k11 + var l12 + var m13 + var n14 + var o15 + (function n14() { + i12++ + })() + let i12 + })() +} catch (e) { + assertEq(e instanceof ReferenceError, true); + assertEq(e.message.indexOf("i12") > 0, true); +} diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index 397bd044f7e..11b8701b38f 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -27,6 +27,7 @@ #endif #include "jsinferinlines.h" #include "jsobjinlines.h" +#include "vm/Interpreter-inl.h" using namespace js; using namespace js::jit; @@ -956,12 +957,18 @@ MacroAssembler::copySlotsFromTemplate(Register obj, const NativeObject *template } void -MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end) +MacroAssembler::fillSlotsWithConstantValue(Address base, Register temp, + uint32_t start, uint32_t end, const Value &v) { + MOZ_ASSERT(v.isUndefined() || IsUninitializedLexical(v)); + + if (start >= end) + return; + #ifdef JS_NUNBOX32 // We only have a single spare register, so do the initialization as two // strided writes of the tag and body. - jsval_layout jv = JSVAL_TO_IMPL(UndefinedValue()); + jsval_layout jv = JSVAL_TO_IMPL(v); Address addr = base; move32(Imm32(jv.s.payload.i32), temp); @@ -973,22 +980,43 @@ MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t sta for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue)) store32(temp, ToType(addr)); #else - moveValue(UndefinedValue(), temp); + moveValue(v, temp); for (uint32_t i = start; i < end; ++i, base.offset += sizeof(HeapValue)) storePtr(temp, base); #endif } -static uint32_t -FindStartOfUndefinedSlots(NativeObject *templateObj, uint32_t nslots) +void +MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end) +{ + fillSlotsWithConstantValue(base, temp, start, end, UndefinedValue()); +} + +void +MacroAssembler::fillSlotsWithUninitialized(Address base, Register temp, uint32_t start, uint32_t end) +{ + fillSlotsWithConstantValue(base, temp, start, end, MagicValue(JS_UNINITIALIZED_LEXICAL)); +} + +static void +FindStartOfUndefinedAndUninitializedSlots(NativeObject *templateObj, uint32_t nslots, + uint32_t *startOfUndefined, uint32_t *startOfUninitialized) { MOZ_ASSERT(nslots == templateObj->lastProperty()->slotSpan(templateObj->getClass())); MOZ_ASSERT(nslots > 0); - for (uint32_t first = nslots; first != 0; --first) { - if (templateObj->getSlot(first - 1) != UndefinedValue()) - return first; + uint32_t first = nslots; + for (; first != 0; --first) { + if (!IsUninitializedLexical(templateObj->getSlot(first - 1))) + break; } - return 0; + *startOfUninitialized = first; + for (; first != 0; --first) { + if (templateObj->getSlot(first - 1) != UndefinedValue()) { + *startOfUndefined = first; + return; + } + } + *startOfUndefined = 0; } void @@ -1011,16 +1039,28 @@ MacroAssembler::initGCSlots(Register obj, Register slots, NativeObject *template // logically into independent non-UndefinedValue writes to the head and // duplicated writes of UndefinedValue to the tail. For the majority of // objects, the "tail" will be the entire slot range. - uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots); + // + // The template object may be a CallObject, in which case we need to + // account for uninitialized lexical slots as well as undefined + // slots. Unitialized lexical slots always appear at the very end of + // slots, after undefined. + uint32_t startOfUndefined = nslots; + uint32_t startOfUninitialized = nslots; + FindStartOfUndefinedAndUninitializedSlots(templateObj, nslots, + &startOfUndefined, &startOfUninitialized); MOZ_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed. + MOZ_ASSERT_IF(startOfUndefined != nfixed, startOfUndefined <= startOfUninitialized); + MOZ_ASSERT_IF(!templateObj->is(), startOfUninitialized == nslots); // Copy over any preserved reserved slots. copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined); - // Fill the rest of the fixed slots with undefined. + // Fill the rest of the fixed slots with undefined and uninitialized. if (initFixedSlots) { fillSlotsWithUndefined(Address(obj, NativeObject::getFixedSlotOffset(startOfUndefined)), slots, - startOfUndefined, nfixed); + startOfUndefined, Min(startOfUninitialized, nfixed)); + size_t offset = NativeObject::getFixedSlotOffset(startOfUninitialized); + fillSlotsWithUninitialized(Address(obj, offset), slots, startOfUninitialized, nfixed); } if (ndynamic) { @@ -1028,7 +1068,14 @@ MacroAssembler::initGCSlots(Register obj, Register slots, NativeObject *template // register briefly for our slots base address. push(obj); loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj); + + // Initially fill all dynamic slots with undefined. fillSlotsWithUndefined(Address(obj, 0), slots, 0, ndynamic); + + // Fill uninitialized slots if necessary. + fillSlotsWithUninitialized(Address(obj, 0), slots, startOfUninitialized - nfixed, + nslots - startOfUninitialized); + pop(obj); } } diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index a303a814fd9..7c8edc77a72 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -810,7 +810,10 @@ class MacroAssembler : public MacroAssemblerSpecific void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail); void copySlotsFromTemplate(Register obj, const NativeObject *templateObj, uint32_t start, uint32_t end); + void fillSlotsWithConstantValue(Address addr, Register temp, uint32_t start, uint32_t end, + 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); public: