LCOV - code coverage report
Current view: top level - js/src/vm - UnboxedObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 332 1058 31.4 %
Date: 2017-07-14 16:53:18 Functions: 33 148 22.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "vm/UnboxedObject-inl.h"
       8             : 
       9             : #include "jit/BaselineIC.h"
      10             : #include "jit/ExecutableAllocator.h"
      11             : #include "jit/JitCommon.h"
      12             : #include "jit/Linker.h"
      13             : 
      14             : #include "jsobjinlines.h"
      15             : 
      16             : #include "gc/Nursery-inl.h"
      17             : #include "jit/MacroAssembler-inl.h"
      18             : #include "vm/Shape-inl.h"
      19             : 
      20             : using mozilla::ArrayLength;
      21             : using mozilla::DebugOnly;
      22             : using mozilla::PodCopy;
      23             : 
      24             : using namespace js;
      25             : 
      26             : /////////////////////////////////////////////////////////////////////
      27             : // UnboxedLayout
      28             : /////////////////////////////////////////////////////////////////////
      29             : 
      30             : void
      31           0 : UnboxedLayout::trace(JSTracer* trc)
      32             : {
      33           0 :     for (size_t i = 0; i < properties_.length(); i++)
      34           0 :         TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name");
      35             : 
      36           0 :     if (newScript())
      37           0 :         newScript()->trace(trc);
      38             : 
      39           0 :     TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
      40           0 :     TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape");
      41           0 :     TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript");
      42           0 :     TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup");
      43           0 :     TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode");
      44           0 : }
      45             : 
      46             : size_t
      47           0 : UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
      48             : {
      49           0 :     return mallocSizeOf(this)
      50           0 :          + properties_.sizeOfExcludingThis(mallocSizeOf)
      51           0 :          + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
      52           0 :          + mallocSizeOf(traceList());
      53             : }
      54             : 
      55             : void
      56           0 : UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */)
      57             : {
      58           0 :     if (newScript_ && writeBarrier)
      59           0 :         TypeNewScript::writeBarrierPre(newScript_);
      60           0 :     newScript_ = newScript;
      61           0 : }
      62             : 
      63             : // Constructor code returns a 0x1 value to indicate the constructor code should
      64             : // be cleared.
      65             : static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
      66             : 
      67             : /* static */ bool
      68           0 : UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
      69             : {
      70           0 :     gc::AutoSuppressGC suppress(cx);
      71             : 
      72             :     using namespace jit;
      73             : 
      74           0 :     if (!cx->compartment()->ensureJitCompartmentExists(cx))
      75           0 :         return false;
      76             : 
      77           0 :     UnboxedLayout& layout = group->unboxedLayout();
      78           0 :     MOZ_ASSERT(!layout.constructorCode());
      79             : 
      80           0 :     UnboxedPlainObject* templateObject = UnboxedPlainObject::create(cx, group, TenuredObject);
      81           0 :     if (!templateObject)
      82           0 :         return false;
      83             : 
      84           0 :     JitContext jitContext(cx, nullptr);
      85             : 
      86           0 :     MacroAssembler masm;
      87             : 
      88           0 :     Register propertiesReg, newKindReg;
      89             : #ifdef JS_CODEGEN_X86
      90             :     propertiesReg = eax;
      91             :     newKindReg = ecx;
      92             :     masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg);
      93             :     masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg);
      94             : #else
      95           0 :     propertiesReg = IntArgReg0;
      96           0 :     newKindReg = IntArgReg1;
      97             : #endif
      98             : 
      99             : #ifdef JS_CODEGEN_ARM64
     100             :     // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing.
     101             :     masm.initStackPtr();
     102             : #endif
     103             : 
     104           0 :     MOZ_ASSERT(propertiesReg.volatile_());
     105           0 :     MOZ_ASSERT(newKindReg.volatile_());
     106             : 
     107           0 :     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     108           0 :     regs.take(propertiesReg);
     109           0 :     regs.take(newKindReg);
     110           0 :     Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
     111             : 
     112           0 :     LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
     113           0 :     masm.PushRegsInMask(savedNonVolatileRegisters);
     114             : 
     115             :     // The scratch double register might be used by MacroAssembler methods.
     116           0 :     if (ScratchDoubleReg.volatile_())
     117           0 :         masm.push(ScratchDoubleReg);
     118             : 
     119           0 :     Label failure, tenuredObject, allocated;
     120           0 :     masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject);
     121           0 :     masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()),
     122           0 :                       Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject);
     123             : 
     124             :     // Allocate an object in the nursery
     125             :     masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure,
     126           0 :                         /* initFixedSlots = */ false);
     127             : 
     128           0 :     masm.jump(&allocated);
     129           0 :     masm.bind(&tenuredObject);
     130             : 
     131             :     // Allocate an object in the tenured heap.
     132             :     masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure,
     133           0 :                         /* initFixedSlots = */ false);
     134             : 
     135             :     // If any of the properties being stored are in the nursery, add a store
     136             :     // buffer entry for the new object.
     137           0 :     Label postBarrier;
     138           0 :     for (size_t i = 0; i < layout.properties().length(); i++) {
     139           0 :         const UnboxedLayout::Property& property = layout.properties()[i];
     140           0 :         if (property.type == JSVAL_TYPE_OBJECT) {
     141           0 :             Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
     142           0 :             Label notObject;
     143           0 :             masm.branchTestObject(Assembler::NotEqual, valueAddress, &notObject);
     144           0 :             Register valueObject = masm.extractObject(valueAddress, scratch1);
     145           0 :             masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier);
     146           0 :             masm.bind(&notObject);
     147             :         }
     148             :     }
     149             : 
     150           0 :     masm.jump(&allocated);
     151           0 :     masm.bind(&postBarrier);
     152             : 
     153           0 :     LiveGeneralRegisterSet liveVolatileRegisters;
     154           0 :     liveVolatileRegisters.add(propertiesReg);
     155           0 :     if (object.volatile_())
     156           0 :         liveVolatileRegisters.add(object);
     157           0 :     masm.PushRegsInMask(liveVolatileRegisters);
     158             : 
     159           0 :     masm.mov(ImmPtr(cx->runtime()), scratch1);
     160           0 :     masm.setupUnalignedABICall(scratch2);
     161           0 :     masm.passABIArg(scratch1);
     162           0 :     masm.passABIArg(object);
     163           0 :     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
     164             : 
     165           0 :     masm.PopRegsInMask(liveVolatileRegisters);
     166             : 
     167           0 :     masm.bind(&allocated);
     168             : 
     169           0 :     ValueOperand valueOperand;
     170             : #ifdef JS_NUNBOX32
     171             :     valueOperand = ValueOperand(scratch1, scratch2);
     172             : #else
     173           0 :     valueOperand = ValueOperand(scratch1);
     174             : #endif
     175             : 
     176           0 :     Label failureStoreOther, failureStoreObject;
     177             : 
     178           0 :     for (size_t i = 0; i < layout.properties().length(); i++) {
     179           0 :         const UnboxedLayout::Property& property = layout.properties()[i];
     180           0 :         Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
     181           0 :         Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset);
     182             : 
     183           0 :         masm.loadValue(valueAddress, valueOperand);
     184             : 
     185           0 :         if (property.type == JSVAL_TYPE_OBJECT) {
     186           0 :             HeapTypeSet* types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
     187             : 
     188           0 :             Label notObject;
     189           0 :             masm.branchTestObject(Assembler::NotEqual, valueOperand,
     190           0 :                                   types->mightBeMIRType(MIRType::Null) ? &notObject : &failureStoreObject);
     191             : 
     192           0 :             Register payloadReg = masm.extractObject(valueOperand, scratch1);
     193             : 
     194           0 :             if (!types->hasType(TypeSet::AnyObjectType())) {
     195           0 :                 Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
     196           0 :                 masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject);
     197             :             }
     198             : 
     199           0 :             masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
     200             :                                       TypedOrValueRegister(MIRType::Object,
     201           0 :                                                            AnyRegister(payloadReg)), nullptr);
     202             : 
     203           0 :             if (notObject.used()) {
     204           0 :                 Label done;
     205           0 :                 masm.jump(&done);
     206           0 :                 masm.bind(&notObject);
     207           0 :                 masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
     208           0 :                 masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr);
     209           0 :                 masm.bind(&done);
     210             :             }
     211             :         } else {
     212           0 :             masm.storeUnboxedProperty(targetAddress, property.type,
     213           0 :                                       ConstantOrRegister(valueOperand), &failureStoreOther);
     214             :         }
     215             :     }
     216             : 
     217           0 :     Label done;
     218           0 :     masm.bind(&done);
     219             : 
     220           0 :     if (object != ReturnReg)
     221           0 :         masm.movePtr(object, ReturnReg);
     222             : 
     223             :     // Restore non-volatile registers which were saved on entry.
     224           0 :     if (ScratchDoubleReg.volatile_())
     225           0 :         masm.pop(ScratchDoubleReg);
     226           0 :     masm.PopRegsInMask(savedNonVolatileRegisters);
     227             : 
     228           0 :     masm.abiret();
     229             : 
     230           0 :     masm.bind(&failureStoreOther);
     231             : 
     232             :     // There was a failure while storing a value which cannot be stored at all
     233             :     // in the unboxed object. Initialize the object so it is safe for GC and
     234             :     // return null.
     235           0 :     masm.initUnboxedObjectContents(object, templateObject);
     236             : 
     237           0 :     masm.bind(&failure);
     238             : 
     239           0 :     masm.movePtr(ImmWord(0), object);
     240           0 :     masm.jump(&done);
     241             : 
     242           0 :     masm.bind(&failureStoreObject);
     243             : 
     244             :     // There was a failure while storing a value to an object slot of the
     245             :     // unboxed object. If the value is storable, the failure occurred due to
     246             :     // incomplete type information in the object, so return a token to trigger
     247             :     // regeneration of the jitcode after a new object is created in the VM.
     248             :     {
     249           0 :         Label isObject;
     250           0 :         masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
     251           0 :         masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
     252           0 :         masm.bind(&isObject);
     253             :     }
     254             : 
     255             :     // Initialize the object so it is safe for GC.
     256           0 :     masm.initUnboxedObjectContents(object, templateObject);
     257             : 
     258           0 :     masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
     259           0 :     masm.jump(&done);
     260             : 
     261           0 :     Linker linker(masm);
     262           0 :     AutoFlushICache afc("UnboxedObject");
     263           0 :     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
     264           0 :     if (!code)
     265           0 :         return false;
     266             : 
     267           0 :     layout.setConstructorCode(code);
     268           0 :     return true;
     269             : }
     270             : 
     271             : void
     272           0 : UnboxedLayout::detachFromCompartment()
     273             : {
     274           0 :     if (isInList())
     275           0 :         remove();
     276           0 : }
     277             : 
     278             : /////////////////////////////////////////////////////////////////////
     279             : // UnboxedPlainObject
     280             : /////////////////////////////////////////////////////////////////////
     281             : 
     282             : bool
     283        1003 : UnboxedPlainObject::setValue(JSContext* cx, const UnboxedLayout::Property& property,
     284             :                              const Value& v)
     285             : {
     286        1003 :     uint8_t* p = &data_[property.offset];
     287        1003 :     return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v,
     288        1003 :                            /* preBarrier = */ true);
     289             : }
     290             : 
     291             : Value
     292        3899 : UnboxedPlainObject::getValue(const UnboxedLayout::Property& property,
     293             :                              bool maybeUninitialized /* = false */)
     294             : {
     295        3899 :     uint8_t* p = &data_[property.offset];
     296        3899 :     return GetUnboxedValue(p, property.type, maybeUninitialized);
     297             : }
     298             : 
     299             : void
     300           0 : UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj)
     301             : {
     302           0 :     if (obj->as<UnboxedPlainObject>().expando_) {
     303             :         TraceManuallyBarrieredEdge(trc,
     304           0 :             reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
     305           0 :             "unboxed_expando");
     306             :     }
     307             : 
     308           0 :     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
     309           0 :     const int32_t* list = layout.traceList();
     310           0 :     if (!list)
     311           0 :         return;
     312             : 
     313           0 :     uint8_t* data = obj->as<UnboxedPlainObject>().data();
     314           0 :     while (*list != -1) {
     315           0 :         GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
     316           0 :         TraceEdge(trc, heap, "unboxed_string");
     317           0 :         list++;
     318             :     }
     319           0 :     list++;
     320           0 :     while (*list != -1) {
     321           0 :         GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
     322           0 :         TraceNullableEdge(trc, heap, "unboxed_object");
     323           0 :         list++;
     324             :     }
     325             : 
     326             :     // Unboxed objects don't have Values to trace.
     327           0 :     MOZ_ASSERT(*(list + 1) == -1);
     328             : }
     329             : 
     330             : /* static */ UnboxedExpandoObject*
     331           0 : UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj)
     332             : {
     333           0 :     if (obj->expando_)
     334           0 :         return obj->expando_;
     335             : 
     336             :     UnboxedExpandoObject* expando =
     337           0 :         NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4);
     338           0 :     if (!expando)
     339           0 :         return nullptr;
     340             : 
     341             :     // Don't track property types for expando objects. This allows Baseline
     342             :     // and Ion AddSlot ICs to guard on the unboxed group without guarding on
     343             :     // the expando group.
     344           0 :     MarkObjectGroupUnknownProperties(cx, expando->group());
     345             : 
     346             :     // If the expando is tenured then the original object must also be tenured.
     347             :     // Otherwise barriers triggered on the original object for writes to the
     348             :     // expando (as can happen in the JIT) won't see the tenured->nursery edge.
     349             :     // See WholeCellEdges::mark.
     350           0 :     MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj));
     351             : 
     352             :     // As with setValue(), we need to manually trigger post barriers on the
     353             :     // whole object. If we treat the field as a GCPtrObject and later
     354             :     // convert the object to its native representation, we will end up with a
     355             :     // corrupted store buffer entry.
     356           0 :     if (IsInsideNursery(expando) && !IsInsideNursery(obj))
     357           0 :         cx->zone()->group()->storeBuffer().putWholeCell(obj);
     358             : 
     359           0 :     obj->expando_ = expando;
     360           0 :     return expando;
     361             : }
     362             : 
     363             : bool
     364        7574 : UnboxedPlainObject::containsUnboxedOrExpandoProperty(JSContext* cx, jsid id) const
     365             : {
     366        7574 :     if (layout().lookup(id))
     367        3817 :         return true;
     368             : 
     369        3757 :     if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
     370           0 :         return true;
     371             : 
     372        3757 :     return false;
     373             : }
     374             : 
     375             : static bool
     376          10 : PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup)
     377             : {
     378          10 :     HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id);
     379          20 :     TypeSet::TypeList types;
     380          10 :     if (!typeProperty->enumerateTypes(&types)) {
     381           0 :         ReportOutOfMemory(cx);
     382           0 :         return false;
     383             :     }
     384          23 :     for (size_t j = 0; j < types.length(); j++)
     385          13 :         AddTypePropertyId(cx, newGroup, nullptr, id, types[j]);
     386          10 :     return true;
     387             : }
     388             : 
     389             : static PlainObject*
     390           0 : MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
     391             : {
     392           0 :     Rooted<PlainObject*> obj(cx, NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
     393           0 :                                                                  TenuredObject));
     394           0 :     if (!obj)
     395           0 :         return nullptr;
     396             : 
     397           0 :     for (size_t i = 0; i < layout.properties().length(); i++) {
     398           0 :         const UnboxedLayout::Property& property = layout.properties()[i];
     399           0 :         if (!NativeObject::addDataProperty(cx, obj, NameToId(property.name), i, JSPROP_ENUMERATE))
     400           0 :             return nullptr;
     401           0 :         MOZ_ASSERT(obj->slotSpan() == i + 1);
     402           0 :         MOZ_ASSERT(!obj->inDictionaryMode());
     403             :     }
     404             : 
     405           0 :     return obj;
     406             : }
     407             : 
     408             : /* static */ bool
     409           4 : UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
     410             : {
     411           8 :     AutoEnterAnalysis enter(cx);
     412             : 
     413           4 :     UnboxedLayout& layout = group->unboxedLayout();
     414           8 :     Rooted<TaggedProto> proto(cx, group->proto());
     415             : 
     416           4 :     MOZ_ASSERT(!layout.nativeGroup());
     417             : 
     418           8 :     RootedObjectGroup replacementGroup(cx);
     419             : 
     420           4 :     const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
     421             : 
     422             :     // Immediately clear any new script on the group. This is done by replacing
     423             :     // the existing new script with one for a replacement default new group.
     424             :     // This is done so that the size of the replacment group's objects is the
     425             :     // same as that for the unboxed group, so that we do not see polymorphic
     426             :     // slot accesses later on for sites that see converted objects from this
     427             :     // group and objects that were allocated using the replacement new group.
     428           4 :     if (layout.newScript()) {
     429           0 :         MOZ_ASSERT(!layout.isArray());
     430             : 
     431           0 :         replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
     432           0 :         if (!replacementGroup)
     433           0 :             return false;
     434             : 
     435           0 :         PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout);
     436           0 :         if (!templateObject)
     437           0 :             return false;
     438             : 
     439             :         TypeNewScript* replacementNewScript =
     440           0 :             TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
     441           0 :         if (!replacementNewScript)
     442           0 :             return false;
     443             : 
     444           0 :         replacementGroup->setNewScript(replacementNewScript);
     445           0 :         gc::TraceTypeNewScript(replacementGroup);
     446             : 
     447           0 :         group->clearNewScript(cx, replacementGroup);
     448             :     }
     449             : 
     450             :     // Similarly, if this group is keyed to an allocation site, replace its
     451             :     // entry with a new group that has no unboxed layout.
     452           4 :     if (layout.allocationScript()) {
     453           8 :         RootedScript script(cx, layout.allocationScript());
     454           4 :         jsbytecode* pc = layout.allocationPc();
     455             : 
     456           4 :         replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto);
     457           4 :         if (!replacementGroup)
     458           0 :             return false;
     459             : 
     460           4 :         PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
     461           4 :         replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
     462             : 
     463           4 :         JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
     464           8 :         cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
     465          12 :                                                                    replacementGroup);
     466             : 
     467             :         // Clear any baseline information at this opcode which might use the old group.
     468           4 :         if (script->hasBaselineScript()) {
     469           4 :             jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
     470           4 :             jit::ICFallbackStub* fallback = entry.fallbackStub();
     471           8 :             for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
     472           4 :                 iter.unlink(cx);
     473           4 :             if (fallback->isNewObject_Fallback())
     474           4 :                 fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
     475           0 :             else if (fallback->isNewArray_Fallback())
     476           0 :                 fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
     477             :         }
     478             :     }
     479             : 
     480           4 :     size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
     481             : 
     482           4 :     if (layout.isArray()) {
     483             :         // The length shape to use for arrays is cached via a modified initial
     484             :         // shape for array objects. Create an array now to make sure this entry
     485             :         // is instantiated.
     486           0 :         if (!NewDenseEmptyArray(cx))
     487           0 :             return false;
     488             :     }
     489             : 
     490           8 :     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0));
     491           4 :     if (!shape)
     492           0 :         return false;
     493             : 
     494           4 :     MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0);
     495             : 
     496             :     // Add shapes for each property, if this is for a plain object.
     497          14 :     for (size_t i = 0; i < layout.properties().length(); i++) {
     498          10 :         const UnboxedLayout::Property& property = layout.properties()[i];
     499             : 
     500          20 :         Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name),
     501          20 :                                                 i, JSPROP_ENUMERATE, 0));
     502          10 :         shape = cx->zone()->propertyTree().getChild(cx, shape, child);
     503          10 :         if (!shape)
     504           0 :             return false;
     505             :     }
     506             : 
     507             :     ObjectGroup* nativeGroup =
     508           8 :         ObjectGroupCompartment::makeGroup(cx, clasp, proto,
     509           8 :                                           group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
     510           4 :     if (!nativeGroup)
     511           0 :         return false;
     512             : 
     513             :     // No sense propagating if we don't know what we started with.
     514           4 :     if (!group->unknownProperties()) {
     515             :         // Propagate all property types from the old group to the new group.
     516           4 :         if (layout.isArray()) {
     517           0 :             if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
     518           0 :                 return false;
     519             :         } else {
     520          14 :             for (size_t i = 0; i < layout.properties().length(); i++) {
     521          10 :                 const UnboxedLayout::Property& property = layout.properties()[i];
     522          10 :                 jsid id = NameToId(property.name);
     523          10 :                 if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
     524           0 :                     return false;
     525             : 
     526             :                 // If we are OOM we may not be able to propagate properties.
     527          10 :                 if (nativeGroup->unknownProperties())
     528           0 :                     break;
     529             : 
     530          10 :                 HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
     531          10 :                 if (nativeProperty && nativeProperty->canSetDefinite(i))
     532          10 :                     nativeProperty->setDefinite(i);
     533             :             }
     534             :         }
     535             :     } else {
     536             :         // If we skip, though, the new group had better agree.
     537           0 :         MOZ_ASSERT(nativeGroup->unknownProperties());
     538             :     }
     539             : 
     540           4 :     layout.nativeGroup_ = nativeGroup;
     541           4 :     layout.nativeShape_ = shape;
     542           4 :     layout.replacementGroup_ = replacementGroup;
     543             : 
     544           4 :     nativeGroup->setOriginalUnboxedGroup(group);
     545             : 
     546           4 :     group->markStateChange(cx);
     547             : 
     548           4 :     return true;
     549             : }
     550             : 
     551             : /* static */ bool
     552           4 : UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj)
     553             : {
     554           4 :     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
     555           4 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
     556             : 
     557           4 :     if (!layout.nativeGroup()) {
     558           4 :         if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
     559           0 :             return false;
     560             : 
     561             :         // makeNativeGroup can reentrantly invoke this method.
     562           4 :         if (obj->is<PlainObject>())
     563           0 :             return true;
     564             :     }
     565             : 
     566           8 :     AutoValueVector values(cx);
     567          14 :     for (size_t i = 0; i < layout.properties().length(); i++) {
     568             :         // We might be reading properties off the object which have not been
     569             :         // initialized yet. Make sure any double values we read here are
     570             :         // canonicalized.
     571          10 :         if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true)))
     572           0 :             return false;
     573             :     }
     574             : 
     575             :     // We are eliminating the expando edge with the conversion, so trigger a
     576             :     // pre barrier.
     577           4 :     JSObject::writeBarrierPre(expando);
     578             : 
     579             :     // Additionally trigger a post barrier on the expando itself. Whole cell
     580             :     // store buffer entries can be added on the original unboxed object for
     581             :     // writes to the expando (see WholeCellEdges::trace), so after conversion
     582             :     // we need to make sure the expando itself will still be traced.
     583           4 :     if (expando && !IsInsideNursery(expando))
     584           0 :         cx->zone()->group()->storeBuffer().putWholeCell(expando);
     585             : 
     586           4 :     obj->setGroup(layout.nativeGroup());
     587           4 :     obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
     588             : 
     589          14 :     for (size_t i = 0; i < values.length(); i++)
     590          10 :         obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
     591             : 
     592           4 :     if (expando) {
     593             :         // Add properties from the expando object to the object, in order.
     594             :         // Suppress GC here, so that callers don't need to worry about this
     595             :         // method collecting. The stuff below can only fail due to OOM, in
     596             :         // which case the object will not have been completely filled back in.
     597           0 :         gc::AutoSuppressGC suppress(cx);
     598             : 
     599           0 :         Vector<jsid> ids(cx);
     600           0 :         for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
     601           0 :             if (!ids.append(r.front().propid()))
     602           0 :                 return false;
     603             :         }
     604           0 :         for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
     605           0 :             if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
     606           0 :                 if (!ids.append(INT_TO_JSID(i)))
     607           0 :                     return false;
     608             :             }
     609             :         }
     610           0 :         ::Reverse(ids.begin(), ids.end());
     611             : 
     612           0 :         RootedPlainObject nobj(cx, &obj->as<PlainObject>());
     613           0 :         Rooted<UnboxedExpandoObject*> nexpando(cx, expando);
     614           0 :         RootedId id(cx);
     615           0 :         Rooted<PropertyDescriptor> desc(cx);
     616           0 :         for (size_t i = 0; i < ids.length(); i++) {
     617           0 :             id = ids[i];
     618           0 :             if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
     619           0 :                 return false;
     620           0 :             ObjectOpResult result;
     621           0 :             if (!DefineProperty(cx, nobj, id, desc, result))
     622           0 :                 return false;
     623           0 :             MOZ_ASSERT(result.ok());
     624             :         }
     625             :     }
     626             : 
     627           4 :     return true;
     628             : }
     629             : 
     630             : /* static */ JS::Result<UnboxedObject*, JS::OOM&>
     631          52 : UnboxedObject::createInternal(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
     632             :                               js::HandleObjectGroup group)
     633             : {
     634          52 :     const js::Class* clasp = group->clasp();
     635          52 :     MOZ_ASSERT(clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_);
     636             : 
     637          52 :     MOZ_ASSERT(CanBeFinalizedInBackground(kind, clasp));
     638          52 :     kind = GetBackgroundAllocKind(kind);
     639             : 
     640          52 :     debugCheckNewObject(group, /* shape = */ nullptr, kind, heap);
     641             : 
     642          52 :     JSObject* obj = js::Allocate<JSObject>(cx, kind, /* nDynamicSlots = */ 0, heap, clasp);
     643          52 :     if (!obj)
     644           0 :         return cx->alreadyReportedOOM();
     645             : 
     646          52 :     UnboxedObject* uobj = static_cast<UnboxedObject*>(obj);
     647          52 :     uobj->group_.init(group);
     648             : 
     649          52 :     if (clasp->shouldDelayMetadataBuilder())
     650          52 :         cx->compartment()->setObjectPendingMetadata(cx, uobj);
     651             :     else
     652           0 :         uobj = SetNewObjectMetadata(cx, uobj);
     653             : 
     654          52 :     js::gc::TraceCreateObject(uobj);
     655             : 
     656          52 :     return uobj;
     657             : }
     658             : 
     659             : /* static */
     660             : UnboxedPlainObject*
     661          52 : UnboxedPlainObject::create(JSContext* cx, HandleObjectGroup group, NewObjectKind newKind)
     662             : {
     663         104 :     AutoSetNewObjectMetadata metadata(cx);
     664             : 
     665          52 :     MOZ_ASSERT(group->clasp() == &class_);
     666          52 :     gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
     667          52 :     gc::InitialHeap heap = GetInitialHeap(newKind, &class_);
     668             : 
     669          52 :     MOZ_ASSERT(newKind != SingletonObject);
     670             : 
     671             :     UnboxedObject* res_;
     672          52 :     JS_TRY_VAR_OR_RETURN_NULL(cx, res_, createInternal(cx, allocKind, heap, group));
     673          52 :     UnboxedPlainObject* res = &res_->as<UnboxedPlainObject>();
     674             : 
     675             :     // Overwrite the dummy shape which was written to the object's expando field.
     676          52 :     res->initExpando();
     677             : 
     678             :     // Initialize reference fields of the object. All fields in the object will
     679             :     // be overwritten shortly, but references need to be safe for the GC.
     680          52 :     const int32_t* list = res->layout().traceList();
     681          52 :     if (list) {
     682          52 :         uint8_t* data = res->data();
     683          76 :         while (*list != -1) {
     684          12 :             GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
     685          12 :             heap->init(cx->names().empty);
     686          12 :             list++;
     687             :         }
     688          52 :         list++;
     689         156 :         while (*list != -1) {
     690          52 :             GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
     691          52 :             heap->init(nullptr);
     692          52 :             list++;
     693             :         }
     694             :         // Unboxed objects don't have Values to initialize.
     695          52 :         MOZ_ASSERT(*(list + 1) == -1);
     696             :     }
     697             : 
     698          52 :     return res;
     699             : }
     700             : 
     701             : /* static */ JSObject*
     702           0 : UnboxedPlainObject::createWithProperties(JSContext* cx, HandleObjectGroup group,
     703             :                                          NewObjectKind newKind, IdValuePair* properties)
     704             : {
     705           0 :     MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
     706             : 
     707           0 :     UnboxedLayout& layout = group->unboxedLayout();
     708             : 
     709           0 :     if (layout.constructorCode()) {
     710           0 :         MOZ_ASSERT(!cx->helperThread());
     711             : 
     712             :         typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind);
     713             :         ConstructorCodeSignature function =
     714           0 :             reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
     715             : 
     716             :         JSObject* obj;
     717             :         {
     718           0 :             JS::AutoSuppressGCAnalysis nogc;
     719           0 :             obj = reinterpret_cast<JSObject*>(CALL_GENERATED_2(function, properties, newKind));
     720             :         }
     721           0 :         if (obj > reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
     722           0 :             return obj;
     723             : 
     724           0 :         if (obj == reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
     725           0 :             layout.setConstructorCode(nullptr);
     726             :     }
     727             : 
     728           0 :     UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind);
     729           0 :     if (!obj)
     730           0 :         return nullptr;
     731             : 
     732           0 :     for (size_t i = 0; i < layout.properties().length(); i++) {
     733           0 :         if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
     734           0 :             return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
     735             :     }
     736             : 
     737             : #ifndef JS_CODEGEN_NONE
     738           0 :     if (!cx->helperThread() &&
     739           0 :         !group->unknownProperties() &&
     740           0 :         !layout.constructorCode() &&
     741           0 :         cx->runtime()->jitSupportsFloatingPoint &&
     742           0 :         jit::CanLikelyAllocateMoreExecutableMemory())
     743             :     {
     744           0 :         if (!UnboxedLayout::makeConstructorCode(cx, group))
     745           0 :             return nullptr;
     746             :     }
     747             : #endif
     748             : 
     749           0 :     return obj;
     750             : }
     751             : 
     752             : /* static */ bool
     753           0 : UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
     754             :                                        HandleId id, MutableHandleObject objp,
     755             :                                        MutableHandle<PropertyResult> propp)
     756             : {
     757           0 :     if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
     758           0 :         propp.setNonNativeProperty();
     759           0 :         objp.set(obj);
     760           0 :         return true;
     761             :     }
     762             : 
     763           0 :     RootedObject proto(cx, obj->staticPrototype());
     764           0 :     if (!proto) {
     765           0 :         objp.set(nullptr);
     766           0 :         propp.setNotFound();
     767           0 :         return true;
     768             :     }
     769             : 
     770           0 :     return LookupProperty(cx, proto, id, objp, propp);
     771             : }
     772             : 
     773             : /* static */ bool
     774           0 : UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
     775             :                                        Handle<PropertyDescriptor> desc,
     776             :                                        ObjectOpResult& result)
     777             : {
     778           0 :     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
     779             : 
     780           0 :     if (const UnboxedLayout::Property* property = layout.lookup(id)) {
     781           0 :         if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
     782             :             // This define is equivalent to setting an existing property.
     783           0 :             if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value()))
     784           0 :                 return result.succeed();
     785             :         }
     786             : 
     787             :         // Trying to incompatibly redefine an existing property requires the
     788             :         // object to be converted to a native object.
     789           0 :         if (!convertToNative(cx, obj))
     790           0 :             return false;
     791             : 
     792           0 :         return DefineProperty(cx, obj, id, desc, result);
     793             :     }
     794             : 
     795             :     // Define the property on the expando object.
     796           0 :     Rooted<UnboxedExpandoObject*> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
     797           0 :     if (!expando)
     798           0 :         return false;
     799             : 
     800             :     // Update property types on the unboxed object as well.
     801           0 :     AddTypePropertyId(cx, obj, id, desc.value());
     802             : 
     803           0 :     return DefineProperty(cx, expando, id, desc, result);
     804             : }
     805             : 
     806             : /* static */ bool
     807        7566 : UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
     808             : {
     809        7566 :     if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
     810        3809 :         *foundp = true;
     811        3809 :         return true;
     812             :     }
     813             : 
     814        7514 :     RootedObject proto(cx, obj->staticPrototype());
     815        3757 :     if (!proto) {
     816           0 :         *foundp = false;
     817           0 :         return true;
     818             :     }
     819             : 
     820        3757 :     return HasProperty(cx, proto, id, foundp);
     821             : }
     822             : 
     823             : /* static */ bool
     824        3889 : UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
     825             :                                     HandleId id, MutableHandleValue vp)
     826             : {
     827        3889 :     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
     828             : 
     829        3889 :     if (const UnboxedLayout::Property* property = layout.lookup(id)) {
     830        3889 :         vp.set(obj->as<UnboxedPlainObject>().getValue(*property));
     831        3889 :         return true;
     832             :     }
     833             : 
     834           0 :     if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
     835           0 :         if (expando->containsShapeOrElement(cx, id)) {
     836           0 :             RootedObject nexpando(cx, expando);
     837           0 :             return GetProperty(cx, nexpando, receiver, id, vp);
     838             :         }
     839             :     }
     840             : 
     841           0 :     RootedObject proto(cx, obj->staticPrototype());
     842           0 :     if (!proto) {
     843           0 :         vp.setUndefined();
     844           0 :         return true;
     845             :     }
     846             : 
     847           0 :     return GetProperty(cx, proto, receiver, id, vp);
     848             : }
     849             : 
     850             : /* static */ bool
     851          83 : UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
     852             :                                     HandleValue receiver, ObjectOpResult& result)
     853             : {
     854          83 :     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
     855             : 
     856          83 :     if (const UnboxedLayout::Property* property = layout.lookup(id)) {
     857          83 :         if (receiver.isObject() && obj == &receiver.toObject()) {
     858          83 :             if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
     859          79 :                 return result.succeed();
     860             : 
     861           4 :             if (!convertToNative(cx, obj))
     862           0 :                 return false;
     863           4 :             return SetProperty(cx, obj, id, v, receiver, result);
     864             :         }
     865             : 
     866           0 :         return SetPropertyByDefining(cx, id, v, receiver, result);
     867             :     }
     868             : 
     869           0 :     if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
     870           0 :         if (expando->containsShapeOrElement(cx, id)) {
     871             :             // Update property types on the unboxed object as well.
     872           0 :             AddTypePropertyId(cx, obj, id, v);
     873             : 
     874           0 :             RootedObject nexpando(cx, expando);
     875           0 :             return SetProperty(cx, nexpando, id, v, receiver, result);
     876             :         }
     877             :     }
     878             : 
     879           0 :     return SetPropertyOnProto(cx, obj, id, v, receiver, result);
     880             : }
     881             : 
     882             : /* static */ bool
     883           0 : UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
     884             :                                                  MutableHandle<PropertyDescriptor> desc)
     885             : {
     886           0 :     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
     887             : 
     888           0 :     if (const UnboxedLayout::Property* property = layout.lookup(id)) {
     889           0 :         desc.value().set(obj->as<UnboxedPlainObject>().getValue(*property));
     890           0 :         desc.setAttributes(JSPROP_ENUMERATE);
     891           0 :         desc.object().set(obj);
     892           0 :         return true;
     893             :     }
     894             : 
     895           0 :     if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
     896           0 :         if (expando->containsShapeOrElement(cx, id)) {
     897           0 :             RootedObject nexpando(cx, expando);
     898           0 :             if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc))
     899           0 :                 return false;
     900           0 :             if (desc.object() == nexpando)
     901           0 :                 desc.object().set(obj);
     902           0 :             return true;
     903             :         }
     904             :     }
     905             : 
     906           0 :     desc.object().set(nullptr);
     907           0 :     return true;
     908             : }
     909             : 
     910             : /* static */ bool
     911           0 : UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
     912             :                                        ObjectOpResult& result)
     913             : {
     914           0 :     if (!convertToNative(cx, obj))
     915           0 :         return false;
     916           0 :     return DeleteProperty(cx, obj, id, result);
     917             : }
     918             : 
     919             : /* static */ bool
     920           0 : UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
     921             : {
     922           0 :     if (!convertToNative(cx, obj))
     923           0 :         return false;
     924           0 :     return WatchProperty(cx, obj, id, callable);
     925             : }
     926             : 
     927             : /* static */ bool
     928           0 : UnboxedPlainObject::newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
     929             :                                  bool enumerableOnly)
     930             : {
     931             :     // Ignore expando properties here, they are special-cased by the property
     932             :     // enumeration code.
     933             : 
     934           0 :     const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
     935           0 :     for (size_t i = 0; i < unboxed.length(); i++) {
     936           0 :         if (!properties.append(NameToId(unboxed[i].name)))
     937           0 :             return false;
     938             :     }
     939             : 
     940           0 :     return true;
     941             : }
     942             : 
     943             : const Class UnboxedExpandoObject::class_ = {
     944             :     "UnboxedExpandoObject",
     945             :     0
     946             : };
     947             : 
     948             : static const ClassOps UnboxedPlainObjectClassOps = {
     949             :     nullptr,        /* addProperty */
     950             :     nullptr,        /* delProperty */
     951             :     nullptr,        /* getProperty */
     952             :     nullptr,        /* setProperty */
     953             :     nullptr,        /* enumerate   */
     954             :     UnboxedPlainObject::newEnumerate,
     955             :     nullptr,        /* resolve     */
     956             :     nullptr,        /* mayResolve  */
     957             :     nullptr,        /* finalize    */
     958             :     nullptr,        /* call        */
     959             :     nullptr,        /* hasInstance */
     960             :     nullptr,        /* construct   */
     961             :     UnboxedPlainObject::trace,
     962             : };
     963             : 
     964             : static const ObjectOps UnboxedPlainObjectObjectOps = {
     965             :     UnboxedPlainObject::obj_lookupProperty,
     966             :     UnboxedPlainObject::obj_defineProperty,
     967             :     UnboxedPlainObject::obj_hasProperty,
     968             :     UnboxedPlainObject::obj_getProperty,
     969             :     UnboxedPlainObject::obj_setProperty,
     970             :     UnboxedPlainObject::obj_getOwnPropertyDescriptor,
     971             :     UnboxedPlainObject::obj_deleteProperty,
     972             :     UnboxedPlainObject::obj_watch,
     973             :     nullptr,   /* No unwatch needed, as watch() converts the object to native */
     974             :     nullptr,   /* getElements */
     975             :     nullptr    /* funToString */
     976             : };
     977             : 
     978             : const Class UnboxedPlainObject::class_ = {
     979             :     js_Object_str,
     980             :     Class::NON_NATIVE |
     981             :     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     982             :     JSCLASS_DELAY_METADATA_BUILDER,
     983             :     &UnboxedPlainObjectClassOps,
     984             :     JS_NULL_CLASS_SPEC,
     985             :     JS_NULL_CLASS_EXT,
     986             :     &UnboxedPlainObjectObjectOps
     987             : };
     988             : 
     989             : /////////////////////////////////////////////////////////////////////
     990             : // UnboxedArrayObject
     991             : /////////////////////////////////////////////////////////////////////
     992             : 
     993             : template <JSValueType Type>
     994             : DenseElementResult
     995           0 : AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen,
     996             :                            MutableHandle<GCVector<Value>> values)
     997             : {
     998           0 :     for (size_t i = 0; i < initlen; i++)
     999           0 :         values.infallibleAppend(obj->getElementSpecific<Type>(i));
    1000           0 :     return DenseElementResult::Success;
    1001             : }
    1002             : 
    1003           0 : DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
    1004             :                              UnboxedArrayObject*, uint32_t, MutableHandle<GCVector<Value>>);
    1005             : 
    1006             : /* static */ bool
    1007           0 : UnboxedArrayObject::convertToNativeWithGroup(JSContext* cx, JSObject* obj,
    1008             :                                              ObjectGroup* group, Shape* shape)
    1009             : {
    1010           0 :     size_t length = obj->as<UnboxedArrayObject>().length();
    1011           0 :     size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
    1012             : 
    1013           0 :     Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
    1014           0 :     if (!values.reserve(initlen))
    1015           0 :         return false;
    1016             : 
    1017           0 :     AppendUnboxedDenseElementsFunctor functor(&obj->as<UnboxedArrayObject>(), initlen, &values);
    1018           0 :     DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj);
    1019           0 :     MOZ_ASSERT(result.value == DenseElementResult::Success);
    1020             : 
    1021           0 :     obj->setGroup(group);
    1022             : 
    1023           0 :     ArrayObject* aobj = &obj->as<ArrayObject>();
    1024           0 :     aobj->setLastPropertyMakeNative(cx, shape);
    1025             : 
    1026             :     // Make sure there is at least one element, so that this array does not
    1027             :     // use emptyObjectElements / emptyObjectElementsShared.
    1028           0 :     if (!aobj->ensureElements(cx, Max<size_t>(initlen, 1)))
    1029           0 :         return false;
    1030             : 
    1031           0 :     MOZ_ASSERT(!aobj->getDenseInitializedLength());
    1032           0 :     aobj->setDenseInitializedLength(initlen);
    1033           0 :     aobj->initDenseElements(0, values.begin(), initlen);
    1034           0 :     aobj->setLengthInt32(length);
    1035             : 
    1036           0 :     return true;
    1037             : }
    1038             : 
    1039             : /* static */ bool
    1040           0 : UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
    1041             : {
    1042           0 :     const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
    1043             : 
    1044           0 :     if (!layout.nativeGroup()) {
    1045           0 :         if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
    1046           0 :             return false;
    1047             :     }
    1048             : 
    1049           0 :     return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
    1050             : }
    1051             : 
    1052             : bool
    1053           0 : UnboxedArrayObject::convertInt32ToDouble(JSContext* cx, ObjectGroup* group)
    1054             : {
    1055           0 :     MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
    1056           0 :     MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
    1057             : 
    1058           0 :     Vector<int32_t> values(cx);
    1059           0 :     if (!values.reserve(initializedLength()))
    1060           0 :         return false;
    1061           0 :     for (size_t i = 0; i < initializedLength(); i++)
    1062           0 :         values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32());
    1063             : 
    1064             :     uint8_t* newElements;
    1065           0 :     if (hasInlineElements()) {
    1066           0 :         newElements = AllocateObjectBuffer<uint8_t>(cx, this, capacity() * sizeof(double));
    1067             :     } else {
    1068           0 :         newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
    1069           0 :                                                       capacity() * sizeof(int32_t),
    1070           0 :                                                       capacity() * sizeof(double));
    1071             :     }
    1072           0 :     if (!newElements)
    1073           0 :         return false;
    1074             : 
    1075           0 :     setGroup(group);
    1076           0 :     elements_ = newElements;
    1077             : 
    1078           0 :     for (size_t i = 0; i < initializedLength(); i++)
    1079           0 :         setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i]));
    1080             : 
    1081           0 :     return true;
    1082             : }
    1083             : 
    1084             : /* static */ UnboxedArrayObject*
    1085           0 : UnboxedArrayObject::create(JSContext* cx, HandleObjectGroup group, uint32_t length,
    1086             :                            NewObjectKind newKind, uint32_t maxLength)
    1087             : {
    1088           0 :     MOZ_ASSERT(length <= MaximumCapacity);
    1089             : 
    1090           0 :     MOZ_ASSERT(group->clasp() == &class_);
    1091           0 :     uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
    1092           0 :     uint32_t capacity = Min(length, maxLength);
    1093           0 :     uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
    1094             : 
    1095           0 :     gc::InitialHeap heap = GetInitialHeap(newKind, &class_);
    1096             : 
    1097             :     UnboxedArrayObject* res;
    1098           0 :     if (nbytes <= JSObject::MAX_BYTE_SIZE) {
    1099           0 :         gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes);
    1100             : 
    1101             :         // If there was no provided length information, pick an allocation kind
    1102             :         // to accommodate small arrays (as is done for normal native arrays).
    1103           0 :         if (capacity == 0)
    1104           0 :             allocKind = gc::AllocKind::OBJECT8;
    1105             : 
    1106             :         UnboxedObject* res_;
    1107           0 :         JS_TRY_VAR_OR_RETURN_NULL(cx, res_, createInternal(cx, allocKind, heap, group));
    1108           0 :         res = &res_->as<UnboxedArrayObject>();
    1109             : 
    1110           0 :         res->setInitializedLengthNoBarrier(0);
    1111           0 :         res->setInlineElements();
    1112             : 
    1113           0 :         size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
    1114           0 :         MOZ_ASSERT(actualCapacity >= capacity);
    1115           0 :         res->setCapacityIndex(exactCapacityIndex(actualCapacity));
    1116             :     } else {
    1117             :         UnboxedObject* res_;
    1118           0 :         JS_TRY_VAR_OR_RETURN_NULL(cx, res_,
    1119             :                                   createInternal(cx, gc::AllocKind::OBJECT0, heap, group));
    1120           0 :         res = &res_->as<UnboxedArrayObject>();
    1121             : 
    1122           0 :         res->setInitializedLengthNoBarrier(0);
    1123             : 
    1124             :         uint32_t capacityIndex = (capacity == length)
    1125           0 :                                  ? CapacityMatchesLengthIndex
    1126           0 :                                  : chooseCapacityIndex(capacity, length);
    1127           0 :         uint32_t actualCapacity = computeCapacity(capacityIndex, length);
    1128             : 
    1129           0 :         res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
    1130           0 :         if (!res->elements_) {
    1131             :             // Make the object safe for GC.
    1132           0 :             res->setInlineElements();
    1133           0 :             return nullptr;
    1134             :         }
    1135             : 
    1136           0 :         res->setCapacityIndex(capacityIndex);
    1137             :     }
    1138             : 
    1139           0 :     res->setLength(cx, length);
    1140           0 :     return res;
    1141             : }
    1142             : 
    1143             : bool
    1144           0 : UnboxedArrayObject::setElement(JSContext* cx, size_t index, const Value& v)
    1145             : {
    1146           0 :     MOZ_ASSERT(index < initializedLength());
    1147           0 :     uint8_t* p = elements() + index * elementSize();
    1148           0 :     return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
    1149             : }
    1150             : 
    1151             : bool
    1152           0 : UnboxedArrayObject::initElement(JSContext* cx, size_t index, const Value& v)
    1153             : {
    1154           0 :     MOZ_ASSERT(index < initializedLength());
    1155           0 :     uint8_t* p = elements() + index * elementSize();
    1156           0 :     return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
    1157             : }
    1158             : 
    1159             : void
    1160           0 : UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v)
    1161             : {
    1162           0 :     MOZ_ASSERT(index < initializedLength());
    1163           0 :     uint8_t* p = elements() + index * elementSize();
    1164           0 :     if (UnboxedTypeNeedsPreBarrier(elementType()))
    1165           0 :         *reinterpret_cast<void**>(p) = nullptr;
    1166           0 :     SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
    1167           0 : }
    1168             : 
    1169             : Value
    1170           0 : UnboxedArrayObject::getElement(size_t index)
    1171             : {
    1172           0 :     MOZ_ASSERT(index < initializedLength());
    1173           0 :     uint8_t* p = elements() + index * elementSize();
    1174           0 :     return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false);
    1175             : }
    1176             : 
    1177             : /* static */ void
    1178           0 : UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj)
    1179             : {
    1180           0 :     JSValueType type = obj->as<UnboxedArrayObject>().elementType();
    1181           0 :     if (!UnboxedTypeNeedsPreBarrier(type))
    1182           0 :         return;
    1183             : 
    1184           0 :     MOZ_ASSERT(obj->as<UnboxedArrayObject>().elementSize() == sizeof(uintptr_t));
    1185           0 :     size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
    1186           0 :     void** elements = reinterpret_cast<void**>(obj->as<UnboxedArrayObject>().elements());
    1187             : 
    1188           0 :     switch (type) {
    1189             :       case JSVAL_TYPE_OBJECT:
    1190           0 :         for (size_t i = 0; i < initlen; i++) {
    1191           0 :             GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(elements + i);
    1192           0 :             TraceNullableEdge(trc, heap, "unboxed_object");
    1193             :         }
    1194           0 :         break;
    1195             : 
    1196             :       case JSVAL_TYPE_STRING:
    1197           0 :         for (size_t i = 0; i < initlen; i++) {
    1198           0 :             GCPtrString* heap = reinterpret_cast<GCPtrString*>(elements + i);
    1199           0 :             TraceEdge(trc, heap, "unboxed_string");
    1200             :         }
    1201           0 :         break;
    1202             : 
    1203             :       default:
    1204           0 :         MOZ_CRASH();
    1205             :     }
    1206             : }
    1207             : 
    1208             : /* static */ void
    1209           0 : UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
    1210             : {
    1211           0 :     UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>();
    1212           0 :     const UnboxedArrayObject& src = old->as<UnboxedArrayObject>();
    1213             : 
    1214             :     // Fix up possible inline data pointer.
    1215           0 :     if (src.hasInlineElements())
    1216           0 :         dst.setInlineElements();
    1217           0 : }
    1218             : 
    1219             : /* static */ void
    1220           0 : UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
    1221             : {
    1222           0 :     MOZ_ASSERT(!IsInsideNursery(obj));
    1223           0 :     if (!obj->as<UnboxedArrayObject>().hasInlineElements())
    1224           0 :         js_free(obj->as<UnboxedArrayObject>().elements());
    1225           0 : }
    1226             : 
    1227             : /* static */ size_t
    1228           0 : UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
    1229             :                                              gc::AllocKind allocKind)
    1230             : {
    1231           0 :     UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
    1232           0 :     UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
    1233           0 :     MOZ_ASSERT(ndst->elements() == nsrc->elements());
    1234             : 
    1235           0 :     Nursery& nursery = dst->zone()->group()->nursery();
    1236             : 
    1237           0 :     if (!nursery.isInside(nsrc->elements())) {
    1238           0 :         nursery.removeMallocedBuffer(nsrc->elements());
    1239           0 :         return 0;
    1240             :     }
    1241             : 
    1242             :     // Determine if we can use inline data for the target array. If this is
    1243             :     // possible, the nursery will have picked an allocation size that is large
    1244             :     // enough.
    1245           0 :     size_t nbytes = nsrc->capacity() * nsrc->elementSize();
    1246           0 :     if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
    1247           0 :         ndst->setInlineElements();
    1248             :     } else {
    1249           0 :         MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
    1250             : 
    1251           0 :         AutoEnterOOMUnsafeRegion oomUnsafe;
    1252           0 :         uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
    1253           0 :         if (!data)
    1254           0 :             oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring.");
    1255           0 :         ndst->elements_ = data;
    1256             :     }
    1257             : 
    1258           0 :     PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
    1259             : 
    1260             :     // Set a forwarding pointer for the element buffers in case they were
    1261             :     // preserved on the stack by Ion.
    1262           0 :     bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
    1263           0 :     nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
    1264             : 
    1265           0 :     return ndst->hasInlineElements() ? 0 : nbytes;
    1266             : }
    1267             : 
    1268             : // Possible capacities for unboxed arrays. Some of these capacities might seem
    1269             : // a little weird, but were chosen to allow the inline data of objects of each
    1270             : // size to be fully utilized for arrays of the various types on both 32 bit and
    1271             : // 64 bit platforms.
    1272             : //
    1273             : // To find the possible inline capacities, the following script was used:
    1274             : //
    1275             : // var fixedSlotCapacities = [0, 2, 4, 8, 12, 16];
    1276             : // var dataSizes = [1, 4, 8];
    1277             : // var header32 = 4 * 2 + 4 * 2;
    1278             : // var header64 = 8 * 2 + 4 * 2;
    1279             : //
    1280             : // for (var i = 0; i < fixedSlotCapacities.length; i++) {
    1281             : //    var nfixed = fixedSlotCapacities[i];
    1282             : //    var size32 = 4 * 4 + 8 * nfixed - header32;
    1283             : //    var size64 = 8 * 4 + 8 * nfixed - header64;
    1284             : //    for (var j = 0; j < dataSizes.length; j++) {
    1285             : //        print(size32 / dataSizes[j]);
    1286             : //        print(size64 / dataSizes[j]);
    1287             : //    }
    1288             : // }
    1289             : //
    1290             : /* static */ const uint32_t
    1291             : UnboxedArrayObject::CapacityArray[] = {
    1292             :     UINT32_MAX, // For CapacityMatchesLengthIndex.
    1293             :     0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 24, 26, 32, 34, 40, 64, 72, 96, 104, 128, 136,
    1294             :     256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
    1295             :     1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336,
    1296             :     13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464,
    1297             :     46137344, 52428800, 59768832, MaximumCapacity
    1298             : };
    1299             : 
    1300             : static const uint32_t
    1301             : Pow2CapacityIndexes[] = {
    1302             :     2,  // 1
    1303             :     3,  // 2
    1304             :     5,  // 4
    1305             :     8,  // 8
    1306             :     13, // 16
    1307             :     18, // 32
    1308             :     21, // 64
    1309             :     25, // 128
    1310             :     27, // 256
    1311             :     28, // 512
    1312             :     29, // 1024
    1313             :     30, // 2048
    1314             :     31, // 4096
    1315             :     32, // 8192
    1316             :     33, // 16384
    1317             :     34, // 32768
    1318             :     35, // 65536
    1319             :     36, // 131072
    1320             :     37, // 262144
    1321             :     38, // 524288
    1322             :     39  // 1048576
    1323             : };
    1324             : 
    1325             : static const uint32_t MebiCapacityIndex = 39;
    1326             : 
    1327             : /* static */ uint32_t
    1328           0 : UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length)
    1329             : {
    1330             :     // Note: the structure and behavior of this method follow along with
    1331             :     // NativeObject::goodAllocated. Changes to the allocation strategy in one
    1332             :     // should generally be matched by the other.
    1333             : 
    1334             :     // Make sure we have enough space to store all possible values for the capacity index.
    1335             :     // This ought to be a static_assert, but MSVC doesn't like that.
    1336           0 :     MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift));
    1337             : 
    1338             :     // The caller should have ensured the capacity is possible for an unboxed array.
    1339           0 :     MOZ_ASSERT(capacity <= MaximumCapacity);
    1340             : 
    1341             :     static const uint32_t Mebi = 1024 * 1024;
    1342             : 
    1343           0 :     if (capacity <= Mebi) {
    1344           0 :         capacity = mozilla::RoundUpPow2(capacity);
    1345             : 
    1346             :         // When the required capacity is close to the array length, then round
    1347             :         // up to the array length itself, as for NativeObject.
    1348           0 :         if (length >= capacity && capacity > (length / 3) * 2)
    1349           0 :             return CapacityMatchesLengthIndex;
    1350             : 
    1351           0 :         if (capacity < MinimumDynamicCapacity)
    1352           0 :             capacity = MinimumDynamicCapacity;
    1353             : 
    1354           0 :         uint32_t bit = mozilla::FloorLog2Size(capacity);
    1355           0 :         MOZ_ASSERT(capacity == uint32_t(1 << bit));
    1356           0 :         MOZ_ASSERT(bit <= 20);
    1357           0 :         MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21);
    1358             : 
    1359           0 :         uint32_t index = Pow2CapacityIndexes[bit];
    1360           0 :         MOZ_ASSERT(CapacityArray[index] == capacity);
    1361             : 
    1362           0 :         return index;
    1363             :     }
    1364             : 
    1365           0 :     MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi);
    1366             : 
    1367           0 :     for (uint32_t i = MebiCapacityIndex + 1;; i++) {
    1368           0 :         if (CapacityArray[i] >= capacity)
    1369           0 :             return i;
    1370             :     }
    1371             : 
    1372             :     MOZ_CRASH("Invalid capacity");
    1373             : }
    1374             : 
    1375             : /* static */ uint32_t
    1376           0 : UnboxedArrayObject::exactCapacityIndex(uint32_t capacity)
    1377             : {
    1378           0 :     for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) {
    1379           0 :         if (CapacityArray[i] == capacity)
    1380           0 :             return i;
    1381             :     }
    1382           0 :     MOZ_CRASH();
    1383             : }
    1384             : 
    1385             : bool
    1386           0 : UnboxedArrayObject::growElements(JSContext* cx, size_t cap)
    1387             : {
    1388             :     // The caller should have checked if this capacity is possible for an
    1389             :     // unboxed array, so the only way this call can fail is from OOM.
    1390           0 :     MOZ_ASSERT(cap <= MaximumCapacity);
    1391             : 
    1392           0 :     uint32_t oldCapacity = capacity();
    1393           0 :     uint32_t newCapacityIndex = chooseCapacityIndex(cap, length());
    1394           0 :     uint32_t newCapacity = computeCapacity(newCapacityIndex, length());
    1395             : 
    1396           0 :     MOZ_ASSERT(oldCapacity < cap);
    1397           0 :     MOZ_ASSERT(cap <= newCapacity);
    1398             : 
    1399             :     // The allocation size computation below cannot have integer overflows.
    1400             :     JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double));
    1401             : 
    1402             :     uint8_t* newElements;
    1403           0 :     if (hasInlineElements()) {
    1404           0 :         newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize());
    1405           0 :         if (!newElements)
    1406           0 :             return false;
    1407           0 :         js_memcpy(newElements, elements(), initializedLength() * elementSize());
    1408             :     } else {
    1409           0 :         newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
    1410           0 :                                                       oldCapacity * elementSize(),
    1411           0 :                                                       newCapacity * elementSize());
    1412           0 :         if (!newElements)
    1413           0 :             return false;
    1414             :     }
    1415             : 
    1416           0 :     elements_ = newElements;
    1417           0 :     setCapacityIndex(newCapacityIndex);
    1418             : 
    1419           0 :     return true;
    1420             : }
    1421             : 
    1422             : void
    1423           0 : UnboxedArrayObject::shrinkElements(JSContext* cx, size_t cap)
    1424             : {
    1425           0 :     if (hasInlineElements())
    1426           0 :         return;
    1427             : 
    1428           0 :     uint32_t oldCapacity = capacity();
    1429           0 :     uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0);
    1430           0 :     uint32_t newCapacity = computeCapacity(newCapacityIndex, 0);
    1431             : 
    1432           0 :     MOZ_ASSERT(cap < oldCapacity);
    1433           0 :     MOZ_ASSERT(cap <= newCapacity);
    1434             : 
    1435           0 :     if (newCapacity >= oldCapacity)
    1436           0 :         return;
    1437             : 
    1438           0 :     uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
    1439           0 :                                                            oldCapacity * elementSize(),
    1440           0 :                                                            newCapacity * elementSize());
    1441           0 :     if (!newElements)
    1442           0 :         return;
    1443             : 
    1444           0 :     elements_ = newElements;
    1445           0 :     setCapacityIndex(newCapacityIndex);
    1446             : }
    1447             : 
    1448             : bool
    1449           0 : UnboxedArrayObject::containsProperty(JSContext* cx, jsid id)
    1450             : {
    1451           0 :     if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
    1452           0 :         return true;
    1453           0 :     if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
    1454           0 :         return true;
    1455           0 :     return false;
    1456             : }
    1457             : 
    1458             : /* static */ bool
    1459           0 : UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
    1460             :                                        HandleId id, MutableHandleObject objp,
    1461             :                                        MutableHandle<PropertyResult> propp)
    1462             : {
    1463           0 :     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
    1464           0 :         propp.setNonNativeProperty();
    1465           0 :         objp.set(obj);
    1466           0 :         return true;
    1467             :     }
    1468             : 
    1469           0 :     RootedObject proto(cx, obj->staticPrototype());
    1470           0 :     if (!proto) {
    1471           0 :         objp.set(nullptr);
    1472           0 :         propp.setNotFound();
    1473           0 :         return true;
    1474             :     }
    1475             : 
    1476           0 :     return LookupProperty(cx, proto, id, objp, propp);
    1477             : }
    1478             : 
    1479             : /* static */ bool
    1480           0 : UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
    1481             :                                        Handle<PropertyDescriptor> desc,
    1482             :                                        ObjectOpResult& result)
    1483             : {
    1484           0 :     if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
    1485           0 :         UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
    1486             : 
    1487           0 :         uint32_t index = JSID_TO_INT(id);
    1488           0 :         if (index < nobj->initializedLength()) {
    1489           0 :             if (nobj->setElement(cx, index, desc.value()))
    1490           0 :                 return result.succeed();
    1491           0 :         } else if (index == nobj->initializedLength() && index < MaximumCapacity) {
    1492           0 :             if (nobj->initializedLength() == nobj->capacity()) {
    1493           0 :                 if (!nobj->growElements(cx, index + 1))
    1494           0 :                     return false;
    1495             :             }
    1496           0 :             nobj->setInitializedLength(index + 1);
    1497           0 :             if (nobj->initElement(cx, index, desc.value())) {
    1498           0 :                 if (nobj->length() <= index)
    1499           0 :                     nobj->setLengthInt32(index + 1);
    1500           0 :                 return result.succeed();
    1501             :             }
    1502           0 :             nobj->setInitializedLengthNoBarrier(index);
    1503             :         }
    1504             :     }
    1505             : 
    1506           0 :     if (!convertToNative(cx, obj))
    1507           0 :         return false;
    1508             : 
    1509           0 :     return DefineProperty(cx, obj, id, desc, result);
    1510             : }
    1511             : 
    1512             : /* static */ bool
    1513           0 : UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
    1514             : {
    1515           0 :     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
    1516           0 :         *foundp = true;
    1517           0 :         return true;
    1518             :     }
    1519             : 
    1520           0 :     RootedObject proto(cx, obj->staticPrototype());
    1521           0 :     if (!proto) {
    1522           0 :         *foundp = false;
    1523           0 :         return true;
    1524             :     }
    1525             : 
    1526           0 :     return HasProperty(cx, proto, id, foundp);
    1527             : }
    1528             : 
    1529             : /* static */ bool
    1530           0 : UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
    1531             :                                     HandleId id, MutableHandleValue vp)
    1532             : {
    1533           0 :     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
    1534           0 :         if (JSID_IS_INT(id))
    1535           0 :             vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
    1536             :         else
    1537           0 :             vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
    1538           0 :         return true;
    1539             :     }
    1540             : 
    1541           0 :     RootedObject proto(cx, obj->staticPrototype());
    1542           0 :     if (!proto) {
    1543           0 :         vp.setUndefined();
    1544           0 :         return true;
    1545             :     }
    1546             : 
    1547           0 :     return GetProperty(cx, proto, receiver, id, vp);
    1548             : }
    1549             : 
    1550             : /* static */ bool
    1551           0 : UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
    1552             :                                     HandleValue receiver, ObjectOpResult& result)
    1553             : {
    1554           0 :     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
    1555           0 :         if (receiver.isObject() && obj == &receiver.toObject()) {
    1556           0 :             if (JSID_IS_INT(id)) {
    1557           0 :                 if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v))
    1558           0 :                     return result.succeed();
    1559             :             } else {
    1560             :                 uint32_t len;
    1561           0 :                 if (!CanonicalizeArrayLengthValue(cx, v, &len))
    1562           0 :                     return false;
    1563           0 :                 UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
    1564           0 :                 if (len < nobj->initializedLength()) {
    1565           0 :                     nobj->setInitializedLength(len);
    1566           0 :                     nobj->shrinkElements(cx, len);
    1567             :                 }
    1568           0 :                 nobj->setLength(cx, len);
    1569           0 :                 return result.succeed();
    1570             :             }
    1571             : 
    1572           0 :             if (!convertToNative(cx, obj))
    1573           0 :                 return false;
    1574           0 :             return SetProperty(cx, obj, id, v, receiver, result);
    1575             :         }
    1576             : 
    1577           0 :         return SetPropertyByDefining(cx, id, v, receiver, result);
    1578             :     }
    1579             : 
    1580           0 :     return SetPropertyOnProto(cx, obj, id, v, receiver, result);
    1581             : }
    1582             : 
    1583             : /* static */ bool
    1584           0 : UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
    1585             :                                                  MutableHandle<PropertyDescriptor> desc)
    1586             : {
    1587           0 :     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
    1588           0 :         if (JSID_IS_INT(id)) {
    1589           0 :             desc.value().set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
    1590           0 :             desc.setAttributes(JSPROP_ENUMERATE);
    1591             :         } else {
    1592           0 :             desc.value().set(Int32Value(obj->as<UnboxedArrayObject>().length()));
    1593           0 :             desc.setAttributes(JSPROP_PERMANENT);
    1594             :         }
    1595           0 :         desc.object().set(obj);
    1596           0 :         return true;
    1597             :     }
    1598             : 
    1599           0 :     desc.object().set(nullptr);
    1600           0 :     return true;
    1601             : }
    1602             : 
    1603             : /* static */ bool
    1604           0 : UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
    1605             :                                        ObjectOpResult& result)
    1606             : {
    1607           0 :     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
    1608           0 :         size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
    1609           0 :         if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
    1610           0 :             obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
    1611           0 :             obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
    1612           0 :             return result.succeed();
    1613             :         }
    1614             :     }
    1615             : 
    1616           0 :     if (!convertToNative(cx, obj))
    1617           0 :         return false;
    1618           0 :     return DeleteProperty(cx, obj, id, result);
    1619             : }
    1620             : 
    1621             : /* static */ bool
    1622           0 : UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
    1623             : {
    1624           0 :     if (!convertToNative(cx, obj))
    1625           0 :         return false;
    1626           0 :     return WatchProperty(cx, obj, id, callable);
    1627             : }
    1628             : 
    1629             : /* static */ bool
    1630           0 : UnboxedArrayObject::newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
    1631             :                                  bool enumerableOnly)
    1632             : {
    1633           0 :     for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) {
    1634           0 :         if (!properties.append(INT_TO_JSID(i)))
    1635           0 :             return false;
    1636             :     }
    1637             : 
    1638           0 :     if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
    1639           0 :         return false;
    1640             : 
    1641           0 :     return true;
    1642             : }
    1643             : 
    1644             : static const ClassOps UnboxedArrayObjectClassOps = {
    1645             :     nullptr,        /* addProperty */
    1646             :     nullptr,        /* delProperty */
    1647             :     nullptr,        /* getProperty */
    1648             :     nullptr,        /* setProperty */
    1649             :     nullptr,        /* enumerate   */
    1650             :     UnboxedArrayObject::newEnumerate,
    1651             :     nullptr,        /* resolve     */
    1652             :     nullptr,        /* mayResolve  */
    1653             :     UnboxedArrayObject::finalize,
    1654             :     nullptr,        /* call        */
    1655             :     nullptr,        /* hasInstance */
    1656             :     nullptr,        /* construct   */
    1657             :     UnboxedArrayObject::trace,
    1658             : };
    1659             : 
    1660             : static const ClassExtension UnboxedArrayObjectClassExtension = {
    1661             :     nullptr,    /* weakmapKeyDelegateOp */
    1662             :     UnboxedArrayObject::objectMoved
    1663             : };
    1664             : 
    1665             : static const ObjectOps UnboxedArrayObjectObjectOps = {
    1666             :     UnboxedArrayObject::obj_lookupProperty,
    1667             :     UnboxedArrayObject::obj_defineProperty,
    1668             :     UnboxedArrayObject::obj_hasProperty,
    1669             :     UnboxedArrayObject::obj_getProperty,
    1670             :     UnboxedArrayObject::obj_setProperty,
    1671             :     UnboxedArrayObject::obj_getOwnPropertyDescriptor,
    1672             :     UnboxedArrayObject::obj_deleteProperty,
    1673             :     UnboxedArrayObject::obj_watch,
    1674             :     nullptr,   /* No unwatch needed, as watch() converts the object to native */
    1675             :     nullptr,   /* getElements */
    1676             :     nullptr    /* funToString */
    1677             : };
    1678             : 
    1679             : const Class UnboxedArrayObject::class_ = {
    1680             :     "Array",
    1681             :     Class::NON_NATIVE |
    1682             :     JSCLASS_SKIP_NURSERY_FINALIZE |
    1683             :     JSCLASS_BACKGROUND_FINALIZE,
    1684             :     &UnboxedArrayObjectClassOps,
    1685             :     JS_NULL_CLASS_SPEC,
    1686             :     &UnboxedArrayObjectClassExtension,
    1687             :     &UnboxedArrayObjectObjectOps
    1688             : };
    1689             : 
    1690             : /////////////////////////////////////////////////////////////////////
    1691             : // API
    1692             : /////////////////////////////////////////////////////////////////////
    1693             : 
    1694             : static bool
    1695          62 : UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype)
    1696             : {
    1697          62 :     if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32)
    1698           0 :         return true;
    1699          62 :     if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL)
    1700           8 :         return true;
    1701          54 :     return false;
    1702             : }
    1703             : 
    1704             : static bool
    1705        1679 : CombineUnboxedTypes(const Value& value, JSValueType* existing)
    1706             : {
    1707        1679 :     JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType();
    1708             : 
    1709        1679 :     if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeIncludes(type, *existing)) {
    1710        1648 :         *existing = type;
    1711        1648 :         return true;
    1712             :     }
    1713          31 :     if (UnboxedTypeIncludes(*existing, type))
    1714           8 :         return true;
    1715          23 :     return false;
    1716             : }
    1717             : 
    1718             : // Return whether the property names and types in layout are a subset of the
    1719             : // specified vector.
    1720             : static bool
    1721          11 : PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLayout* layout)
    1722             : {
    1723          16 :     for (size_t i = 0; i < layout->properties().length(); i++) {
    1724          14 :         const UnboxedLayout::Property& layoutProperty = layout->properties()[i];
    1725          14 :         bool found = false;
    1726          38 :         for (size_t j = 0; j < properties.length(); j++) {
    1727          31 :             if (layoutProperty.name == properties[j].name) {
    1728           7 :                 found = (layoutProperty.type == properties[j].type);
    1729           7 :                 break;
    1730             :             }
    1731             :         }
    1732          14 :         if (!found)
    1733           9 :             return false;
    1734             :     }
    1735           2 :     return true;
    1736             : }
    1737             : 
    1738             : static bool
    1739         752 : CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape,
    1740             :                              UnboxedLayout::PropertyVector& properties)
    1741             : {
    1742             :     // All preliminary objects must have been created with enough space to
    1743             :     // fill in their unboxed data inline. This is ensured either by using
    1744             :     // the largest allocation kind (which limits the maximum size of an
    1745             :     // unboxed object), or by using an allocation kind that covers all
    1746             :     // properties in the template, as the space used by unboxed properties
    1747             :     // is less than or equal to that used by boxed properties.
    1748         752 :     MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >=
    1749             :                Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan()));
    1750             : 
    1751         752 :     if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) {
    1752             :         // Only use an unboxed representation if all created objects match
    1753             :         // the template shape exactly.
    1754           1 :         return false;
    1755             :     }
    1756             : 
    1757        2407 :     for (size_t i = 0; i < templateShape->slotSpan(); i++) {
    1758        1679 :         Value val = obj->getSlot(i);
    1759             : 
    1760        1679 :         JSValueType& existing = properties[i].type;
    1761        1679 :         if (!CombineUnboxedTypes(val, &existing))
    1762          23 :             return false;
    1763             :     }
    1764             : 
    1765         728 :     return true;
    1766             : }
    1767             : 
    1768             : static bool
    1769           0 : CombineArrayObjectElements(JSContext* cx, ArrayObject* obj, JSValueType* elementType)
    1770             : {
    1771           0 :     if (obj->inDictionaryMode() ||
    1772           0 :         obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
    1773           0 :         !obj->lastProperty()->previous()->isEmptyShape())
    1774             :     {
    1775             :         // Only use an unboxed representation if the object has no properties.
    1776           0 :         return false;
    1777             :     }
    1778             : 
    1779           0 :     for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
    1780           0 :         Value val = obj->getDenseElement(i);
    1781             : 
    1782             :         // For now, unboxed arrays cannot have holes.
    1783           0 :         if (val.isMagic(JS_ELEMENTS_HOLE))
    1784           0 :             return false;
    1785             : 
    1786           0 :         if (!CombineUnboxedTypes(val, elementType))
    1787           0 :             return false;
    1788             :     }
    1789             : 
    1790           0 :     return true;
    1791             : }
    1792             : 
    1793             : static size_t
    1794          19 : ComputePlainObjectLayout(JSContext* cx, Shape* templateShape,
    1795             :                          UnboxedLayout::PropertyVector& properties)
    1796             : {
    1797             :     // Fill in the names for all the object's properties.
    1798          65 :     for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
    1799          46 :         size_t slot = r.front().slot();
    1800          46 :         MOZ_ASSERT(!properties[slot].name);
    1801          46 :         properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName();
    1802             :     }
    1803             : 
    1804             :     // Fill in all the unboxed object's property offsets.
    1805          19 :     uint32_t offset = 0;
    1806             : 
    1807             :     // Search for an existing unboxed layout which is a subset of this one.
    1808             :     // If there are multiple such layouts, use the largest one. If we're able
    1809             :     // to find such a layout, use the same property offsets for the shared
    1810             :     // properties, which will allow us to generate better code if the objects
    1811             :     // have a subtype/supertype relation and are accessed at common sites.
    1812          19 :     UnboxedLayout* bestExisting = nullptr;
    1813          30 :     for (UnboxedLayout* existing : cx->compartment()->unboxedLayouts) {
    1814          11 :         if (PropertiesAreSuperset(properties, existing)) {
    1815           2 :             if (!bestExisting ||
    1816           0 :                 existing->properties().length() > bestExisting->properties().length())
    1817             :             {
    1818           2 :                 bestExisting = existing;
    1819             :             }
    1820             :         }
    1821             :     }
    1822          19 :     if (bestExisting) {
    1823           7 :         for (size_t i = 0; i < bestExisting->properties().length(); i++) {
    1824           5 :             const UnboxedLayout::Property& existingProperty = bestExisting->properties()[i];
    1825          18 :             for (size_t j = 0; j < templateShape->slotSpan(); j++) {
    1826          13 :                 if (existingProperty.name == properties[j].name) {
    1827           5 :                     MOZ_ASSERT(existingProperty.type == properties[j].type);
    1828           5 :                     properties[j].offset = existingProperty.offset;
    1829             :                 }
    1830             :             }
    1831             :         }
    1832           2 :         offset = bestExisting->size();
    1833             :     }
    1834             : 
    1835             :     // Order remaining properties from the largest down for the best space
    1836             :     // utilization.
    1837             :     static const size_t typeSizes[] = { 8, 4, 1 };
    1838             : 
    1839          76 :     for (size_t i = 0; i < ArrayLength(typeSizes); i++) {
    1840          57 :         size_t size = typeSizes[i];
    1841         195 :         for (size_t j = 0; j < templateShape->slotSpan(); j++) {
    1842         138 :             if (properties[j].offset != UINT32_MAX)
    1843          61 :                 continue;
    1844          77 :             JSValueType type = properties[j].type;
    1845          77 :             if (UnboxedTypeSize(type) == size) {
    1846          41 :                 offset = JS_ROUNDUP(offset, size);
    1847          41 :                 properties[j].offset = offset;
    1848          41 :                 offset += size;
    1849             :             }
    1850             :         }
    1851             :     }
    1852             : 
    1853             :     // The final offset is the amount of data needed by the object.
    1854          19 :     return offset;
    1855             : }
    1856             : 
    1857             : static bool
    1858          19 : SetLayoutTraceList(JSContext* cx, UnboxedLayout* layout)
    1859             : {
    1860             :     // Figure out the offsets of any objects or string properties.
    1861          38 :     Vector<int32_t, 8, SystemAllocPolicy> objectOffsets, stringOffsets;
    1862          65 :     for (size_t i = 0; i < layout->properties().length(); i++) {
    1863          46 :         const UnboxedLayout::Property& property = layout->properties()[i];
    1864          46 :         MOZ_ASSERT(property.offset != UINT32_MAX);
    1865          46 :         if (property.type == JSVAL_TYPE_OBJECT) {
    1866          19 :             if (!objectOffsets.append(property.offset))
    1867           0 :                 return false;
    1868          27 :         } else if (property.type == JSVAL_TYPE_STRING) {
    1869           6 :             if (!stringOffsets.append(property.offset))
    1870           0 :                 return false;
    1871             :         }
    1872             :     }
    1873             : 
    1874             :     // Construct the layout's trace list.
    1875          19 :     if (!objectOffsets.empty() || !stringOffsets.empty()) {
    1876          38 :         Vector<int32_t, 8, SystemAllocPolicy> entries;
    1877          95 :         if (!entries.appendAll(stringOffsets) ||
    1878          95 :             !entries.append(-1) ||
    1879          57 :             !entries.appendAll(objectOffsets) ||
    1880         133 :             !entries.append(-1) ||
    1881          57 :             !entries.append(-1))
    1882             :         {
    1883           0 :             return false;
    1884             :         }
    1885          19 :         int32_t* traceList = cx->zone()->pod_malloc<int32_t>(entries.length());
    1886          19 :         if (!traceList)
    1887           0 :             return false;
    1888          19 :         PodCopy(traceList, entries.begin(), entries.length());
    1889          19 :         layout->setTraceList(traceList);
    1890             :     }
    1891             : 
    1892          19 :     return true;
    1893             : }
    1894             : 
    1895             : static inline Value
    1896         920 : NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
    1897             : {
    1898         920 :     return values[(*valueCursor)++];
    1899             : }
    1900             : 
    1901             : static bool
    1902           0 : GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle<GCVector<Value>> values)
    1903             : {
    1904           0 :     if (!values.append(Int32Value(obj->length())))
    1905           0 :         return false;
    1906           0 :     if (!values.append(Int32Value(obj->getDenseInitializedLength())))
    1907           0 :         return false;
    1908           0 :     for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
    1909           0 :         if (!values.append(obj->getDenseElement(i)))
    1910           0 :             return false;
    1911             :     }
    1912           0 :     return true;
    1913             : }
    1914             : 
    1915             : void
    1916           0 : UnboxedArrayObject::fillAfterConvert(JSContext* cx,
    1917             :                                      Handle<GCVector<Value>> values, size_t* valueCursor)
    1918             : {
    1919           0 :     MOZ_ASSERT(CapacityArray[1] == 0);
    1920           0 :     setCapacityIndex(1);
    1921           0 :     setInitializedLengthNoBarrier(0);
    1922           0 :     setInlineElements();
    1923             : 
    1924           0 :     setLength(cx, NextValue(values, valueCursor).toInt32());
    1925             : 
    1926           0 :     int32_t initlen = NextValue(values, valueCursor).toInt32();
    1927           0 :     if (!initlen)
    1928           0 :         return;
    1929             : 
    1930           0 :     AutoEnterOOMUnsafeRegion oomUnsafe;
    1931           0 :     if (!growElements(cx, initlen))
    1932           0 :         oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert");
    1933             : 
    1934           0 :     setInitializedLength(initlen);
    1935             : 
    1936           0 :     for (size_t i = 0; i < size_t(initlen); i++)
    1937           0 :         JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor)));
    1938             : }
    1939             : 
    1940             : static bool
    1941         380 : GetValuesFromPreliminaryPlainObject(PlainObject* obj, MutableHandle<GCVector<Value>> values)
    1942             : {
    1943        1300 :     for (size_t i = 0; i < obj->slotSpan(); i++) {
    1944         920 :         if (!values.append(obj->getSlot(i)))
    1945           0 :             return false;
    1946             :     }
    1947         380 :     return true;
    1948             : }
    1949             : 
    1950             : void
    1951         380 : UnboxedPlainObject::fillAfterConvert(JSContext* cx,
    1952             :                                      Handle<GCVector<Value>> values, size_t* valueCursor)
    1953             : {
    1954         380 :     initExpando();
    1955         380 :     memset(data(), 0, layout().size());
    1956        1300 :     for (size_t i = 0; i < layout().properties().length(); i++)
    1957         920 :         JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
    1958         380 : }
    1959             : 
    1960             : bool
    1961          59 : js::TryConvertToUnboxedLayout(JSContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
    1962             :                               ObjectGroup* group, PreliminaryObjectArray* objects)
    1963             : {
    1964          59 :     bool isArray = !templateShape;
    1965             : 
    1966             :     // Unboxed arrays are nightly only for now. The getenv() call will be
    1967             :     // removed when they are on by default. See bug 1153266.
    1968          59 :     if (isArray) {
    1969             : #ifdef NIGHTLY_BUILD
    1970           1 :         if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) {
    1971           1 :             if (!cx->options().unboxedArrays())
    1972           1 :                 return true;
    1973             :         }
    1974             : #else
    1975             :         return true;
    1976             : #endif
    1977             :     } else {
    1978          58 :         if (jit::JitOptions.disableUnboxedObjects)
    1979           0 :             return true;
    1980             :     }
    1981             : 
    1982          58 :     MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags());
    1983             : 
    1984          58 :     if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
    1985           3 :         return true;
    1986             : 
    1987          55 :     if (!isArray && templateShape->slotSpan() == 0)
    1988           1 :         return true;
    1989             : 
    1990         108 :     UnboxedLayout::PropertyVector properties;
    1991          54 :     if (!isArray) {
    1992          54 :         if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan()))
    1993           0 :             return false;
    1994             :     }
    1995          54 :     JSValueType elementType = JSVAL_TYPE_MAGIC;
    1996             : 
    1997          54 :     size_t objectCount = 0;
    1998         801 :     for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
    1999         771 :         JSObject* obj = objects->get(i);
    2000         771 :         if (!obj)
    2001          19 :             continue;
    2002             : 
    2003         752 :         if (obj->isSingleton() || obj->group() != group)
    2004           0 :             return true;
    2005             : 
    2006         752 :         objectCount++;
    2007             : 
    2008         752 :         if (isArray) {
    2009           0 :             if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elementType))
    2010           0 :                 return true;
    2011             :         } else {
    2012         752 :             if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape, properties))
    2013          24 :                 return true;
    2014             :         }
    2015             :     }
    2016             : 
    2017          30 :     size_t layoutSize = 0;
    2018          30 :     if (isArray) {
    2019             :         // Don't use an unboxed representation if we couldn't determine an
    2020             :         // element type for the objects.
    2021           0 :         if (UnboxedTypeSize(elementType) == 0)
    2022           0 :             return true;
    2023             :     } else {
    2024          30 :         if (objectCount <= 1) {
    2025             :             // If only one of the objects has been created, it is more likely
    2026             :             // to have new properties added later. This heuristic is not used
    2027             :             // for array objects, where we might want an unboxed representation
    2028             :             // even if there is only one large array.
    2029           1 :             return true;
    2030             :         }
    2031             : 
    2032          75 :         for (size_t i = 0; i < templateShape->slotSpan(); i++) {
    2033             :             // We can't use an unboxed representation if e.g. all the objects have
    2034             :             // a null value for one of the properties, as we can't decide what type
    2035             :             // it is supposed to have.
    2036          56 :             if (UnboxedTypeSize(properties[i].type) == 0)
    2037          10 :                 return true;
    2038             :         }
    2039             : 
    2040             :         // Make sure that all properties on the template shape are property
    2041             :         // names, and not indexes.
    2042          65 :         for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
    2043          46 :             jsid id = r.front().propid();
    2044             :             uint32_t dummy;
    2045          46 :             if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
    2046           0 :                 return true;
    2047             :         }
    2048             : 
    2049          19 :         layoutSize = ComputePlainObjectLayout(cx, templateShape, properties);
    2050             : 
    2051             :         // The entire object must be allocatable inline.
    2052          19 :         if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE_SIZE)
    2053           0 :             return true;
    2054             :     }
    2055             : 
    2056          19 :     UniquePtr<UnboxedLayout>& layout = enter.unboxedLayoutToCleanUp;
    2057          19 :     MOZ_ASSERT(!layout);
    2058          19 :     layout = group->zone()->make_unique<UnboxedLayout>(group->zone());
    2059          19 :     if (!layout)
    2060           0 :         return false;
    2061             : 
    2062          19 :     if (isArray) {
    2063           0 :         layout->initArray(elementType);
    2064             :     } else {
    2065          19 :         if (!layout->initProperties(properties, layoutSize))
    2066           0 :             return false;
    2067             : 
    2068             :         // The unboxedLayouts list only tracks layouts for plain objects.
    2069          19 :         cx->compartment()->unboxedLayouts.insertFront(layout.get());
    2070             : 
    2071          19 :         if (!SetLayoutTraceList(cx, layout.get()))
    2072           0 :             return false;
    2073             :     }
    2074             : 
    2075             :     // We've determined that all the preliminary objects can use the new layout
    2076             :     // just constructed, so convert the existing group to use the unboxed class,
    2077             :     // and update the preliminary objects to use the new layout. Do the
    2078             :     // fallible stuff first before modifying any objects.
    2079             : 
    2080             :     // Get an empty shape which we can use for the preliminary objects.
    2081          19 :     const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_;
    2082          19 :     Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0);
    2083          19 :     if (!newShape) {
    2084           0 :         cx->recoverFromOutOfMemory();
    2085           0 :         return false;
    2086             :     }
    2087             : 
    2088             :     // Accumulate a list of all the values in each preliminary object, and
    2089             :     // update their shapes.
    2090          38 :     Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
    2091         399 :     for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
    2092         380 :         JSObject* obj = objects->get(i);
    2093         380 :         if (!obj)
    2094           0 :             continue;
    2095             : 
    2096             :         bool ok;
    2097         380 :         if (isArray)
    2098           0 :             ok = GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), &values);
    2099             :         else
    2100         380 :             ok = GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), &values);
    2101             : 
    2102         380 :         if (!ok) {
    2103           0 :             cx->recoverFromOutOfMemory();
    2104           0 :             return false;
    2105             :         }
    2106             :     }
    2107             : 
    2108          19 :     if (TypeNewScript* newScript = group->newScript())
    2109           0 :         layout->setNewScript(newScript);
    2110             : 
    2111         399 :     for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
    2112         380 :         if (JSObject* obj = objects->get(i))
    2113         380 :             obj->as<NativeObject>().setLastPropertyMakeNonNative(newShape);
    2114             :     }
    2115             : 
    2116          19 :     group->setClasp(clasp);
    2117          19 :     group->setUnboxedLayout(layout.release());
    2118             : 
    2119          19 :     size_t valueCursor = 0;
    2120         399 :     for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
    2121         380 :         JSObject* obj = objects->get(i);
    2122         380 :         if (!obj)
    2123           0 :             continue;
    2124             : 
    2125         380 :         if (isArray)
    2126           0 :             obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCursor);
    2127             :         else
    2128         380 :             obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
    2129             :     }
    2130             : 
    2131          19 :     MOZ_ASSERT(valueCursor == values.length());
    2132          19 :     return true;
    2133             : }
    2134             : 
    2135        5888 : DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
    2136             :                              JSContext*, JSObject*, uint32_t, const Value*, uint32_t,
    2137             :                              ShouldUpdateTypes);
    2138             : 
    2139             : DenseElementResult
    2140        2944 : js::SetOrExtendAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
    2141             :                                               uint32_t start, const Value* vp, uint32_t count,
    2142             :                                               ShouldUpdateTypes updateTypes)
    2143             : {
    2144        2944 :     SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, count, updateTypes);
    2145        2944 :     return CallBoxedOrUnboxedSpecialization(functor, obj);
    2146             : };
    2147             : 
    2148          40 : DefineBoxedOrUnboxedFunctor5(MoveBoxedOrUnboxedDenseElements,
    2149             :                              JSContext*, JSObject*, uint32_t, uint32_t, uint32_t);
    2150             : 
    2151             : DenseElementResult
    2152          20 : js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
    2153             :                                        uint32_t dstStart, uint32_t srcStart, uint32_t length)
    2154             : {
    2155          20 :     MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length);
    2156          20 :     return CallBoxedOrUnboxedSpecialization(functor, obj);
    2157             : }
    2158             : 
    2159          12 : DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
    2160             :                                  JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
    2161             : 
    2162             : DenseElementResult
    2163           6 : js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
    2164             :                                        uint32_t dstStart, uint32_t srcStart, uint32_t length)
    2165             : {
    2166           6 :     CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
    2167           6 :     return CallBoxedOrUnboxedSpecialization(functor, dst, src);
    2168             : }
    2169             : 
    2170          40 : DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
    2171             :                              JSContext*, JSObject*, size_t);
    2172             : 
    2173             : void
    2174          20 : js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
    2175             : {
    2176          20 :     SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
    2177          20 :     JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
    2178          20 : }
    2179             : 
    2180           0 : DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements,
    2181             :                              JSContext*, JSObject*, size_t);
    2182             : 
    2183             : DenseElementResult
    2184           0 : js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen)
    2185             : {
    2186           0 :     EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen);
    2187           0 :     return CallBoxedOrUnboxedSpecialization(functor, obj);
    2188             : }

Generated by: LCOV version 1.13