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_UnboxedObject_inl_h
8 : #define vm_UnboxedObject_inl_h
9 :
10 : #include "vm/UnboxedObject.h"
11 :
12 : #include "gc/StoreBuffer-inl.h"
13 : #include "vm/ArrayObject-inl.h"
14 : #include "vm/NativeObject-inl.h"
15 :
16 : namespace js {
17 :
18 : static inline Value
19 3899 : GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized)
20 : {
21 3899 : switch (type) {
22 : case JSVAL_TYPE_BOOLEAN:
23 2572 : return BooleanValue(*p != 0);
24 :
25 : case JSVAL_TYPE_INT32:
26 0 : return Int32Value(*reinterpret_cast<int32_t*>(p));
27 :
28 : case JSVAL_TYPE_DOUBLE: {
29 : // During unboxed plain object creation, non-GC thing properties are
30 : // left uninitialized. This is normally fine, since the properties will
31 : // be filled in shortly, but if they are read before that happens we
32 : // need to make sure that doubles are canonical.
33 0 : double d = *reinterpret_cast<double*>(p);
34 0 : if (maybeUninitialized)
35 0 : return DoubleValue(JS::CanonicalizeNaN(d));
36 0 : return DoubleValue(d);
37 : }
38 :
39 : case JSVAL_TYPE_STRING:
40 7 : return StringValue(*reinterpret_cast<JSString**>(p));
41 :
42 : case JSVAL_TYPE_OBJECT:
43 1320 : return ObjectOrNullValue(*reinterpret_cast<JSObject**>(p));
44 :
45 : default:
46 0 : MOZ_CRASH("Invalid type for unboxed value");
47 : }
48 : }
49 :
50 : static inline void
51 0 : SetUnboxedValueNoTypeChange(JSObject* unboxedObject,
52 : uint8_t* p, JSValueType type, const Value& v,
53 : bool preBarrier)
54 : {
55 0 : switch (type) {
56 : case JSVAL_TYPE_BOOLEAN:
57 0 : *p = v.toBoolean();
58 0 : return;
59 :
60 : case JSVAL_TYPE_INT32:
61 0 : *reinterpret_cast<int32_t*>(p) = v.toInt32();
62 0 : return;
63 :
64 : case JSVAL_TYPE_DOUBLE:
65 0 : *reinterpret_cast<double*>(p) = v.toNumber();
66 0 : return;
67 :
68 : case JSVAL_TYPE_STRING: {
69 0 : MOZ_ASSERT(!IsInsideNursery(v.toString()));
70 0 : JSString** np = reinterpret_cast<JSString**>(p);
71 0 : if (preBarrier)
72 0 : JSString::writeBarrierPre(*np);
73 0 : *np = v.toString();
74 0 : return;
75 : }
76 :
77 : case JSVAL_TYPE_OBJECT: {
78 0 : JSObject** np = reinterpret_cast<JSObject**>(p);
79 :
80 : // Manually trigger post barriers on the whole object. If we treat
81 : // the pointer as a HeapPtrObject we will get confused later if the
82 : // object is converted to its native representation.
83 0 : JSObject* obj = v.toObjectOrNull();
84 0 : if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject))
85 0 : unboxedObject->zone()->group()->storeBuffer().putWholeCell(unboxedObject);
86 :
87 0 : if (preBarrier)
88 0 : JSObject::writeBarrierPre(*np);
89 0 : *np = obj;
90 0 : return;
91 : }
92 :
93 : default:
94 0 : MOZ_CRASH("Invalid type for unboxed value");
95 : }
96 : }
97 :
98 : static inline bool
99 1003 : SetUnboxedValue(JSContext* cx, JSObject* unboxedObject, jsid id,
100 : uint8_t* p, JSValueType type, const Value& v, bool preBarrier)
101 : {
102 1003 : switch (type) {
103 : case JSVAL_TYPE_BOOLEAN:
104 464 : if (v.isBoolean()) {
105 464 : *p = v.toBoolean();
106 464 : return true;
107 : }
108 0 : return false;
109 :
110 : case JSVAL_TYPE_INT32:
111 0 : if (v.isInt32()) {
112 0 : *reinterpret_cast<int32_t*>(p) = v.toInt32();
113 0 : return true;
114 : }
115 0 : return false;
116 :
117 : case JSVAL_TYPE_DOUBLE:
118 0 : if (v.isNumber()) {
119 0 : *reinterpret_cast<double*>(p) = v.toNumber();
120 0 : return true;
121 : }
122 0 : return false;
123 :
124 : case JSVAL_TYPE_STRING:
125 126 : if (v.isString()) {
126 125 : MOZ_ASSERT(!IsInsideNursery(v.toString()));
127 125 : JSString** np = reinterpret_cast<JSString**>(p);
128 125 : if (preBarrier)
129 125 : JSString::writeBarrierPre(*np);
130 125 : *np = v.toString();
131 125 : return true;
132 : }
133 1 : return false;
134 :
135 : case JSVAL_TYPE_OBJECT:
136 413 : if (v.isObjectOrNull()) {
137 410 : JSObject** np = reinterpret_cast<JSObject**>(p);
138 :
139 : // Update property types when writing object properties. Types for
140 : // other properties were captured when the unboxed layout was
141 : // created.
142 410 : AddTypePropertyId(cx, unboxedObject, id, v);
143 :
144 : // As above, trigger post barriers on the whole object.
145 410 : JSObject* obj = v.toObjectOrNull();
146 410 : if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject))
147 134 : unboxedObject->zone()->group()->storeBuffer().putWholeCell(unboxedObject);
148 :
149 410 : if (preBarrier)
150 410 : JSObject::writeBarrierPre(*np);
151 410 : *np = obj;
152 410 : return true;
153 : }
154 3 : return false;
155 :
156 : default:
157 0 : MOZ_CRASH("Invalid type for unboxed value");
158 : }
159 : }
160 :
161 : /////////////////////////////////////////////////////////////////////
162 : // UnboxedPlainObject
163 : /////////////////////////////////////////////////////////////////////
164 :
165 : inline const UnboxedLayout&
166 14341 : UnboxedPlainObject::layout() const
167 : {
168 14341 : return group()->unboxedLayout();
169 : }
170 :
171 : /////////////////////////////////////////////////////////////////////
172 : // UnboxedArrayObject
173 : /////////////////////////////////////////////////////////////////////
174 :
175 : inline const UnboxedLayout&
176 0 : UnboxedArrayObject::layout() const
177 : {
178 0 : return group()->unboxedLayout();
179 : }
180 :
181 : inline void
182 0 : UnboxedArrayObject::setLength(JSContext* cx, uint32_t length)
183 : {
184 0 : if (length > INT32_MAX) {
185 : // Track objects with overflowing lengths in type information.
186 0 : MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW);
187 : }
188 :
189 0 : length_ = length;
190 0 : }
191 :
192 : inline void
193 0 : UnboxedArrayObject::setInitializedLength(uint32_t initlen)
194 : {
195 0 : if (initlen < initializedLength()) {
196 0 : switch (elementType()) {
197 : case JSVAL_TYPE_STRING:
198 0 : for (size_t i = initlen; i < initializedLength(); i++)
199 0 : triggerPreBarrier<JSVAL_TYPE_STRING>(i);
200 0 : break;
201 : case JSVAL_TYPE_OBJECT:
202 0 : for (size_t i = initlen; i < initializedLength(); i++)
203 0 : triggerPreBarrier<JSVAL_TYPE_OBJECT>(i);
204 0 : break;
205 : default:
206 0 : MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(elementType()));
207 : }
208 : }
209 0 : setInitializedLengthNoBarrier(initlen);
210 0 : }
211 :
212 : template <JSValueType Type>
213 : inline bool
214 0 : UnboxedArrayObject::setElementSpecific(JSContext* cx, size_t index, const Value& v)
215 : {
216 0 : MOZ_ASSERT(index < initializedLength());
217 0 : MOZ_ASSERT(Type == elementType());
218 0 : uint8_t* p = elements() + index * UnboxedTypeSize(Type);
219 0 : return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
220 : }
221 :
222 : template <JSValueType Type>
223 : inline void
224 0 : UnboxedArrayObject::setElementNoTypeChangeSpecific(size_t index, const Value& v)
225 : {
226 0 : MOZ_ASSERT(index < initializedLength());
227 0 : MOZ_ASSERT(Type == elementType());
228 0 : uint8_t* p = elements() + index * UnboxedTypeSize(Type);
229 0 : return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ true);
230 : }
231 :
232 : template <JSValueType Type>
233 : inline bool
234 0 : UnboxedArrayObject::initElementSpecific(JSContext* cx, size_t index, const Value& v)
235 : {
236 0 : MOZ_ASSERT(index < initializedLength());
237 0 : MOZ_ASSERT(Type == elementType());
238 0 : uint8_t* p = elements() + index * UnboxedTypeSize(Type);
239 0 : return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
240 : }
241 :
242 : template <JSValueType Type>
243 : inline void
244 0 : UnboxedArrayObject::initElementNoTypeChangeSpecific(size_t index, const Value& v)
245 : {
246 0 : MOZ_ASSERT(index < initializedLength());
247 0 : MOZ_ASSERT(Type == elementType());
248 0 : uint8_t* p = elements() + index * UnboxedTypeSize(Type);
249 0 : return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
250 : }
251 :
252 : template <JSValueType Type>
253 : inline Value
254 0 : UnboxedArrayObject::getElementSpecific(size_t index)
255 : {
256 0 : MOZ_ASSERT(index < initializedLength());
257 0 : MOZ_ASSERT(Type == elementType());
258 0 : uint8_t* p = elements() + index * UnboxedTypeSize(Type);
259 0 : return GetUnboxedValue(p, Type, /* maybeUninitialized = */ false);
260 : }
261 :
262 : template <JSValueType Type>
263 : inline void
264 0 : UnboxedArrayObject::triggerPreBarrier(size_t index)
265 : {
266 0 : MOZ_ASSERT(UnboxedTypeNeedsPreBarrier(Type));
267 :
268 0 : uint8_t* p = elements() + index * UnboxedTypeSize(Type);
269 :
270 : switch (Type) {
271 : case JSVAL_TYPE_STRING: {
272 0 : JSString** np = reinterpret_cast<JSString**>(p);
273 0 : JSString::writeBarrierPre(*np);
274 0 : break;
275 : }
276 :
277 : case JSVAL_TYPE_OBJECT: {
278 0 : JSObject** np = reinterpret_cast<JSObject**>(p);
279 0 : JSObject::writeBarrierPre(*np);
280 0 : break;
281 : }
282 :
283 : default:
284 0 : MOZ_CRASH("Bad type");
285 : }
286 0 : }
287 :
288 : /////////////////////////////////////////////////////////////////////
289 : // Combined methods for NativeObject and UnboxedArrayObject accesses.
290 : /////////////////////////////////////////////////////////////////////
291 :
292 : static inline bool
293 3417 : HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
294 : {
295 3417 : return obj->isNative() || obj->is<UnboxedArrayObject>();
296 : }
297 :
298 : static inline size_t
299 6069 : GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
300 : {
301 6069 : if (obj->isNative())
302 6058 : return obj->as<NativeObject>().getDenseInitializedLength();
303 11 : if (obj->is<UnboxedArrayObject>())
304 0 : return obj->as<UnboxedArrayObject>().initializedLength();
305 11 : return 0;
306 : }
307 :
308 : static inline size_t
309 : GetAnyBoxedOrUnboxedCapacity(JSObject* obj)
310 : {
311 : if (obj->isNative())
312 : return obj->as<NativeObject>().getDenseCapacity();
313 : if (obj->is<UnboxedArrayObject>())
314 : return obj->as<UnboxedArrayObject>().capacity();
315 : return 0;
316 : }
317 :
318 : static inline Value
319 2711 : GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
320 : {
321 2711 : if (obj->isNative())
322 2711 : return obj->as<NativeObject>().getDenseElement(index);
323 0 : return obj->as<UnboxedArrayObject>().getElement(index);
324 : }
325 :
326 : static inline size_t
327 776 : GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
328 : {
329 776 : if (obj->is<ArrayObject>())
330 776 : return obj->as<ArrayObject>().length();
331 0 : return obj->as<UnboxedArrayObject>().length();
332 : }
333 :
334 : static inline void
335 4 : SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
336 : {
337 4 : if (obj->is<ArrayObject>()) {
338 4 : MOZ_ASSERT(length >= obj->as<ArrayObject>().length());
339 4 : obj->as<ArrayObject>().setLength(cx, length);
340 : } else {
341 0 : MOZ_ASSERT(length >= obj->as<UnboxedArrayObject>().length());
342 0 : obj->as<UnboxedArrayObject>().setLength(cx, length);
343 : }
344 4 : }
345 :
346 : static inline bool
347 3 : SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
348 : {
349 3 : if (obj->isNative()) {
350 3 : obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
351 3 : return true;
352 : }
353 0 : return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
354 : }
355 :
356 : static inline bool
357 : InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
358 : {
359 : if (obj->isNative()) {
360 : obj->as<NativeObject>().initDenseElementWithType(cx, index, value);
361 : return true;
362 : }
363 : return obj->as<UnboxedArrayObject>().initElement(cx, index, value);
364 : }
365 :
366 : /////////////////////////////////////////////////////////////////////
367 : // Template methods for NativeObject and UnboxedArrayObject accesses.
368 : /////////////////////////////////////////////////////////////////////
369 :
370 : static inline JSValueType
371 3417 : GetBoxedOrUnboxedType(JSObject* obj)
372 : {
373 3417 : if (obj->isNative())
374 3417 : return JSVAL_TYPE_MAGIC;
375 0 : return obj->as<UnboxedArrayObject>().elementType();
376 : }
377 :
378 : template <JSValueType Type>
379 : static inline bool
380 32 : HasBoxedOrUnboxedDenseElements(JSObject* obj)
381 : {
382 : if (Type == JSVAL_TYPE_MAGIC)
383 32 : return obj->isNative();
384 0 : return obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().elementType() == Type;
385 : }
386 :
387 : template <JSValueType Type>
388 : static inline size_t
389 459 : GetBoxedOrUnboxedInitializedLength(JSObject* obj)
390 : {
391 : if (Type == JSVAL_TYPE_MAGIC)
392 459 : return obj->as<NativeObject>().getDenseInitializedLength();
393 0 : return obj->as<UnboxedArrayObject>().initializedLength();
394 : }
395 :
396 : template <JSValueType Type>
397 : static inline DenseElementResult
398 26 : SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
399 : {
400 26 : size_t oldInitlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
401 : if (Type == JSVAL_TYPE_MAGIC) {
402 26 : obj->as<NativeObject>().setDenseInitializedLength(initlen);
403 26 : if (initlen < oldInitlen)
404 2 : obj->as<NativeObject>().shrinkElements(cx, initlen);
405 : } else {
406 0 : obj->as<UnboxedArrayObject>().setInitializedLength(initlen);
407 0 : if (initlen < oldInitlen)
408 0 : obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen);
409 : }
410 26 : return DenseElementResult::Success;
411 : }
412 :
413 : template <JSValueType Type>
414 : static inline size_t
415 6 : GetBoxedOrUnboxedCapacity(JSObject* obj)
416 : {
417 : if (Type == JSVAL_TYPE_MAGIC)
418 6 : return obj->as<NativeObject>().getDenseCapacity();
419 0 : return obj->as<UnboxedArrayObject>().capacity();
420 : }
421 :
422 : template <JSValueType Type>
423 : static inline Value
424 1939 : GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
425 : {
426 : if (Type == JSVAL_TYPE_MAGIC)
427 1939 : return obj->as<NativeObject>().getDenseElement(index);
428 0 : return obj->as<UnboxedArrayObject>().getElementSpecific<Type>(index);
429 : }
430 :
431 : template <JSValueType Type>
432 : static inline void
433 0 : SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value)
434 : {
435 : if (Type == JSVAL_TYPE_MAGIC)
436 0 : obj->as<NativeObject>().setDenseElement(index, value);
437 : else
438 0 : obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
439 0 : }
440 :
441 : template <JSValueType Type>
442 : static inline bool
443 : SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
444 : {
445 : if (Type == JSVAL_TYPE_MAGIC) {
446 : obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
447 : return true;
448 : }
449 : return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
450 : }
451 :
452 : template <JSValueType Type>
453 : static inline DenseElementResult
454 0 : EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
455 : {
456 : if (Type == JSVAL_TYPE_MAGIC) {
457 0 : if (!obj->as<ArrayObject>().ensureElements(cx, count))
458 0 : return DenseElementResult::Failure;
459 : } else {
460 0 : if (obj->as<UnboxedArrayObject>().capacity() < count) {
461 0 : if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
462 0 : return DenseElementResult::Failure;
463 : }
464 : }
465 0 : return DenseElementResult::Success;
466 : }
467 :
468 : template <JSValueType Type>
469 : static inline DenseElementResult
470 2944 : SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
471 : uint32_t start, const Value* vp, uint32_t count,
472 : ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
473 : {
474 : if (Type == JSVAL_TYPE_MAGIC) {
475 2944 : NativeObject* nobj = &obj->as<NativeObject>();
476 :
477 2944 : if (nobj->denseElementsAreFrozen())
478 0 : return DenseElementResult::Incomplete;
479 :
480 8832 : if (obj->is<ArrayObject>() &&
481 2944 : !obj->as<ArrayObject>().lengthIsWritable() &&
482 0 : start + count >= obj->as<ArrayObject>().length())
483 : {
484 0 : return DenseElementResult::Incomplete;
485 : }
486 :
487 2944 : DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
488 2944 : if (result != DenseElementResult::Success)
489 0 : return result;
490 :
491 2944 : if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
492 2943 : obj->as<ArrayObject>().setLengthInt32(start + count);
493 :
494 2944 : if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
495 876 : nobj->copyDenseElements(start, vp, count);
496 : } else {
497 4243 : for (size_t i = 0; i < count; i++)
498 2175 : nobj->setDenseElementWithType(cx, start + i, vp[i]);
499 : }
500 :
501 2944 : return DenseElementResult::Success;
502 : }
503 :
504 0 : UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
505 :
506 0 : if (start > nobj->initializedLength())
507 0 : return DenseElementResult::Incomplete;
508 :
509 0 : if (start + count >= UnboxedArrayObject::MaximumCapacity)
510 0 : return DenseElementResult::Incomplete;
511 :
512 0 : if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count))
513 0 : return DenseElementResult::Failure;
514 :
515 0 : size_t oldInitlen = nobj->initializedLength();
516 :
517 : // Overwrite any existing elements covered by the new range. If we fail
518 : // after this point due to some incompatible type being written to the
519 : // object's elements, afterwards the contents will be different from when
520 : // we started. The caller must retry the operation using a generic path,
521 : // which will overwrite the already-modified elements as well as the ones
522 : // that were left alone.
523 0 : size_t i = 0;
524 0 : if (updateTypes == ShouldUpdateTypes::DontUpdate) {
525 0 : for (size_t j = start; i < count && j < oldInitlen; i++, j++)
526 0 : nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]);
527 : } else {
528 0 : for (size_t j = start; i < count && j < oldInitlen; i++, j++) {
529 0 : if (!nobj->setElementSpecific<Type>(cx, j, vp[i]))
530 0 : return DenseElementResult::Incomplete;
531 : }
532 : }
533 :
534 0 : if (i != count) {
535 0 : obj->as<UnboxedArrayObject>().setInitializedLength(start + count);
536 0 : if (updateTypes == ShouldUpdateTypes::DontUpdate) {
537 0 : for (; i < count; i++)
538 0 : nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]);
539 : } else {
540 0 : for (; i < count; i++) {
541 0 : if (!nobj->initElementSpecific<Type>(cx, start + i, vp[i])) {
542 0 : nobj->setInitializedLengthNoBarrier(oldInitlen);
543 0 : return DenseElementResult::Incomplete;
544 : }
545 : }
546 : }
547 : }
548 :
549 0 : if (start + count >= nobj->length())
550 0 : nobj->setLength(cx, start + count);
551 :
552 0 : return DenseElementResult::Success;
553 : }
554 :
555 : template <JSValueType Type>
556 : static inline DenseElementResult
557 20 : MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart,
558 : uint32_t length)
559 : {
560 20 : MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
561 :
562 : if (Type == JSVAL_TYPE_MAGIC) {
563 20 : if (obj->as<NativeObject>().denseElementsAreFrozen())
564 0 : return DenseElementResult::Incomplete;
565 :
566 20 : if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
567 0 : return DenseElementResult::Failure;
568 20 : obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
569 : } else {
570 0 : uint8_t* data = obj->as<UnboxedArrayObject>().elements();
571 0 : size_t elementSize = UnboxedTypeSize(Type);
572 :
573 0 : if (UnboxedTypeNeedsPreBarrier(Type) &&
574 0 : JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier())
575 : {
576 : // Trigger pre barriers on any elements we are overwriting. See
577 : // NativeObject::moveDenseElements. No post barrier is needed as
578 : // only whole cell post barriers are used with unboxed objects.
579 0 : for (size_t i = 0; i < length; i++)
580 0 : obj->as<UnboxedArrayObject>().triggerPreBarrier<Type>(dstStart + i);
581 : }
582 :
583 0 : memmove(data + dstStart * elementSize,
584 0 : data + srcStart * elementSize,
585 : length * elementSize);
586 : }
587 :
588 20 : return DenseElementResult::Success;
589 : }
590 :
591 : template <JSValueType DstType, JSValueType SrcType>
592 : static inline DenseElementResult
593 6 : CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
594 : uint32_t dstStart, uint32_t srcStart, uint32_t length)
595 : {
596 6 : MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
597 6 : MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
598 6 : MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
599 6 : MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<SrcType>(src) >= srcStart + length);
600 6 : MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length);
601 :
602 6 : SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length);
603 :
604 : if (DstType == JSVAL_TYPE_MAGIC) {
605 : if (SrcType == JSVAL_TYPE_MAGIC) {
606 6 : const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
607 6 : dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
608 : } else {
609 0 : for (size_t i = 0; i < length; i++) {
610 0 : Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
611 0 : dst->as<NativeObject>().initDenseElement(dstStart + i, v);
612 : }
613 : }
614 : } else if (DstType == SrcType) {
615 0 : uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
616 0 : uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
617 0 : size_t elementSize = UnboxedTypeSize(DstType);
618 :
619 0 : memcpy(dstData + dstStart * elementSize,
620 0 : srcData + srcStart * elementSize,
621 : length * elementSize);
622 :
623 : // Add a store buffer entry if we might have copied a nursery pointer to dst.
624 0 : if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
625 0 : dst->zone()->group()->storeBuffer().putWholeCell(dst);
626 : } else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
627 0 : uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
628 0 : uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
629 :
630 0 : for (size_t i = 0; i < length; i++) {
631 0 : int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t));
632 0 : *reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v;
633 : }
634 : } else {
635 0 : for (size_t i = 0; i < length; i++) {
636 0 : Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
637 0 : dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v);
638 : }
639 : }
640 :
641 6 : return DenseElementResult::Success;
642 : }
643 :
644 : /////////////////////////////////////////////////////////////////////
645 : // Dispatch to specialized methods based on the type of an object.
646 : /////////////////////////////////////////////////////////////////////
647 :
648 : // Goop to fix MSVC. See DispatchTraceKindTyped in TraceKind.h.
649 : // The clang-cl front end defines _MSC_VER, but still requires the explicit
650 : // template declaration, so we must test for __clang__ here as well.
651 : #if defined(_MSC_VER) && !defined(__clang__)
652 : # define DEPENDENT_TEMPLATE_HINT
653 : #else
654 : # define DEPENDENT_TEMPLATE_HINT template
655 : #endif
656 :
657 : // Function to dispatch a method specialized to whatever boxed or unboxed dense
658 : // elements which an input object has.
659 : template <typename F>
660 : DenseElementResult
661 3405 : CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
662 : {
663 3405 : if (!HasAnyBoxedOrUnboxedDenseElements(obj))
664 0 : return DenseElementResult::Incomplete;
665 3405 : switch (GetBoxedOrUnboxedType(obj)) {
666 : case JSVAL_TYPE_MAGIC:
667 3405 : return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
668 : case JSVAL_TYPE_BOOLEAN:
669 0 : return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
670 : case JSVAL_TYPE_INT32:
671 0 : return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
672 : case JSVAL_TYPE_DOUBLE:
673 0 : return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
674 : case JSVAL_TYPE_STRING:
675 0 : return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
676 : case JSVAL_TYPE_OBJECT:
677 0 : return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
678 : default:
679 0 : MOZ_CRASH();
680 : }
681 : }
682 :
683 : // As above, except the specialization can reflect the unboxed type of two objects.
684 : template <typename F>
685 : DenseElementResult
686 6 : CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
687 : {
688 6 : if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
689 0 : return DenseElementResult::Incomplete;
690 :
691 : #define SPECIALIZE_OBJ2(TYPE) \
692 : switch (GetBoxedOrUnboxedType(obj2)) { \
693 : case JSVAL_TYPE_MAGIC: \
694 : return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_MAGIC>(); \
695 : case JSVAL_TYPE_BOOLEAN: \
696 : return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_BOOLEAN>(); \
697 : case JSVAL_TYPE_INT32: \
698 : return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_INT32>(); \
699 : case JSVAL_TYPE_DOUBLE: \
700 : return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_DOUBLE>(); \
701 : case JSVAL_TYPE_STRING: \
702 : return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_STRING>(); \
703 : case JSVAL_TYPE_OBJECT: \
704 : return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_OBJECT>(); \
705 : default: \
706 : MOZ_CRASH(); \
707 : }
708 :
709 6 : switch (GetBoxedOrUnboxedType(obj1)) {
710 : case JSVAL_TYPE_MAGIC:
711 6 : SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC)
712 : case JSVAL_TYPE_BOOLEAN:
713 0 : SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN)
714 : case JSVAL_TYPE_INT32:
715 0 : SPECIALIZE_OBJ2(JSVAL_TYPE_INT32)
716 : case JSVAL_TYPE_DOUBLE:
717 0 : SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE)
718 : case JSVAL_TYPE_STRING:
719 0 : SPECIALIZE_OBJ2(JSVAL_TYPE_STRING)
720 : case JSVAL_TYPE_OBJECT:
721 0 : SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT)
722 : default:
723 0 : MOZ_CRASH();
724 : }
725 :
726 : #undef SPECIALIZE_OBJ2
727 : }
728 :
729 : #undef DEPENDENT_TEMPLATE_HINT
730 :
731 : #define DefineBoxedOrUnboxedFunctor1(Signature, A) \
732 : struct Signature ## Functor { \
733 : A a; \
734 : explicit Signature ## Functor(A a) \
735 : : a(a) \
736 : {} \
737 : template <JSValueType Type> \
738 : DenseElementResult operator()() { \
739 : return Signature<Type>(a); \
740 : } \
741 : }
742 :
743 : #define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \
744 : struct Signature ## Functor { \
745 : A a; B b; C c; \
746 : Signature ## Functor(A a, B b, C c) \
747 : : a(a), b(b), c(c) \
748 : {} \
749 : template <JSValueType Type> \
750 : DenseElementResult operator()() { \
751 : return Signature<Type>(a, b, c); \
752 : } \
753 : }
754 :
755 : #define DefineBoxedOrUnboxedFunctor4(Signature, A, B, C, D) \
756 : struct Signature ## Functor { \
757 : A a; B b; C c; D d; \
758 : Signature ## Functor(A a, B b, C c, D d) \
759 : : a(a), b(b), c(c), d(d) \
760 : {} \
761 : template <JSValueType Type> \
762 : DenseElementResult operator()() { \
763 : return Signature<Type>(a, b, c, d); \
764 : } \
765 : }
766 :
767 : #define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \
768 : struct Signature ## Functor { \
769 : A a; B b; C c; D d; \
770 : Signature ## Functor(A a, B b, C c, D d) \
771 : : a(a), b(b), c(c), d(d) \
772 : {} \
773 : template <JSValueType TypeOne, JSValueType TypeTwo> \
774 : DenseElementResult operator()() { \
775 : return Signature<TypeOne, TypeTwo>(a, b, c, d); \
776 : } \
777 : }
778 :
779 : #define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \
780 : struct Signature ## Functor { \
781 : A a; B b; C c; D d; E e; \
782 : Signature ## Functor(A a, B b, C c, D d, E e) \
783 : : a(a), b(b), c(c), d(d), e(e) \
784 : {} \
785 : template <JSValueType Type> \
786 : DenseElementResult operator()() { \
787 : return Signature<Type>(a, b, c, d, e); \
788 : } \
789 : }
790 :
791 : #define DefineBoxedOrUnboxedFunctor6(Signature, A, B, C, D, E, F) \
792 : struct Signature ## Functor { \
793 : A a; B b; C c; D d; E e; F f; \
794 : Signature ## Functor(A a, B b, C c, D d, E e, F f) \
795 : : a(a), b(b), c(c), d(d), e(e), f(f) \
796 : {} \
797 : template <JSValueType Type> \
798 : DenseElementResult operator()() { \
799 : return Signature<Type>(a, b, c, d, e, f); \
800 : } \
801 : }
802 :
803 : #define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \
804 : struct Signature ## Functor { \
805 : A a; B b; C c; D d; E e; F f; \
806 : Signature ## Functor(A a, B b, C c, D d, E e, F f) \
807 : : a(a), b(b), c(c), d(d), e(e), f(f) \
808 : {} \
809 : template <JSValueType TypeOne, JSValueType TypeTwo> \
810 : DenseElementResult operator()() { \
811 : return Signature<TypeOne, TypeTwo>(a, b, c, d, e, f); \
812 : } \
813 : }
814 :
815 : DenseElementResult
816 : SetOrExtendAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
817 : uint32_t start, const Value* vp, uint32_t count,
818 : ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
819 :
820 : DenseElementResult
821 : MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
822 : uint32_t dstStart, uint32_t srcStart, uint32_t length);
823 :
824 : DenseElementResult
825 : CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
826 : uint32_t dstStart, uint32_t srcStart, uint32_t length);
827 :
828 : void
829 : SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
830 :
831 : DenseElementResult
832 : EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
833 :
834 : } // namespace js
835 :
836 : #endif // vm_UnboxedObject_inl_h
|