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, ¬Object);
144 0 : Register valueObject = masm.extractObject(valueAddress, scratch1);
145 0 : masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier);
146 0 : masm.bind(¬Object);
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) ? ¬Object : &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(¬Object);
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 : }
|