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 : #ifndef vm_NativeObject_inl_h
8 : #define vm_NativeObject_inl_h
9 :
10 : #include "vm/NativeObject.h"
11 :
12 : #include "jscntxt.h"
13 :
14 : #include "builtin/TypedObject.h"
15 : #include "proxy/Proxy.h"
16 : #include "vm/ProxyObject.h"
17 : #include "vm/TypedArrayObject.h"
18 :
19 : #include "jsobjinlines.h"
20 :
21 : #include "gc/Heap-inl.h"
22 :
23 : namespace js {
24 :
25 : inline uint8_t*
26 0 : NativeObject::fixedData(size_t nslots) const
27 : {
28 0 : MOZ_ASSERT(ClassCanHaveFixedData(getClass()));
29 0 : MOZ_ASSERT(nslots == numFixedSlots() + (hasPrivate() ? 1 : 0));
30 0 : return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots]);
31 : }
32 :
33 : inline void
34 39 : NativeObject::removeLastProperty(JSContext* cx)
35 : {
36 39 : MOZ_ASSERT(canRemoveLastProperty());
37 39 : JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->previous()));
38 39 : }
39 :
40 : inline bool
41 79 : NativeObject::canRemoveLastProperty()
42 : {
43 : /*
44 : * Check that the information about the object stored in the last
45 : * property's base shape is consistent with that stored in the previous
46 : * shape. If not consistent, then the last property cannot be removed as it
47 : * will induce a change in the object itself, and the object must be
48 : * converted to dictionary mode instead. See BaseShape comment in jsscope.h
49 : */
50 79 : MOZ_ASSERT(!inDictionaryMode());
51 79 : Shape* previous = lastProperty()->previous().get();
52 79 : return previous->getObjectFlags() == lastProperty()->getObjectFlags();
53 : }
54 :
55 : inline void
56 0 : NativeObject::setShouldConvertDoubleElements()
57 : {
58 0 : MOZ_ASSERT(is<ArrayObject>() && !hasEmptyElements());
59 0 : getElementsHeader()->setShouldConvertDoubleElements();
60 0 : }
61 :
62 : inline void
63 : NativeObject::clearShouldConvertDoubleElements()
64 : {
65 : MOZ_ASSERT(is<ArrayObject>() && !hasEmptyElements());
66 : getElementsHeader()->clearShouldConvertDoubleElements();
67 : }
68 :
69 : inline void
70 9006 : NativeObject::setDenseElementWithType(JSContext* cx, uint32_t index, const Value& val)
71 : {
72 : // Avoid a slow AddTypePropertyId call if the type is the same as the type
73 : // of the previous element.
74 9006 : TypeSet::Type thisType = TypeSet::GetValueType(val);
75 9006 : if (index == 0 || TypeSet::GetValueType(elements_[index - 1]) != thisType)
76 3812 : AddTypePropertyId(cx, this, JSID_VOID, thisType);
77 9006 : setDenseElementMaybeConvertDouble(index, val);
78 9006 : }
79 :
80 : inline void
81 0 : NativeObject::initDenseElementWithType(JSContext* cx, uint32_t index, const Value& val)
82 : {
83 0 : MOZ_ASSERT(!shouldConvertDoubleElements());
84 0 : if (val.isMagic(JS_ELEMENTS_HOLE))
85 0 : markDenseElementsNotPacked(cx);
86 : else
87 0 : AddTypePropertyId(cx, this, JSID_VOID, val);
88 0 : initDenseElement(index, val);
89 0 : }
90 :
91 : inline void
92 0 : NativeObject::setDenseElementHole(JSContext* cx, uint32_t index)
93 : {
94 0 : MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED);
95 0 : setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE));
96 0 : }
97 :
98 : /* static */ inline void
99 98 : NativeObject::removeDenseElementForSparseIndex(JSContext* cx,
100 : HandleNativeObject obj, uint32_t index)
101 : {
102 98 : MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_NON_PACKED | OBJECT_FLAG_SPARSE_INDEXES);
103 98 : if (obj->containsDenseElement(index))
104 0 : obj->setDenseElementUnchecked(index, MagicValue(JS_ELEMENTS_HOLE));
105 98 : }
106 :
107 : inline bool
108 8210 : NativeObject::writeToIndexWouldMarkNotPacked(uint32_t index)
109 : {
110 8210 : return getElementsHeader()->initializedLength < index;
111 : }
112 :
113 : inline void
114 41 : NativeObject::markDenseElementsNotPacked(JSContext* cx)
115 : {
116 41 : MOZ_ASSERT(isNative());
117 41 : MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED);
118 41 : }
119 :
120 : inline void
121 3183 : NativeObject::elementsRangeWriteBarrierPost(uint32_t start, uint32_t count)
122 : {
123 13611 : for (size_t i = 0; i < count; i++) {
124 10649 : const Value& v = elements_[start + i];
125 10649 : if (v.isObject() && IsInsideNursery(&v.toObject())) {
126 442 : zone()->group()->storeBuffer().putSlot(this, HeapSlot::Element,
127 221 : unshiftedIndex(start + i),
128 442 : count - i);
129 221 : return;
130 : }
131 : }
132 : }
133 :
134 : inline void
135 876 : NativeObject::copyDenseElements(uint32_t dstStart, const Value* src, uint32_t count)
136 : {
137 876 : MOZ_ASSERT(dstStart + count <= getDenseCapacity());
138 876 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
139 876 : MOZ_ASSERT(!denseElementsAreFrozen());
140 : #ifdef DEBUG
141 2003 : for (uint32_t i = 0; i < count; ++i)
142 1127 : checkStoredValue(src[i]);
143 : #endif
144 876 : if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
145 3 : uint32_t numShifted = getElementsHeader()->numShiftedElements();
146 17 : for (uint32_t i = 0; i < count; ++i) {
147 14 : elements_[dstStart + i].set(this, HeapSlot::Element,
148 14 : dstStart + i + numShifted,
149 28 : src[i]);
150 : }
151 : } else {
152 873 : memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
153 873 : elementsRangeWriteBarrierPost(dstStart, count);
154 : }
155 876 : }
156 :
157 : inline void
158 2280 : NativeObject::initDenseElements(uint32_t dstStart, const Value* src, uint32_t count)
159 : {
160 2280 : MOZ_ASSERT(dstStart + count <= getDenseCapacity());
161 2280 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
162 2280 : MOZ_ASSERT(!denseElementsAreFrozen());
163 : #ifdef DEBUG
164 11823 : for (uint32_t i = 0; i < count; ++i)
165 9543 : checkStoredValue(src[i]);
166 : #endif
167 2280 : memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
168 2280 : elementsRangeWriteBarrierPost(dstStart, count);
169 2280 : }
170 :
171 : inline bool
172 7 : NativeObject::tryShiftDenseElements(uint32_t count)
173 : {
174 7 : ObjectElements* header = getElementsHeader();
175 21 : if (header->initializedLength == count ||
176 7 : count > ObjectElements::MaxShiftedElements ||
177 14 : header->isCopyOnWrite() ||
178 21 : header->isFrozen() ||
179 7 : header->hasNonwritableArrayLength())
180 : {
181 0 : return false;
182 : }
183 :
184 7 : shiftDenseElementsUnchecked(count);
185 7 : return true;
186 : }
187 :
188 : inline void
189 7 : NativeObject::shiftDenseElementsUnchecked(uint32_t count)
190 : {
191 7 : ObjectElements* header = getElementsHeader();
192 7 : MOZ_ASSERT(count > 0);
193 7 : MOZ_ASSERT(count < header->initializedLength);
194 :
195 7 : if (MOZ_UNLIKELY(header->numShiftedElements() + count > ObjectElements::MaxShiftedElements)) {
196 0 : moveShiftedElements();
197 0 : header = getElementsHeader();
198 : }
199 :
200 7 : prepareElementRangeForOverwrite(0, count);
201 7 : header->addShiftedElements(count);
202 :
203 7 : elements_ += count;
204 7 : ObjectElements* newHeader = getElementsHeader();
205 7 : memmove(newHeader, header, sizeof(ObjectElements));
206 7 : }
207 :
208 : inline void
209 34 : NativeObject::moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count)
210 : {
211 34 : MOZ_ASSERT(dstStart + count <= getDenseCapacity());
212 34 : MOZ_ASSERT(srcStart + count <= getDenseInitializedLength());
213 34 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
214 34 : MOZ_ASSERT(!denseElementsAreFrozen());
215 :
216 : /*
217 : * Using memmove here would skip write barriers. Also, we need to consider
218 : * an array containing [A, B, C], in the following situation:
219 : *
220 : * 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code.
221 : * 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C].
222 : * 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C).
223 : *
224 : * Since normal marking never happens on B, it is very important that the
225 : * write barrier is invoked here on B, despite the fact that it exists in
226 : * the array before and after the move.
227 : */
228 34 : if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
229 4 : uint32_t numShifted = getElementsHeader()->numShiftedElements();
230 4 : if (dstStart < srcStart) {
231 0 : HeapSlot* dst = elements_ + dstStart;
232 0 : HeapSlot* src = elements_ + srcStart;
233 0 : for (uint32_t i = 0; i < count; i++, dst++, src++)
234 0 : dst->set(this, HeapSlot::Element, dst - elements_ + numShifted, *src);
235 : } else {
236 4 : HeapSlot* dst = elements_ + dstStart + count - 1;
237 4 : HeapSlot* src = elements_ + srcStart + count - 1;
238 19 : for (uint32_t i = 0; i < count; i++, dst--, src--)
239 15 : dst->set(this, HeapSlot::Element, dst - elements_ + numShifted, *src);
240 : }
241 : } else {
242 30 : memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot));
243 30 : elementsRangeWriteBarrierPost(dstStart, count);
244 : }
245 34 : }
246 :
247 : inline void
248 0 : NativeObject::moveDenseElementsNoPreBarrier(uint32_t dstStart, uint32_t srcStart, uint32_t count)
249 : {
250 0 : MOZ_ASSERT(!shadowZone()->needsIncrementalBarrier());
251 :
252 0 : MOZ_ASSERT(dstStart + count <= getDenseCapacity());
253 0 : MOZ_ASSERT(srcStart + count <= getDenseCapacity());
254 0 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
255 0 : MOZ_ASSERT(!denseElementsAreFrozen());
256 :
257 0 : memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(Value));
258 0 : elementsRangeWriteBarrierPost(dstStart, count);
259 0 : }
260 :
261 : inline void
262 8180 : NativeObject::ensureDenseInitializedLengthNoPackedCheck(JSContext* cx, uint32_t index,
263 : uint32_t extra)
264 : {
265 8180 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
266 8180 : MOZ_ASSERT(!denseElementsAreFrozen());
267 :
268 : /*
269 : * Ensure that the array's contents have been initialized up to index, and
270 : * mark the elements through 'index + extra' as initialized in preparation
271 : * for a write.
272 : */
273 8180 : MOZ_ASSERT(index + extra <= getDenseCapacity());
274 8180 : uint32_t& initlen = getElementsHeader()->initializedLength;
275 :
276 8180 : if (initlen < index + extra) {
277 7772 : uint32_t numShifted = getElementsHeader()->numShiftedElements();
278 7772 : size_t offset = initlen;
279 17607 : for (HeapSlot* sp = elements_ + initlen;
280 17607 : sp != elements_ + (index + extra);
281 : sp++, offset++)
282 : {
283 9835 : sp->init(this, HeapSlot::Element, offset + numShifted, MagicValue(JS_ELEMENTS_HOLE));
284 : }
285 7772 : initlen = index + extra;
286 : }
287 8180 : }
288 :
289 : inline void
290 0 : NativeObject::ensureDenseInitializedLength(JSContext* cx, uint32_t index, uint32_t extra)
291 : {
292 0 : if (writeToIndexWouldMarkNotPacked(index))
293 0 : markDenseElementsNotPacked(cx);
294 0 : ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
295 0 : }
296 :
297 : DenseElementResult
298 93 : NativeObject::extendDenseElements(JSContext* cx,
299 : uint32_t requiredCapacity, uint32_t extra)
300 : {
301 93 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
302 93 : MOZ_ASSERT(!denseElementsAreFrozen());
303 :
304 : /*
305 : * Don't grow elements for non-extensible objects or watched objects. Dense
306 : * elements can be added/written with no extensible or watchpoint checks as
307 : * long as there is capacity for them.
308 : */
309 93 : if (!nonProxyIsExtensible() || watched()) {
310 0 : MOZ_ASSERT(getDenseCapacity() == 0);
311 0 : return DenseElementResult::Incomplete;
312 : }
313 :
314 : /*
315 : * Don't grow elements for objects which already have sparse indexes.
316 : * This avoids needing to count non-hole elements in willBeSparseElements
317 : * every time a new index is added.
318 : */
319 93 : if (isIndexed())
320 29 : return DenseElementResult::Incomplete;
321 :
322 : /*
323 : * We use the extra argument also as a hint about number of non-hole
324 : * elements to be inserted.
325 : */
326 65 : if (requiredCapacity > MIN_SPARSE_INDEX &&
327 1 : willBeSparseElements(requiredCapacity, extra)) {
328 1 : return DenseElementResult::Incomplete;
329 : }
330 :
331 63 : if (!growElements(cx, requiredCapacity))
332 0 : return DenseElementResult::Failure;
333 :
334 63 : return DenseElementResult::Success;
335 : }
336 :
337 : inline DenseElementResult
338 8210 : NativeObject::ensureDenseElements(JSContext* cx, uint32_t index, uint32_t extra)
339 : {
340 8210 : MOZ_ASSERT(isNative());
341 :
342 8210 : if (writeToIndexWouldMarkNotPacked(index))
343 41 : markDenseElementsNotPacked(cx);
344 :
345 8210 : if (!maybeCopyElementsForWrite(cx))
346 0 : return DenseElementResult::Failure;
347 :
348 8210 : uint32_t currentCapacity = getDenseCapacity();
349 :
350 : uint32_t requiredCapacity;
351 8210 : if (extra == 1) {
352 : /* Optimize for the common case. */
353 7283 : if (index < currentCapacity) {
354 7191 : ensureDenseInitializedLengthNoPackedCheck(cx, index, 1);
355 7191 : return DenseElementResult::Success;
356 : }
357 92 : requiredCapacity = index + 1;
358 92 : if (requiredCapacity == 0) {
359 : /* Overflow. */
360 0 : return DenseElementResult::Incomplete;
361 : }
362 : } else {
363 927 : requiredCapacity = index + extra;
364 927 : if (requiredCapacity < index) {
365 : /* Overflow. */
366 0 : return DenseElementResult::Incomplete;
367 : }
368 927 : if (requiredCapacity <= currentCapacity) {
369 926 : ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
370 926 : return DenseElementResult::Success;
371 : }
372 : }
373 :
374 93 : DenseElementResult result = extendDenseElements(cx, requiredCapacity, extra);
375 93 : if (result != DenseElementResult::Success)
376 30 : return result;
377 :
378 63 : ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
379 63 : return DenseElementResult::Success;
380 : }
381 :
382 : inline Value
383 3633 : NativeObject::getDenseOrTypedArrayElement(uint32_t idx)
384 : {
385 3633 : if (is<TypedArrayObject>())
386 0 : return as<TypedArrayObject>().getElement(idx);
387 3633 : return getDenseElement(idx);
388 : }
389 :
390 : /* static */ inline NativeObject*
391 : NativeObject::copy(JSContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
392 : HandleNativeObject templateObject)
393 : {
394 : RootedShape shape(cx, templateObject->lastProperty());
395 : RootedObjectGroup group(cx, templateObject->group());
396 : MOZ_ASSERT(!templateObject->denseElementsAreCopyOnWrite());
397 :
398 : JSObject* baseObj;
399 : JS_TRY_VAR_OR_RETURN_NULL(cx, baseObj, create(cx, kind, heap, shape, group));
400 :
401 : NativeObject* obj = &baseObj->as<NativeObject>();
402 :
403 : size_t span = shape->slotSpan();
404 : if (span) {
405 : uint32_t numFixed = templateObject->numFixedSlots();
406 : const Value* fixed = &templateObject->getSlot(0);
407 : // Only copy elements which are registered in the shape, even if the
408 : // number of fixed slots is larger.
409 : if (span < numFixed)
410 : numFixed = span;
411 : obj->copySlotRange(0, fixed, numFixed);
412 :
413 : if (numFixed < span) {
414 : uint32_t numSlots = span - numFixed;
415 : const Value* slots = &templateObject->getSlot(numFixed);
416 : obj->copySlotRange(numFixed, slots, numSlots);
417 : }
418 : }
419 :
420 : return obj;
421 : }
422 :
423 : MOZ_ALWAYS_INLINE void
424 115087 : NativeObject::setSlotWithType(JSContext* cx, Shape* shape,
425 : const Value& value, bool overwriting)
426 : {
427 115087 : setSlot(shape->slot(), value);
428 :
429 115087 : if (overwriting)
430 2306 : shape->setOverwritten();
431 :
432 115087 : AddTypePropertyId(cx, this, shape->propid(), value);
433 115084 : }
434 :
435 : inline void
436 18577 : NativeObject::updateShapeAfterMovingGC()
437 : {
438 18577 : Shape* shape = shape_;
439 18577 : if (IsForwarded(shape))
440 0 : shape_.unsafeSet(Forwarded(shape));
441 18577 : }
442 :
443 : inline bool
444 0 : NativeObject::isInWholeCellBuffer() const
445 : {
446 0 : const gc::TenuredCell* cell = &asTenured();
447 0 : gc::ArenaCellSet* cells = cell->arena()->bufferedCells();
448 0 : return cells && cells->hasCell(cell);
449 : }
450 :
451 : /* static */ inline JS::Result<NativeObject*, JS::OOM&>
452 131350 : NativeObject::create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
453 : js::HandleShape shape, js::HandleObjectGroup group)
454 : {
455 131350 : debugCheckNewObject(group, shape, kind, heap);
456 :
457 131352 : const js::Class* clasp = group->clasp();
458 131352 : MOZ_ASSERT(clasp->isNative());
459 :
460 131352 : size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
461 :
462 131351 : JSObject* obj = js::Allocate<JSObject>(cx, kind, nDynamicSlots, heap, clasp);
463 131353 : if (!obj)
464 0 : return cx->alreadyReportedOOM();
465 :
466 131353 : NativeObject* nobj = static_cast<NativeObject*>(obj);
467 131353 : nobj->group_.init(group);
468 131353 : nobj->initShape(shape);
469 :
470 : // Note: slots are created and assigned internally by Allocate<JSObject>.
471 131353 : nobj->setInitialElementsMaybeNonNative(js::emptyObjectElements);
472 :
473 131353 : if (clasp->hasPrivate())
474 8061 : nobj->privateRef(shape->numFixedSlots()) = nullptr;
475 :
476 131353 : if (size_t span = shape->slotSpan())
477 15348 : nobj->initializeSlotRange(0, span);
478 :
479 : // JSFunction's fixed slots expect POD-style initialization.
480 131353 : if (clasp->isJSFunction()) {
481 95396 : MOZ_ASSERT(kind == js::gc::AllocKind::FUNCTION ||
482 : kind == js::gc::AllocKind::FUNCTION_EXTENDED);
483 : size_t size =
484 95396 : kind == js::gc::AllocKind::FUNCTION ? sizeof(JSFunction) : sizeof(js::FunctionExtended);
485 95396 : memset(nobj->as<JSFunction>().fixedSlots(), 0, size - sizeof(js::NativeObject));
486 95396 : if (kind == js::gc::AllocKind::FUNCTION_EXTENDED) {
487 : // SetNewObjectMetadata may gc, which will be unhappy if flags &
488 : // EXTENDED doesn't match the arena's AllocKind.
489 43426 : nobj->as<JSFunction>().setFlags(JSFunction::EXTENDED);
490 : }
491 : }
492 :
493 131353 : if (clasp->shouldDelayMetadataBuilder())
494 139 : cx->compartment()->setObjectPendingMetadata(cx, nobj);
495 : else
496 131214 : nobj = SetNewObjectMetadata(cx, nobj);
497 :
498 131353 : js::gc::TraceCreateObject(nobj);
499 :
500 131353 : return nobj;
501 : }
502 :
503 : MOZ_ALWAYS_INLINE uint32_t
504 2374296 : NativeObject::numDynamicSlots() const
505 : {
506 2374296 : return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
507 : }
508 :
509 : /* static */ MOZ_ALWAYS_INLINE uint32_t
510 2769934 : NativeObject::dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class* clasp)
511 : {
512 2769934 : if (span <= nfixed)
513 962799 : return 0;
514 1807135 : span -= nfixed;
515 :
516 : // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
517 : // the dynamic slots need to get increased again. ArrayObjects ignore
518 : // this because slots are uncommon in that case.
519 1807135 : if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN)
520 350239 : return SLOT_CAPACITY_MIN;
521 :
522 1456896 : uint32_t slots = mozilla::RoundUpPow2(span);
523 1456987 : MOZ_ASSERT(slots >= span);
524 1456987 : return slots;
525 : }
526 :
527 : /* static */ MOZ_ALWAYS_INLINE uint32_t
528 668 : NativeObject::dynamicSlotsCount(Shape* shape)
529 : {
530 668 : return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), shape->getObjectClass());
531 : }
532 :
533 : MOZ_ALWAYS_INLINE bool
534 129656 : NativeObject::updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan)
535 : {
536 129656 : MOZ_ASSERT(oldSpan != newSpan);
537 :
538 129656 : size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan, getClass());
539 129656 : size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan, getClass());
540 :
541 129657 : if (oldSpan < newSpan) {
542 129641 : if (oldCount < newCount && !growSlots(cx, oldCount, newCount))
543 0 : return false;
544 :
545 129641 : if (newSpan == oldSpan + 1)
546 125300 : initSlotUnchecked(oldSpan, UndefinedValue());
547 : else
548 4341 : initializeSlotRange(oldSpan, newSpan - oldSpan);
549 : } else {
550 : /* Trigger write barriers on the old slots before reallocating. */
551 16 : prepareSlotRangeForOverwrite(newSpan, oldSpan);
552 16 : invalidateSlotRange(newSpan, oldSpan - newSpan);
553 :
554 16 : if (oldCount > newCount)
555 0 : shrinkSlots(cx, oldCount, newCount);
556 : }
557 :
558 129656 : return true;
559 : }
560 :
561 : MOZ_ALWAYS_INLINE bool
562 145282 : NativeObject::setLastProperty(JSContext* cx, Shape* shape)
563 : {
564 145282 : MOZ_ASSERT(!inDictionaryMode());
565 145282 : MOZ_ASSERT(!shape->inDictionary());
566 145282 : MOZ_ASSERT(shape->zone() == zone());
567 145281 : MOZ_ASSERT(shape->numFixedSlots() == numFixedSlots());
568 145281 : MOZ_ASSERT(shape->getObjectClass() == getClass());
569 :
570 145281 : size_t oldSpan = lastProperty()->slotSpan();
571 145280 : size_t newSpan = shape->slotSpan();
572 :
573 145281 : if (oldSpan == newSpan) {
574 17564 : shape_ = shape;
575 17564 : return true;
576 : }
577 :
578 127717 : if (MOZ_UNLIKELY(!updateSlotsForSpan(cx, oldSpan, newSpan)))
579 0 : return false;
580 :
581 127717 : shape_ = shape;
582 127717 : return true;
583 : }
584 :
585 : /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
586 : static inline PlainObject*
587 3725 : CopyInitializerObject(JSContext* cx, HandlePlainObject baseobj, NewObjectKind newKind = GenericObject)
588 : {
589 3725 : MOZ_ASSERT(!baseobj->inDictionaryMode());
590 :
591 3725 : gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
592 3725 : allocKind = gc::GetBackgroundAllocKind(allocKind);
593 3725 : MOZ_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->asTenured().getAllocKind());
594 7450 : RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
595 3725 : if (!obj)
596 0 : return nullptr;
597 :
598 3725 : if (!obj->setLastProperty(cx, baseobj->lastProperty()))
599 0 : return nullptr;
600 :
601 3725 : return obj;
602 : }
603 :
604 : inline NativeObject*
605 : NewNativeObjectWithGivenTaggedProto(JSContext* cx, const Class* clasp,
606 : Handle<TaggedProto> proto,
607 : gc::AllocKind allocKind, NewObjectKind newKind)
608 : {
609 : return MaybeNativeObject(NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind,
610 : newKind));
611 : }
612 :
613 : inline NativeObject*
614 : NewNativeObjectWithGivenTaggedProto(JSContext* cx, const Class* clasp,
615 : Handle<TaggedProto> proto,
616 : NewObjectKind newKind = GenericObject)
617 : {
618 : return MaybeNativeObject(NewObjectWithGivenTaggedProto(cx, clasp, proto, newKind));
619 : }
620 :
621 : inline NativeObject*
622 : NewNativeObjectWithGivenProto(JSContext* cx, const Class* clasp,
623 : HandleObject proto,
624 : gc::AllocKind allocKind, NewObjectKind newKind)
625 : {
626 : return MaybeNativeObject(NewObjectWithGivenProto(cx, clasp, proto, allocKind, newKind));
627 : }
628 :
629 : inline NativeObject*
630 1133 : NewNativeObjectWithGivenProto(JSContext* cx, const Class* clasp,
631 : HandleObject proto,
632 : NewObjectKind newKind = GenericObject)
633 : {
634 1133 : return MaybeNativeObject(NewObjectWithGivenProto(cx, clasp, proto, newKind));
635 : }
636 :
637 : inline NativeObject*
638 : NewNativeObjectWithClassProto(JSContext* cx, const Class* clasp, HandleObject proto,
639 : gc::AllocKind allocKind,
640 : NewObjectKind newKind = GenericObject)
641 : {
642 : return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind));
643 : }
644 :
645 : inline NativeObject*
646 4 : NewNativeObjectWithClassProto(JSContext* cx, const Class* clasp, HandleObject proto,
647 : NewObjectKind newKind = GenericObject)
648 : {
649 4 : return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, newKind));
650 : }
651 :
652 : /*
653 : * Call obj's resolve hook.
654 : *
655 : * cx and id are the parameters initially passed to the ongoing lookup;
656 : * propp and recursedp are its out parameters.
657 : *
658 : * There are four possible outcomes:
659 : *
660 : * - On failure, report an error or exception and return false.
661 : *
662 : * - If we are already resolving a property of obj, set *recursedp = true,
663 : * and return true.
664 : *
665 : * - If the resolve hook finds or defines the sought property, set propp
666 : * appropriately, set *recursedp = false, and return true.
667 : *
668 : * - Otherwise no property was resolved. Set propp to nullptr and
669 : * *recursedp = false and return true.
670 : */
671 : static MOZ_ALWAYS_INLINE bool
672 57467 : CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id,
673 : MutableHandle<PropertyResult> propp, bool* recursedp)
674 : {
675 : // Avoid recursion on (obj, id) already being resolved on cx.
676 114934 : AutoResolving resolving(cx, obj, id);
677 57467 : if (resolving.alreadyStarted()) {
678 : // Already resolving id in obj, suppress recursion.
679 0 : *recursedp = true;
680 0 : return true;
681 : }
682 57467 : *recursedp = false;
683 :
684 57467 : bool resolved = false;
685 57467 : if (!obj->getClass()->getResolve()(cx, obj, id, &resolved))
686 0 : return false;
687 :
688 57467 : if (!resolved)
689 51947 : return true;
690 :
691 : // Assert the mayResolve hook, if there is one, returns true for this
692 : // property.
693 5520 : MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
694 : obj->getClass()->getMayResolve()(cx->names(), id, obj));
695 :
696 5520 : if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
697 0 : propp.setDenseOrTypedArrayElement();
698 0 : return true;
699 : }
700 :
701 5520 : MOZ_ASSERT(!obj->is<TypedArrayObject>());
702 :
703 11040 : RootedShape shape(cx, obj->lookup(cx, id));
704 5520 : if (shape)
705 5520 : propp.setNativeProperty(shape);
706 : else
707 0 : propp.setNotFound();
708 :
709 5520 : return true;
710 : }
711 :
712 : template <AllowGC allowGC>
713 : static MOZ_ALWAYS_INLINE bool
714 310347 : LookupOwnPropertyInline(JSContext* cx,
715 : typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
716 : typename MaybeRooted<jsid, allowGC>::HandleType id,
717 : typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp,
718 : bool* donep)
719 : {
720 : // Check for a native dense element.
721 310347 : if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
722 4267 : propp.setDenseOrTypedArrayElement();
723 4267 : *donep = true;
724 4267 : return true;
725 : }
726 :
727 : // Check for a typed array element. Integer lookups always finish here
728 : // so that integer properties on the prototype are ignored even for out
729 : // of bounds accesses.
730 306080 : if (obj->template is<TypedArrayObject>()) {
731 : uint64_t index;
732 0 : if (IsTypedArrayIndex(id, &index)) {
733 0 : if (index < obj->template as<TypedArrayObject>().length())
734 0 : propp.setDenseOrTypedArrayElement();
735 : else
736 0 : propp.setNotFound();
737 0 : *donep = true;
738 0 : return true;
739 : }
740 : }
741 :
742 : // Check for a native property. Call Shape::search directly (instead of
743 : // NativeObject::lookup) because it's inlined.
744 306080 : if (Shape* shape = obj->lastProperty()->search(cx, id)) {
745 85880 : propp.setNativeProperty(shape);
746 85880 : *donep = true;
747 85880 : return true;
748 : }
749 :
750 : // id was not found in obj. Try obj's resolve hook, if any.
751 220199 : if (obj->getClass()->getResolve()) {
752 58274 : MOZ_ASSERT(!cx->helperThread());
753 : if (!allowGC)
754 6327 : return false;
755 :
756 : bool recursed;
757 57467 : if (!CallResolveOp(cx,
758 : MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
759 : MaybeRooted<jsid, allowGC>::toHandle(id),
760 : MaybeRooted<PropertyResult, allowGC>::toMutableHandle(propp),
761 : &recursed))
762 : {
763 0 : return false;
764 : }
765 :
766 57467 : if (recursed) {
767 0 : propp.setNotFound();
768 0 : *donep = true;
769 0 : return true;
770 : }
771 :
772 57467 : if (propp) {
773 5520 : *donep = true;
774 5520 : return true;
775 : }
776 : }
777 :
778 213872 : propp.setNotFound();
779 213872 : *donep = false;
780 213872 : return true;
781 : }
782 :
783 : /*
784 : * Simplified version of LookupOwnPropertyInline that doesn't call resolve
785 : * hooks.
786 : */
787 : static inline void
788 14774 : NativeLookupOwnPropertyNoResolve(JSContext* cx, HandleNativeObject obj, HandleId id,
789 : MutableHandle<PropertyResult> result)
790 : {
791 : // Check for a native dense element.
792 14774 : if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
793 0 : result.setDenseOrTypedArrayElement();
794 0 : return;
795 : }
796 :
797 : // Check for a typed array element.
798 14774 : if (obj->is<TypedArrayObject>()) {
799 : uint64_t index;
800 0 : if (IsTypedArrayIndex(id, &index)) {
801 0 : if (index < obj->as<TypedArrayObject>().length())
802 0 : result.setDenseOrTypedArrayElement();
803 : else
804 0 : result.setNotFound();
805 0 : return;
806 : }
807 : }
808 :
809 : // Check for a native property.
810 14774 : if (Shape* shape = obj->lookup(cx, id))
811 14 : result.setNativeProperty(shape);
812 : else
813 14760 : result.setNotFound();
814 : }
815 :
816 : template <AllowGC allowGC>
817 : static MOZ_ALWAYS_INLINE bool
818 44965 : LookupPropertyInline(JSContext* cx,
819 : typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
820 : typename MaybeRooted<jsid, allowGC>::HandleType id,
821 : typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
822 : typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp)
823 : {
824 : /* Search scopes starting with obj and following the prototype link. */
825 59093 : typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
826 :
827 11182 : while (true) {
828 : bool done;
829 56147 : if (!LookupOwnPropertyInline<allowGC>(cx, current, id, propp, &done))
830 19850 : return false;
831 55494 : if (done) {
832 18147 : if (propp)
833 18147 : objp.set(current);
834 : else
835 0 : objp.set(nullptr);
836 18147 : return true;
837 : }
838 :
839 48529 : typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->staticPrototype());
840 :
841 37347 : if (!proto)
842 25768 : break;
843 11579 : if (!proto->isNative()) {
844 397 : MOZ_ASSERT(!cx->helperThread());
845 : if (!allowGC)
846 0 : return false;
847 794 : return LookupProperty(cx,
848 : MaybeRooted<JSObject*, allowGC>::toHandle(proto),
849 : MaybeRooted<jsid, allowGC>::toHandle(id),
850 : MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
851 397 : MaybeRooted<PropertyResult, allowGC>::toMutableHandle(propp));
852 : }
853 :
854 11182 : current = &proto->template as<NativeObject>();
855 : }
856 :
857 25768 : objp.set(nullptr);
858 25768 : propp.setNotFound();
859 25768 : return true;
860 : }
861 :
862 : inline bool
863 569 : ThrowIfNotConstructing(JSContext *cx, const CallArgs &args, const char *builtinName)
864 : {
865 569 : if (args.isConstructing())
866 569 : return true;
867 : return JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
868 0 : JSMSG_BUILTIN_CTOR_NO_NEW, builtinName);
869 : }
870 :
871 : inline bool
872 350 : IsPackedArray(JSObject* obj)
873 : {
874 968 : return obj->is<ArrayObject>() && !obj->hasLazyGroup() &&
875 957 : !obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) &&
876 648 : obj->as<ArrayObject>().getDenseInitializedLength() == obj->as<ArrayObject>().length();
877 : }
878 :
879 : } // namespace js
880 :
881 : #endif /* vm_NativeObject_inl_h */
|