summaryrefslogtreecommitdiffstats
path: root/js/src/vm/UnboxedObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/UnboxedObject.cpp')
-rw-r--r--js/src/vm/UnboxedObject.cpp1752
1 files changed, 0 insertions, 1752 deletions
diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
deleted file mode 100644
index d7ad91de4..000000000
--- a/js/src/vm/UnboxedObject.cpp
+++ /dev/null
@@ -1,1752 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "vm/UnboxedObject-inl.h"
-
-#include "jit/BaselineIC.h"
-#include "jit/ExecutableAllocator.h"
-#include "jit/JitCommon.h"
-#include "jit/Linker.h"
-
-#include "jsobjinlines.h"
-
-#include "gc/Nursery-inl.h"
-#include "jit/MacroAssembler-inl.h"
-#include "vm/Shape-inl.h"
-
-using mozilla::ArrayLength;
-using mozilla::DebugOnly;
-using mozilla::PodCopy;
-
-using namespace js;
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedLayout
-/////////////////////////////////////////////////////////////////////
-
-void
-UnboxedLayout::trace(JSTracer* trc)
-{
- for (size_t i = 0; i < properties_.length(); i++)
- TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name");
-
- if (newScript())
- newScript()->trace(trc);
-
- TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
- TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape");
- TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript");
- TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup");
- TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode");
-}
-
-size_t
-UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
-{
- return mallocSizeOf(this)
- + properties_.sizeOfExcludingThis(mallocSizeOf)
- + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
- + mallocSizeOf(traceList());
-}
-
-void
-UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */)
-{
- if (newScript_ && writeBarrier)
- TypeNewScript::writeBarrierPre(newScript_);
- newScript_ = newScript;
-}
-
-// Constructor code returns a 0x1 value to indicate the constructor code should
-// be cleared.
-static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
-
-/* static */ bool
-UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
-{
- gc::AutoSuppressGC suppress(cx);
-
- using namespace jit;
-
- if (!cx->compartment()->ensureJitCompartmentExists(cx))
- return false;
-
- UnboxedLayout& layout = group->unboxedLayout();
- MOZ_ASSERT(!layout.constructorCode());
-
- UnboxedPlainObject* templateObject = UnboxedPlainObject::create(cx, group, TenuredObject);
- if (!templateObject)
- return false;
-
- JitContext jitContext(cx, nullptr);
-
- MacroAssembler masm;
-
- Register propertiesReg, newKindReg;
-#ifdef JS_CODEGEN_X86
- propertiesReg = eax;
- newKindReg = ecx;
- masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg);
- masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg);
-#else
- propertiesReg = IntArgReg0;
- newKindReg = IntArgReg1;
-#endif
-
-#ifdef JS_CODEGEN_ARM64
- // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing.
- masm.initStackPtr();
-#endif
-
- MOZ_ASSERT(propertiesReg.volatile_());
- MOZ_ASSERT(newKindReg.volatile_());
-
- AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
- regs.take(propertiesReg);
- regs.take(newKindReg);
- Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
-
- LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
- masm.PushRegsInMask(savedNonVolatileRegisters);
-
- // The scratch double register might be used by MacroAssembler methods.
- if (ScratchDoubleReg.volatile_())
- masm.push(ScratchDoubleReg);
-
- Label failure, tenuredObject, allocated;
- masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject);
- masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()),
- Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject);
-
- // Allocate an object in the nursery
- masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure,
- /* initFixedSlots = */ false);
-
- masm.jump(&allocated);
- masm.bind(&tenuredObject);
-
- // Allocate an object in the tenured heap.
- masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure,
- /* initFixedSlots = */ false);
-
- // If any of the properties being stored are in the nursery, add a store
- // buffer entry for the new object.
- Label postBarrier;
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- if (property.type == JSVAL_TYPE_OBJECT) {
- Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
- Label notObject;
- masm.branchTestObject(Assembler::NotEqual, valueAddress, &notObject);
- Register valueObject = masm.extractObject(valueAddress, scratch1);
- masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier);
- masm.bind(&notObject);
- }
- }
-
- masm.jump(&allocated);
- masm.bind(&postBarrier);
-
- LiveGeneralRegisterSet liveVolatileRegisters;
- liveVolatileRegisters.add(propertiesReg);
- if (object.volatile_())
- liveVolatileRegisters.add(object);
- masm.PushRegsInMask(liveVolatileRegisters);
-
- masm.mov(ImmPtr(cx->runtime()), scratch1);
- masm.setupUnalignedABICall(scratch2);
- masm.passABIArg(scratch1);
- masm.passABIArg(object);
- masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
-
- masm.PopRegsInMask(liveVolatileRegisters);
-
- masm.bind(&allocated);
-
- ValueOperand valueOperand;
-#ifdef JS_NUNBOX32
- valueOperand = ValueOperand(scratch1, scratch2);
-#else
- valueOperand = ValueOperand(scratch1);
-#endif
-
- Label failureStoreOther, failureStoreObject;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
- Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset);
-
- masm.loadValue(valueAddress, valueOperand);
-
- if (property.type == JSVAL_TYPE_OBJECT) {
- HeapTypeSet* types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
-
- Label notObject;
- masm.branchTestObject(Assembler::NotEqual, valueOperand,
- types->mightBeMIRType(MIRType::Null) ? &notObject : &failureStoreObject);
-
- Register payloadReg = masm.extractObject(valueOperand, scratch1);
-
- if (!types->hasType(TypeSet::AnyObjectType())) {
- Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
- masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject);
- }
-
- masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
- TypedOrValueRegister(MIRType::Object,
- AnyRegister(payloadReg)), nullptr);
-
- if (notObject.used()) {
- Label done;
- masm.jump(&done);
- masm.bind(&notObject);
- masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
- masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr);
- masm.bind(&done);
- }
- } else {
- masm.storeUnboxedProperty(targetAddress, property.type,
- ConstantOrRegister(valueOperand), &failureStoreOther);
- }
- }
-
- Label done;
- masm.bind(&done);
-
- if (object != ReturnReg)
- masm.movePtr(object, ReturnReg);
-
- // Restore non-volatile registers which were saved on entry.
- if (ScratchDoubleReg.volatile_())
- masm.pop(ScratchDoubleReg);
- masm.PopRegsInMask(savedNonVolatileRegisters);
-
- masm.abiret();
-
- masm.bind(&failureStoreOther);
-
- // There was a failure while storing a value which cannot be stored at all
- // in the unboxed object. Initialize the object so it is safe for GC and
- // return null.
- masm.initUnboxedObjectContents(object, templateObject);
-
- masm.bind(&failure);
-
- masm.movePtr(ImmWord(0), object);
- masm.jump(&done);
-
- masm.bind(&failureStoreObject);
-
- // There was a failure while storing a value to an object slot of the
- // unboxed object. If the value is storable, the failure occurred due to
- // incomplete type information in the object, so return a token to trigger
- // regeneration of the jitcode after a new object is created in the VM.
- {
- Label isObject;
- masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
- masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
- masm.bind(&isObject);
- }
-
- // Initialize the object so it is safe for GC.
- masm.initUnboxedObjectContents(object, templateObject);
-
- masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
- masm.jump(&done);
-
- Linker linker(masm);
- AutoFlushICache afc("UnboxedObject");
- JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
- if (!code)
- return false;
-
- layout.setConstructorCode(code);
- return true;
-}
-
-void
-UnboxedLayout::detachFromCompartment()
-{
- if (isInList())
- remove();
-}
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedPlainObject
-/////////////////////////////////////////////////////////////////////
-
-bool
-UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property,
- const Value& v)
-{
- uint8_t* p = &data_[property.offset];
- return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v,
- /* preBarrier = */ true);
-}
-
-Value
-UnboxedPlainObject::getValue(const UnboxedLayout::Property& property,
- bool maybeUninitialized /* = false */)
-{
- uint8_t* p = &data_[property.offset];
- return GetUnboxedValue(p, property.type, maybeUninitialized);
-}
-
-void
-UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj)
-{
- if (obj->as<UnboxedPlainObject>().expando_) {
- TraceManuallyBarrieredEdge(trc,
- reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
- "unboxed_expando");
- }
-
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
- const int32_t* list = layout.traceList();
- if (!list)
- return;
-
- uint8_t* data = obj->as<UnboxedPlainObject>().data();
- while (*list != -1) {
- GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
- TraceEdge(trc, heap, "unboxed_string");
- list++;
- }
- list++;
- while (*list != -1) {
- GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
- TraceNullableEdge(trc, heap, "unboxed_object");
- list++;
- }
-
- // Unboxed objects don't have Values to trace.
- MOZ_ASSERT(*(list + 1) == -1);
-}
-
-/* static */ UnboxedExpandoObject*
-UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj)
-{
- if (obj->expando_)
- return obj->expando_;
-
- UnboxedExpandoObject* expando =
- NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4);
- if (!expando)
- return nullptr;
-
- // Don't track property types for expando objects. This allows Baseline
- // and Ion AddSlot ICs to guard on the unboxed group without guarding on
- // the expando group.
- MarkObjectGroupUnknownProperties(cx, expando->group());
-
- // If the expando is tenured then the original object must also be tenured.
- // Otherwise barriers triggered on the original object for writes to the
- // expando (as can happen in the JIT) won't see the tenured->nursery edge.
- // See WholeCellEdges::mark.
- MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj));
-
- // As with setValue(), we need to manually trigger post barriers on the
- // whole object. If we treat the field as a GCPtrObject and later
- // convert the object to its native representation, we will end up with a
- // corrupted store buffer entry.
- if (IsInsideNursery(expando) && !IsInsideNursery(obj))
- cx->runtime()->gc.storeBuffer.putWholeCell(obj);
-
- obj->expando_ = expando;
- return expando;
-}
-
-bool
-UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const
-{
- if (layout().lookup(id))
- return true;
-
- if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
- return true;
-
- return false;
-}
-
-static bool
-PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup)
-{
- HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id);
- TypeSet::TypeList types;
- if (!typeProperty->enumerateTypes(&types)) {
- ReportOutOfMemory(cx);
- return false;
- }
- for (size_t j = 0; j < types.length(); j++)
- AddTypePropertyId(cx, newGroup, nullptr, id, types[j]);
- return true;
-}
-
-static PlainObject*
-MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
-{
- PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
- TenuredObject);
- if (!obj)
- return nullptr;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
- return nullptr;
- MOZ_ASSERT(obj->slotSpan() == i + 1);
- MOZ_ASSERT(!obj->inDictionaryMode());
- }
-
- return obj;
-}
-
-/* static */ bool
-UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
-{
- AutoEnterAnalysis enter(cx);
-
- UnboxedLayout& layout = group->unboxedLayout();
- Rooted<TaggedProto> proto(cx, group->proto());
-
- MOZ_ASSERT(!layout.nativeGroup());
-
- RootedObjectGroup replacementGroup(cx);
-
- const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
-
- // Immediately clear any new script on the group. This is done by replacing
- // the existing new script with one for a replacement default new group.
- // This is done so that the size of the replacment group's objects is the
- // same as that for the unboxed group, so that we do not see polymorphic
- // slot accesses later on for sites that see converted objects from this
- // group and objects that were allocated using the replacement new group.
- if (layout.newScript()) {
- MOZ_ASSERT(!layout.isArray());
-
- replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
- if (!replacementGroup)
- return false;
-
- PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout);
- if (!templateObject)
- return false;
-
- TypeNewScript* replacementNewScript =
- TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
- if (!replacementNewScript)
- return false;
-
- replacementGroup->setNewScript(replacementNewScript);
- gc::TraceTypeNewScript(replacementGroup);
-
- group->clearNewScript(cx, replacementGroup);
- }
-
- // Similarly, if this group is keyed to an allocation site, replace its
- // entry with a new group that has no unboxed layout.
- if (layout.allocationScript()) {
- RootedScript script(cx, layout.allocationScript());
- jsbytecode* pc = layout.allocationPc();
-
- replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto);
- if (!replacementGroup)
- return false;
-
- PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
- replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
-
- JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
- cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
- replacementGroup);
-
- // Clear any baseline information at this opcode which might use the old group.
- if (script->hasBaselineScript()) {
- jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
- jit::ICFallbackStub* fallback = entry.fallbackStub();
- for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
- iter.unlink(cx);
- if (fallback->isNewObject_Fallback())
- fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
- else if (fallback->isNewArray_Fallback())
- fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
- }
- }
-
- size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
-
- if (layout.isArray()) {
- // The length shape to use for arrays is cached via a modified initial
- // shape for array objects. Create an array now to make sure this entry
- // is instantiated.
- if (!NewDenseEmptyArray(cx))
- return false;
- }
-
- RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0));
- if (!shape)
- return false;
-
- MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0);
-
- // Add shapes for each property, if this is for a plain object.
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
-
- Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name),
- i, JSPROP_ENUMERATE, 0));
- shape = cx->zone()->propertyTree.getChild(cx, shape, child);
- if (!shape)
- return false;
- }
-
- ObjectGroup* nativeGroup =
- ObjectGroupCompartment::makeGroup(cx, clasp, proto,
- group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
- if (!nativeGroup)
- return false;
-
- // No sense propagating if we don't know what we started with.
- if (!group->unknownProperties()) {
- // Propagate all property types from the old group to the new group.
- if (layout.isArray()) {
- if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
- return false;
- } else {
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- jsid id = NameToId(property.name);
- if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
- return false;
-
- // If we are OOM we may not be able to propagate properties.
- if (nativeGroup->unknownProperties())
- break;
-
- HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
- if (nativeProperty && nativeProperty->canSetDefinite(i))
- nativeProperty->setDefinite(i);
- }
- }
- } else {
- // If we skip, though, the new group had better agree.
- MOZ_ASSERT(nativeGroup->unknownProperties());
- }
-
- layout.nativeGroup_ = nativeGroup;
- layout.nativeShape_ = shape;
- layout.replacementGroup_ = replacementGroup;
-
- nativeGroup->setOriginalUnboxedGroup(group);
-
- group->markStateChange(cx);
-
- return true;
-}
-
-/* static */ bool
-UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
- UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
-
- if (!layout.nativeGroup()) {
- if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
- return false;
-
- // makeNativeGroup can reentrantly invoke this method.
- if (obj->is<PlainObject>())
- return true;
- }
-
- AutoValueVector values(cx);
- for (size_t i = 0; i < layout.properties().length(); i++) {
- // We might be reading properties off the object which have not been
- // initialized yet. Make sure any double values we read here are
- // canonicalized.
- if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true)))
- return false;
- }
-
- // We are eliminating the expando edge with the conversion, so trigger a
- // pre barrier.
- JSObject::writeBarrierPre(expando);
-
- // Additionally trigger a post barrier on the expando itself. Whole cell
- // store buffer entries can be added on the original unboxed object for
- // writes to the expando (see WholeCellEdges::trace), so after conversion
- // we need to make sure the expando itself will still be traced.
- if (expando && !IsInsideNursery(expando))
- cx->runtime()->gc.storeBuffer.putWholeCell(expando);
-
- obj->setGroup(layout.nativeGroup());
- obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
-
- for (size_t i = 0; i < values.length(); i++)
- obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
-
- if (expando) {
- // Add properties from the expando object to the object, in order.
- // Suppress GC here, so that callers don't need to worry about this
- // method collecting. The stuff below can only fail due to OOM, in
- // which case the object will not have been completely filled back in.
- gc::AutoSuppressGC suppress(cx);
-
- Vector<jsid> ids(cx);
- for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
- if (!ids.append(r.front().propid()))
- return false;
- }
- for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
- if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
- if (!ids.append(INT_TO_JSID(i)))
- return false;
- }
- }
- ::Reverse(ids.begin(), ids.end());
-
- RootedPlainObject nobj(cx, &obj->as<PlainObject>());
- Rooted<UnboxedExpandoObject*> nexpando(cx, expando);
- RootedId id(cx);
- Rooted<PropertyDescriptor> desc(cx);
- for (size_t i = 0; i < ids.length(); i++) {
- id = ids[i];
- if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
- return false;
- ObjectOpResult result;
- if (!DefineProperty(cx, nobj, id, desc, result))
- return false;
- MOZ_ASSERT(result.ok());
- }
- }
-
- return true;
-}
-
-/* static */
-UnboxedPlainObject*
-UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind)
-{
- AutoSetNewObjectMetadata metadata(cx);
-
- MOZ_ASSERT(group->clasp() == &class_);
- gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
-
- UnboxedPlainObject* res =
- NewObjectWithGroup<UnboxedPlainObject>(cx, group, allocKind, newKind);
- if (!res)
- return nullptr;
-
- // Overwrite the dummy shape which was written to the object's expando field.
- res->initExpando();
-
- // Initialize reference fields of the object. All fields in the object will
- // be overwritten shortly, but references need to be safe for the GC.
- const int32_t* list = res->layout().traceList();
- if (list) {
- uint8_t* data = res->data();
- while (*list != -1) {
- GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
- heap->init(cx->names().empty);
- list++;
- }
- list++;
- while (*list != -1) {
- GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
- heap->init(nullptr);
- list++;
- }
- // Unboxed objects don't have Values to initialize.
- MOZ_ASSERT(*(list + 1) == -1);
- }
-
- return res;
-}
-
-/* static */ JSObject*
-UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
- NewObjectKind newKind, IdValuePair* properties)
-{
- MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
-
- UnboxedLayout& layout = group->unboxedLayout();
-
- if (layout.constructorCode()) {
- MOZ_ASSERT(cx->isJSContext());
-
- typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind);
- ConstructorCodeSignature function =
- reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
-
- JSObject* obj;
- {
- JS::AutoSuppressGCAnalysis nogc;
- obj = reinterpret_cast<JSObject*>(CALL_GENERATED_2(function, properties, newKind));
- }
- if (obj > reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
- return obj;
-
- if (obj == reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
- layout.setConstructorCode(nullptr);
- }
-
- UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind);
- if (!obj)
- return nullptr;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
- return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
- }
-
-#ifndef JS_CODEGEN_NONE
- if (cx->isJSContext() &&
- !group->unknownProperties() &&
- !layout.constructorCode() &&
- cx->asJSContext()->runtime()->jitSupportsFloatingPoint &&
- jit::CanLikelyAllocateMoreExecutableMemory())
- {
- if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
- return nullptr;
- }
-#endif
-
- return obj;
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
- HandleId id, MutableHandleObject objp,
- MutableHandleShape propp)
-{
- if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
- MarkNonNativePropertyFound<CanGC>(propp);
- objp.set(obj);
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- objp.set(nullptr);
- propp.set(nullptr);
- return true;
- }
-
- return LookupProperty(cx, proto, id, objp, propp);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
- Handle<PropertyDescriptor> desc,
- ObjectOpResult& result)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
- // This define is equivalent to setting an existing property.
- if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value()))
- return result.succeed();
- }
-
- // Trying to incompatibly redefine an existing property requires the
- // object to be converted to a native object.
- if (!convertToNative(cx, obj))
- return false;
-
- return DefineProperty(cx, obj, id, desc, result);
- }
-
- // Define the property on the expando object.
- Rooted<UnboxedExpandoObject*> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
- if (!expando)
- return false;
-
- // Update property types on the unboxed object as well.
- AddTypePropertyId(cx, obj, id, desc.value());
-
- return DefineProperty(cx, expando, id, desc, result);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
-{
- if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
- *foundp = true;
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- *foundp = false;
- return true;
- }
-
- return HasProperty(cx, proto, id, foundp);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
- HandleId id, MutableHandleValue vp)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- vp.set(obj->as<UnboxedPlainObject>().getValue(*property));
- return true;
- }
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- if (expando->containsShapeOrElement(cx, id)) {
- RootedObject nexpando(cx, expando);
- return GetProperty(cx, nexpando, receiver, id, vp);
- }
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- vp.setUndefined();
- return true;
- }
-
- return GetProperty(cx, proto, receiver, id, vp);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, ObjectOpResult& result)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- if (receiver.isObject() && obj == &receiver.toObject()) {
- if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
- return result.succeed();
-
- if (!convertToNative(cx, obj))
- return false;
- return SetProperty(cx, obj, id, v, receiver, result);
- }
-
- return SetPropertyByDefining(cx, id, v, receiver, result);
- }
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- if (expando->containsShapeOrElement(cx, id)) {
- // Update property types on the unboxed object as well.
- AddTypePropertyId(cx, obj, id, v);
-
- RootedObject nexpando(cx, expando);
- return SetProperty(cx, nexpando, id, v, receiver, result);
- }
- }
-
- return SetPropertyOnProto(cx, obj, id, v, receiver, result);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
- MutableHandle<PropertyDescriptor> desc)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- desc.value().set(obj->as<UnboxedPlainObject>().getValue(*property));
- desc.setAttributes(JSPROP_ENUMERATE);
- desc.object().set(obj);
- return true;
- }
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- if (expando->containsShapeOrElement(cx, id)) {
- RootedObject nexpando(cx, expando);
- if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc))
- return false;
- if (desc.object() == nexpando)
- desc.object().set(obj);
- return true;
- }
- }
-
- desc.object().set(nullptr);
- return true;
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
- ObjectOpResult& result)
-{
- if (!convertToNative(cx, obj))
- return false;
- return DeleteProperty(cx, obj, id, result);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
- if (!convertToNative(cx, obj))
- return false;
- return WatchProperty(cx, obj, id, callable);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
- bool enumerableOnly)
-{
- // Ignore expando properties here, they are special-cased by the property
- // enumeration code.
-
- const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
- for (size_t i = 0; i < unboxed.length(); i++) {
- if (!properties.append(NameToId(unboxed[i].name)))
- return false;
- }
-
- return true;
-}
-
-const Class UnboxedExpandoObject::class_ = {
- "UnboxedExpandoObject",
- 0
-};
-
-static const ClassOps UnboxedPlainObjectClassOps = {
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- nullptr, /* finalize */
- nullptr, /* call */
- nullptr, /* hasInstance */
- nullptr, /* construct */
- UnboxedPlainObject::trace,
-};
-
-static const ObjectOps UnboxedPlainObjectObjectOps = {
- UnboxedPlainObject::obj_lookupProperty,
- UnboxedPlainObject::obj_defineProperty,
- UnboxedPlainObject::obj_hasProperty,
- UnboxedPlainObject::obj_getProperty,
- UnboxedPlainObject::obj_setProperty,
- UnboxedPlainObject::obj_getOwnPropertyDescriptor,
- UnboxedPlainObject::obj_deleteProperty,
- UnboxedPlainObject::obj_watch,
- nullptr, /* No unwatch needed, as watch() converts the object to native */
- nullptr, /* getElements */
- UnboxedPlainObject::obj_enumerate,
- nullptr /* funToString */
-};
-
-const Class UnboxedPlainObject::class_ = {
- js_Object_str,
- Class::NON_NATIVE |
- JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
- JSCLASS_DELAY_METADATA_BUILDER,
- &UnboxedPlainObjectClassOps,
- JS_NULL_CLASS_SPEC,
- JS_NULL_CLASS_EXT,
- &UnboxedPlainObjectObjectOps
-};
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedArrayObject
-/////////////////////////////////////////////////////////////////////
-
-template <JSValueType Type>
-DenseElementResult
-AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen,
- MutableHandle<GCVector<Value>> values)
-{
- for (size_t i = 0; i < initlen; i++)
- values.infallibleAppend(obj->getElementSpecific<Type>(i));
- return DenseElementResult::Success;
-}
-
-DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
- UnboxedArrayObject*, uint32_t, MutableHandle<GCVector<Value>>);
-
-/* static */ bool
-UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
- ObjectGroup* group, Shape* shape)
-{
- size_t length = obj->as<UnboxedArrayObject>().length();
- size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
-
- Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
- if (!values.reserve(initlen))
- return false;
-
- AppendUnboxedDenseElementsFunctor functor(&obj->as<UnboxedArrayObject>(), initlen, &values);
- DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj);
- MOZ_ASSERT(result.value == DenseElementResult::Success);
-
- obj->setGroup(group);
-
- ArrayObject* aobj = &obj->as<ArrayObject>();
- aobj->setLastPropertyMakeNative(cx, shape);
-
- // Make sure there is at least one element, so that this array does not
- // use emptyObjectElements / emptyObjectElementsShared.
- if (!aobj->ensureElements(cx, Max<size_t>(initlen, 1)))
- return false;
-
- MOZ_ASSERT(!aobj->getDenseInitializedLength());
- aobj->setDenseInitializedLength(initlen);
- aobj->initDenseElements(0, values.begin(), initlen);
- aobj->setLengthInt32(length);
-
- return true;
-}
-
-/* static */ bool
-UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
-{
- const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
-
- if (!layout.nativeGroup()) {
- if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
- return false;
- }
-
- return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
-}
-
-bool
-UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group)
-{
- MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
- MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
-
- Vector<int32_t> values(cx);
- if (!values.reserve(initializedLength()))
- return false;
- for (size_t i = 0; i < initializedLength(); i++)
- values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32());
-
- uint8_t* newElements;
- if (hasInlineElements()) {
- newElements = AllocateObjectBuffer<uint8_t>(cx, this, capacity() * sizeof(double));
- } else {
- newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
- capacity() * sizeof(int32_t),
- capacity() * sizeof(double));
- }
- if (!newElements)
- return false;
-
- setGroup(group);
- elements_ = newElements;
-
- for (size_t i = 0; i < initializedLength(); i++)
- setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i]));
-
- return true;
-}
-
-/* static */ UnboxedArrayObject*
-UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
- NewObjectKind newKind, uint32_t maxLength)
-{
- MOZ_ASSERT(length <= MaximumCapacity);
-
- MOZ_ASSERT(group->clasp() == &class_);
- uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
- uint32_t capacity = Min(length, maxLength);
- uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
-
- UnboxedArrayObject* res;
- if (nbytes <= JSObject::MAX_BYTE_SIZE) {
- gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes);
-
- // If there was no provided length information, pick an allocation kind
- // to accommodate small arrays (as is done for normal native arrays).
- if (capacity == 0)
- allocKind = gc::AllocKind::OBJECT8;
-
- res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
- if (!res)
- return nullptr;
- res->setInitializedLengthNoBarrier(0);
- res->setInlineElements();
-
- size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
- MOZ_ASSERT(actualCapacity >= capacity);
- res->setCapacityIndex(exactCapacityIndex(actualCapacity));
- } else {
- res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
- if (!res)
- return nullptr;
- res->setInitializedLengthNoBarrier(0);
-
- uint32_t capacityIndex = (capacity == length)
- ? CapacityMatchesLengthIndex
- : chooseCapacityIndex(capacity, length);
- uint32_t actualCapacity = computeCapacity(capacityIndex, length);
-
- res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
- if (!res->elements_) {
- // Make the object safe for GC.
- res->setInlineElements();
- return nullptr;
- }
-
- res->setCapacityIndex(capacityIndex);
- }
-
- res->setLength(cx, length);
- return res;
-}
-
-bool
-UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
-}
-
-bool
-UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
-}
-
-void
-UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- if (UnboxedTypeNeedsPreBarrier(elementType()))
- *reinterpret_cast<void**>(p) = nullptr;
- SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
-}
-
-Value
-UnboxedArrayObject::getElement(size_t index)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false);
-}
-
-/* static */ void
-UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj)
-{
- JSValueType type = obj->as<UnboxedArrayObject>().elementType();
- if (!UnboxedTypeNeedsPreBarrier(type))
- return;
-
- MOZ_ASSERT(obj->as<UnboxedArrayObject>().elementSize() == sizeof(uintptr_t));
- size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
- void** elements = reinterpret_cast<void**>(obj->as<UnboxedArrayObject>().elements());
-
- switch (type) {
- case JSVAL_TYPE_OBJECT:
- for (size_t i = 0; i < initlen; i++) {
- GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(elements + i);
- TraceNullableEdge(trc, heap, "unboxed_object");
- }
- break;
-
- case JSVAL_TYPE_STRING:
- for (size_t i = 0; i < initlen; i++) {
- GCPtrString* heap = reinterpret_cast<GCPtrString*>(elements + i);
- TraceEdge(trc, heap, "unboxed_string");
- }
- break;
-
- default:
- MOZ_CRASH();
- }
-}
-
-/* static */ void
-UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
-{
- UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>();
- const UnboxedArrayObject& src = old->as<UnboxedArrayObject>();
-
- // Fix up possible inline data pointer.
- if (src.hasInlineElements())
- dst.setInlineElements();
-}
-
-/* static */ void
-UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
-{
- MOZ_ASSERT(!IsInsideNursery(obj));
- if (!obj->as<UnboxedArrayObject>().hasInlineElements())
- js_free(obj->as<UnboxedArrayObject>().elements());
-}
-
-/* static */ size_t
-UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
- gc::AllocKind allocKind)
-{
- UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
- UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
- MOZ_ASSERT(ndst->elements() == nsrc->elements());
-
- Nursery& nursery = trc->runtime()->gc.nursery;
-
- if (!nursery.isInside(nsrc->elements())) {
- nursery.removeMallocedBuffer(nsrc->elements());
- return 0;
- }
-
- // Determine if we can use inline data for the target array. If this is
- // possible, the nursery will have picked an allocation size that is large
- // enough.
- size_t nbytes = nsrc->capacity() * nsrc->elementSize();
- if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
- ndst->setInlineElements();
- } else {
- MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
-
- AutoEnterOOMUnsafeRegion oomUnsafe;
- uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
- if (!data)
- oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring.");
- ndst->elements_ = data;
- }
-
- PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
-
- // Set a forwarding pointer for the element buffers in case they were
- // preserved on the stack by Ion.
- bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
- nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
-
- return ndst->hasInlineElements() ? 0 : nbytes;
-}
-
-// Possible capacities for unboxed arrays. Some of these capacities might seem
-// a little weird, but were chosen to allow the inline data of objects of each
-// size to be fully utilized for arrays of the various types on both 32 bit and
-// 64 bit platforms.
-//
-// To find the possible inline capacities, the following script was used:
-//
-// var fixedSlotCapacities = [0, 2, 4, 8, 12, 16];
-// var dataSizes = [1, 4, 8];
-// var header32 = 4 * 2 + 4 * 2;
-// var header64 = 8 * 2 + 4 * 2;
-//
-// for (var i = 0; i < fixedSlotCapacities.length; i++) {
-// var nfixed = fixedSlotCapacities[i];
-// var size32 = 4 * 4 + 8 * nfixed - header32;
-// var size64 = 8 * 4 + 8 * nfixed - header64;
-// for (var j = 0; j < dataSizes.length; j++) {
-// print(size32 / dataSizes[j]);
-// print(size64 / dataSizes[j]);
-// }
-// }
-//
-/* static */ const uint32_t
-UnboxedArrayObject::CapacityArray[] = {
- UINT32_MAX, // For CapacityMatchesLengthIndex.
- 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,
- 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
- 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336,
- 13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464,
- 46137344, 52428800, 59768832, MaximumCapacity
-};
-
-static const uint32_t
-Pow2CapacityIndexes[] = {
- 2, // 1
- 3, // 2
- 5, // 4
- 8, // 8
- 13, // 16
- 18, // 32
- 21, // 64
- 25, // 128
- 27, // 256
- 28, // 512
- 29, // 1024
- 30, // 2048
- 31, // 4096
- 32, // 8192
- 33, // 16384
- 34, // 32768
- 35, // 65536
- 36, // 131072
- 37, // 262144
- 38, // 524288
- 39 // 1048576
-};
-
-static const uint32_t MebiCapacityIndex = 39;
-
-/* static */ uint32_t
-UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length)
-{
- // Note: the structure and behavior of this method follow along with
- // NativeObject::goodAllocated. Changes to the allocation strategy in one
- // should generally be matched by the other.
-
- // Make sure we have enough space to store all possible values for the capacity index.
- // This ought to be a static_assert, but MSVC doesn't like that.
- MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift));
-
- // The caller should have ensured the capacity is possible for an unboxed array.
- MOZ_ASSERT(capacity <= MaximumCapacity);
-
- static const uint32_t Mebi = 1024 * 1024;
-
- if (capacity <= Mebi) {
- capacity = mozilla::RoundUpPow2(capacity);
-
- // When the required capacity is close to the array length, then round
- // up to the array length itself, as for NativeObject.
- if (length >= capacity && capacity > (length / 3) * 2)
- return CapacityMatchesLengthIndex;
-
- if (capacity < MinimumDynamicCapacity)
- capacity = MinimumDynamicCapacity;
-
- uint32_t bit = mozilla::FloorLog2Size(capacity);
- MOZ_ASSERT(capacity == uint32_t(1 << bit));
- MOZ_ASSERT(bit <= 20);
- MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21);
-
- uint32_t index = Pow2CapacityIndexes[bit];
- MOZ_ASSERT(CapacityArray[index] == capacity);
-
- return index;
- }
-
- MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi);
-
- for (uint32_t i = MebiCapacityIndex + 1;; i++) {
- if (CapacityArray[i] >= capacity)
- return i;
- }
-
- MOZ_CRASH("Invalid capacity");
-}
-
-/* static */ uint32_t
-UnboxedArrayObject::exactCapacityIndex(uint32_t capacity)
-{
- for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) {
- if (CapacityArray[i] == capacity)
- return i;
- }
- MOZ_CRASH();
-}
-
-bool
-UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap)
-{
- // The caller should have checked if this capacity is possible for an
- // unboxed array, so the only way this call can fail is from OOM.
- MOZ_ASSERT(cap <= MaximumCapacity);
-
- uint32_t oldCapacity = capacity();
- uint32_t newCapacityIndex = chooseCapacityIndex(cap, length());
- uint32_t newCapacity = computeCapacity(newCapacityIndex, length());
-
- MOZ_ASSERT(oldCapacity < cap);
- MOZ_ASSERT(cap <= newCapacity);
-
- // The allocation size computation below cannot have integer overflows.
- JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double));
-
- uint8_t* newElements;
- if (hasInlineElements()) {
- newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize());
- if (!newElements)
- return false;
- js_memcpy(newElements, elements(), initializedLength() * elementSize());
- } else {
- newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
- oldCapacity * elementSize(),
- newCapacity * elementSize());
- if (!newElements)
- return false;
- }
-
- elements_ = newElements;
- setCapacityIndex(newCapacityIndex);
-
- return true;
-}
-
-void
-UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap)
-{
- if (hasInlineElements())
- return;
-
- uint32_t oldCapacity = capacity();
- uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0);
- uint32_t newCapacity = computeCapacity(newCapacityIndex, 0);
-
- MOZ_ASSERT(cap < oldCapacity);
- MOZ_ASSERT(cap <= newCapacity);
-
- if (newCapacity >= oldCapacity)
- return;
-
- uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
- oldCapacity * elementSize(),
- newCapacity * elementSize());
- if (!newElements)
- return;
-
- elements_ = newElements;
- setCapacityIndex(newCapacityIndex);
-}
-
-bool
-UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
-{
- if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
- return true;
- if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
- return true;
- return false;
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
- HandleId id, MutableHandleObject objp,
- MutableHandleShape propp)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- MarkNonNativePropertyFound<CanGC>(propp);
- objp.set(obj);
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- objp.set(nullptr);
- propp.set(nullptr);
- return true;
- }
-
- return LookupProperty(cx, proto, id, objp, propp);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
- Handle<PropertyDescriptor> desc,
- ObjectOpResult& result)
-{
- if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
- UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
-
- uint32_t index = JSID_TO_INT(id);
- if (index < nobj->initializedLength()) {
- if (nobj->setElement(cx, index, desc.value()))
- return result.succeed();
- } else if (index == nobj->initializedLength() && index < MaximumCapacity) {
- if (nobj->initializedLength() == nobj->capacity()) {
- if (!nobj->growElements(cx, index + 1))
- return false;
- }
- nobj->setInitializedLength(index + 1);
- if (nobj->initElement(cx, index, desc.value())) {
- if (nobj->length() <= index)
- nobj->setLengthInt32(index + 1);
- return result.succeed();
- }
- nobj->setInitializedLengthNoBarrier(index);
- }
- }
-
- if (!convertToNative(cx, obj))
- return false;
-
- return DefineProperty(cx, obj, id, desc, result);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- *foundp = true;
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- *foundp = false;
- return true;
- }
-
- return HasProperty(cx, proto, id, foundp);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
- HandleId id, MutableHandleValue vp)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- if (JSID_IS_INT(id))
- vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
- else
- vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- vp.setUndefined();
- return true;
- }
-
- return GetProperty(cx, proto, receiver, id, vp);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, ObjectOpResult& result)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- if (receiver.isObject() && obj == &receiver.toObject()) {
- if (JSID_IS_INT(id)) {
- if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v))
- return result.succeed();
- } else {
- uint32_t len;
- if (!CanonicalizeArrayLengthValue(cx, v, &len))
- return false;
- UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
- if (len < nobj->initializedLength()) {
- nobj->setInitializedLength(len);
- nobj->shrinkElements(cx, len);
- }
- nobj->setLength(cx, len);
- return result.succeed();
- }
-
- if (!convertToNative(cx, obj))
- return false;
- return SetProperty(cx, obj, id, v, receiver, result);
- }
-
- return SetPropertyByDefining(cx, id, v, receiver, result);
- }
-
- return SetPropertyOnProto(cx, obj, id, v, receiver, result);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
- MutableHandle<PropertyDescriptor> desc)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- if (JSID_IS_INT(id)) {
- desc.value().set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
- desc.setAttributes(JSPROP_ENUMERATE);
- } else {
- desc.value().set(Int32Value(obj->as<UnboxedArrayObject>().length()));
- desc.setAttributes(JSPROP_PERMANENT);
- }
- desc.object().set(obj);
- return true;
- }
-
- desc.object().set(nullptr);
- return true;
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
- ObjectOpResult& result)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
- if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
- obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
- obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
- return result.succeed();
- }
- }
-
- if (!convertToNative(cx, obj))
- return false;
- return DeleteProperty(cx, obj, id, result);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
- if (!convertToNative(cx, obj))
- return false;
- return WatchProperty(cx, obj, id, callable);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
- bool enumerableOnly)
-{
- for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) {
- if (!properties.append(INT_TO_JSID(i)))
- return false;
- }
-
- if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
- return false;
-
- return true;
-}
-
-static const ClassOps UnboxedArrayObjectClassOps = {
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- UnboxedArrayObject::finalize,
- nullptr, /* call */
- nullptr, /* hasInstance */
- nullptr, /* construct */
- UnboxedArrayObject::trace,
-};
-
-static const ClassExtension UnboxedArrayObjectClassExtension = {
- nullptr, /* weakmapKeyDelegateOp */
- UnboxedArrayObject::objectMoved
-};
-
-static const ObjectOps UnboxedArrayObjectObjectOps = {
- UnboxedArrayObject::obj_lookupProperty,
- UnboxedArrayObject::obj_defineProperty,
- UnboxedArrayObject::obj_hasProperty,
- UnboxedArrayObject::obj_getProperty,
- UnboxedArrayObject::obj_setProperty,
- UnboxedArrayObject::obj_getOwnPropertyDescriptor,
- UnboxedArrayObject::obj_deleteProperty,
- UnboxedArrayObject::obj_watch,
- nullptr, /* No unwatch needed, as watch() converts the object to native */
- nullptr, /* getElements */
- UnboxedArrayObject::obj_enumerate,
- nullptr /* funToString */
-};
-
-const Class UnboxedArrayObject::class_ = {
- "Array",
- Class::NON_NATIVE |
- JSCLASS_SKIP_NURSERY_FINALIZE |
- JSCLASS_BACKGROUND_FINALIZE,
- &UnboxedArrayObjectClassOps,
- JS_NULL_CLASS_SPEC,
- &UnboxedArrayObjectClassExtension,
- &UnboxedArrayObjectObjectOps
-};
-
-/////////////////////////////////////////////////////////////////////
-// API
-/////////////////////////////////////////////////////////////////////
-
-static inline Value
-NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- return values[(*valueCursor)++];
-}
-
-void
-UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
- Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- MOZ_ASSERT(CapacityArray[1] == 0);
- setCapacityIndex(1);
- setInitializedLengthNoBarrier(0);
- setInlineElements();
-
- setLength(cx, NextValue(values, valueCursor).toInt32());
-
- int32_t initlen = NextValue(values, valueCursor).toInt32();
- if (!initlen)
- return;
-
- AutoEnterOOMUnsafeRegion oomUnsafe;
- if (!growElements(cx, initlen))
- oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert");
-
- setInitializedLength(initlen);
-
- for (size_t i = 0; i < size_t(initlen); i++)
- JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor)));
-}
-
-void
-UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
- Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- initExpando();
- memset(data(), 0, layout().size());
- for (size_t i = 0; i < layout().properties().length(); i++)
- JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
-}
-
-DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
- ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t,
- ShouldUpdateTypes);
-
-DenseElementResult
-js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
- uint32_t start, const Value* vp, uint32_t count,
- ShouldUpdateTypes updateTypes)
-{
- SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, count, updateTypes);
- return CallBoxedOrUnboxedSpecialization(functor, obj);
-};
-
-DefineBoxedOrUnboxedFunctor5(MoveBoxedOrUnboxedDenseElements,
- JSContext*, JSObject*, uint32_t, uint32_t, uint32_t);
-
-DenseElementResult
-js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
- uint32_t dstStart, uint32_t srcStart, uint32_t length)
-{
- MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length);
- return CallBoxedOrUnboxedSpecialization(functor, obj);
-}
-
-DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
- JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
-
-DenseElementResult
-js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
- uint32_t dstStart, uint32_t srcStart, uint32_t length)
-{
- CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
- return CallBoxedOrUnboxedSpecialization(functor, dst, src);
-}
-
-DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
- JSContext*, JSObject*, size_t);
-
-void
-js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
-{
- SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
- JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
-}
-
-DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements,
- JSContext*, JSObject*, size_t);
-
-DenseElementResult
-js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen)
-{
- EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen);
- return CallBoxedOrUnboxedSpecialization(functor, obj);
-}