summaryrefslogtreecommitdiffstats
path: root/js/src/vm/UnboxedObject-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/UnboxedObject-inl.h')
-rw-r--r--js/src/vm/UnboxedObject-inl.h840
1 files changed, 840 insertions, 0 deletions
diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h
new file mode 100644
index 000000000..93ad7bf28
--- /dev/null
+++ b/js/src/vm/UnboxedObject-inl.h
@@ -0,0 +1,840 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_UnboxedObject_inl_h
+#define vm_UnboxedObject_inl_h
+
+#include "vm/UnboxedObject.h"
+
+#include "gc/StoreBuffer-inl.h"
+#include "vm/ArrayObject-inl.h"
+#include "vm/NativeObject-inl.h"
+
+namespace js {
+
+static inline Value
+GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized)
+{
+ switch (type) {
+ case JSVAL_TYPE_BOOLEAN:
+ return BooleanValue(*p != 0);
+
+ case JSVAL_TYPE_INT32:
+ return Int32Value(*reinterpret_cast<int32_t*>(p));
+
+ case JSVAL_TYPE_DOUBLE: {
+ // During unboxed plain object creation, non-GC thing properties are
+ // left uninitialized. This is normally fine, since the properties will
+ // be filled in shortly, but if they are read before that happens we
+ // need to make sure that doubles are canonical.
+ double d = *reinterpret_cast<double*>(p);
+ if (maybeUninitialized)
+ return DoubleValue(JS::CanonicalizeNaN(d));
+ return DoubleValue(d);
+ }
+
+ case JSVAL_TYPE_STRING:
+ return StringValue(*reinterpret_cast<JSString**>(p));
+
+ case JSVAL_TYPE_OBJECT:
+ return ObjectOrNullValue(*reinterpret_cast<JSObject**>(p));
+
+ default:
+ MOZ_CRASH("Invalid type for unboxed value");
+ }
+}
+
+static inline void
+SetUnboxedValueNoTypeChange(JSObject* unboxedObject,
+ uint8_t* p, JSValueType type, const Value& v,
+ bool preBarrier)
+{
+ switch (type) {
+ case JSVAL_TYPE_BOOLEAN:
+ *p = v.toBoolean();
+ return;
+
+ case JSVAL_TYPE_INT32:
+ *reinterpret_cast<int32_t*>(p) = v.toInt32();
+ return;
+
+ case JSVAL_TYPE_DOUBLE:
+ *reinterpret_cast<double*>(p) = v.toNumber();
+ return;
+
+ case JSVAL_TYPE_STRING: {
+ MOZ_ASSERT(!IsInsideNursery(v.toString()));
+ JSString** np = reinterpret_cast<JSString**>(p);
+ if (preBarrier)
+ JSString::writeBarrierPre(*np);
+ *np = v.toString();
+ return;
+ }
+
+ case JSVAL_TYPE_OBJECT: {
+ JSObject** np = reinterpret_cast<JSObject**>(p);
+
+ // Manually trigger post barriers on the whole object. If we treat
+ // the pointer as a HeapPtrObject we will get confused later if the
+ // object is converted to its native representation.
+ JSObject* obj = v.toObjectOrNull();
+ if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject)) {
+ JSRuntime* rt = unboxedObject->runtimeFromMainThread();
+ rt->gc.storeBuffer.putWholeCell(unboxedObject);
+ }
+
+ if (preBarrier)
+ JSObject::writeBarrierPre(*np);
+ *np = obj;
+ return;
+ }
+
+ default:
+ MOZ_CRASH("Invalid type for unboxed value");
+ }
+}
+
+static inline bool
+SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id,
+ uint8_t* p, JSValueType type, const Value& v, bool preBarrier)
+{
+ switch (type) {
+ case JSVAL_TYPE_BOOLEAN:
+ if (v.isBoolean()) {
+ *p = v.toBoolean();
+ return true;
+ }
+ return false;
+
+ case JSVAL_TYPE_INT32:
+ if (v.isInt32()) {
+ *reinterpret_cast<int32_t*>(p) = v.toInt32();
+ return true;
+ }
+ return false;
+
+ case JSVAL_TYPE_DOUBLE:
+ if (v.isNumber()) {
+ *reinterpret_cast<double*>(p) = v.toNumber();
+ return true;
+ }
+ return false;
+
+ case JSVAL_TYPE_STRING:
+ if (v.isString()) {
+ MOZ_ASSERT(!IsInsideNursery(v.toString()));
+ JSString** np = reinterpret_cast<JSString**>(p);
+ if (preBarrier)
+ JSString::writeBarrierPre(*np);
+ *np = v.toString();
+ return true;
+ }
+ return false;
+
+ case JSVAL_TYPE_OBJECT:
+ if (v.isObjectOrNull()) {
+ JSObject** np = reinterpret_cast<JSObject**>(p);
+
+ // Update property types when writing object properties. Types for
+ // other properties were captured when the unboxed layout was
+ // created.
+ AddTypePropertyId(cx, unboxedObject, id, v);
+
+ // As above, trigger post barriers on the whole object.
+ JSObject* obj = v.toObjectOrNull();
+ if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) {
+ JSRuntime* rt = unboxedObject->runtimeFromMainThread();
+ rt->gc.storeBuffer.putWholeCell(unboxedObject);
+ }
+
+ if (preBarrier)
+ JSObject::writeBarrierPre(*np);
+ *np = obj;
+ return true;
+ }
+ return false;
+
+ default:
+ MOZ_CRASH("Invalid type for unboxed value");
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+// UnboxedPlainObject
+/////////////////////////////////////////////////////////////////////
+
+inline const UnboxedLayout&
+UnboxedPlainObject::layout() const
+{
+ return group()->unboxedLayout();
+}
+
+/////////////////////////////////////////////////////////////////////
+// UnboxedArrayObject
+/////////////////////////////////////////////////////////////////////
+
+inline const UnboxedLayout&
+UnboxedArrayObject::layout() const
+{
+ return group()->unboxedLayout();
+}
+
+inline void
+UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length)
+{
+ if (length > INT32_MAX) {
+ // Track objects with overflowing lengths in type information.
+ MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW);
+ }
+
+ length_ = length;
+}
+
+inline void
+UnboxedArrayObject::setInitializedLength(uint32_t initlen)
+{
+ if (initlen < initializedLength()) {
+ switch (elementType()) {
+ case JSVAL_TYPE_STRING:
+ for (size_t i = initlen; i < initializedLength(); i++)
+ triggerPreBarrier<JSVAL_TYPE_STRING>(i);
+ break;
+ case JSVAL_TYPE_OBJECT:
+ for (size_t i = initlen; i < initializedLength(); i++)
+ triggerPreBarrier<JSVAL_TYPE_OBJECT>(i);
+ break;
+ default:
+ MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(elementType()));
+ }
+ }
+ setInitializedLengthNoBarrier(initlen);
+}
+
+template <JSValueType Type>
+inline bool
+UnboxedArrayObject::setElementSpecific(ExclusiveContext* cx, size_t index, const Value& v)
+{
+ MOZ_ASSERT(index < initializedLength());
+ MOZ_ASSERT(Type == elementType());
+ uint8_t* p = elements() + index * UnboxedTypeSize(Type);
+ return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
+}
+
+template <JSValueType Type>
+inline void
+UnboxedArrayObject::setElementNoTypeChangeSpecific(size_t index, const Value& v)
+{
+ MOZ_ASSERT(index < initializedLength());
+ MOZ_ASSERT(Type == elementType());
+ uint8_t* p = elements() + index * UnboxedTypeSize(Type);
+ return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ true);
+}
+
+template <JSValueType Type>
+inline bool
+UnboxedArrayObject::initElementSpecific(ExclusiveContext* cx, size_t index, const Value& v)
+{
+ MOZ_ASSERT(index < initializedLength());
+ MOZ_ASSERT(Type == elementType());
+ uint8_t* p = elements() + index * UnboxedTypeSize(Type);
+ return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
+}
+
+template <JSValueType Type>
+inline void
+UnboxedArrayObject::initElementNoTypeChangeSpecific(size_t index, const Value& v)
+{
+ MOZ_ASSERT(index < initializedLength());
+ MOZ_ASSERT(Type == elementType());
+ uint8_t* p = elements() + index * UnboxedTypeSize(Type);
+ return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
+}
+
+template <JSValueType Type>
+inline Value
+UnboxedArrayObject::getElementSpecific(size_t index)
+{
+ MOZ_ASSERT(index < initializedLength());
+ MOZ_ASSERT(Type == elementType());
+ uint8_t* p = elements() + index * UnboxedTypeSize(Type);
+ return GetUnboxedValue(p, Type, /* maybeUninitialized = */ false);
+}
+
+template <JSValueType Type>
+inline void
+UnboxedArrayObject::triggerPreBarrier(size_t index)
+{
+ MOZ_ASSERT(UnboxedTypeNeedsPreBarrier(Type));
+
+ uint8_t* p = elements() + index * UnboxedTypeSize(Type);
+
+ switch (Type) {
+ case JSVAL_TYPE_STRING: {
+ JSString** np = reinterpret_cast<JSString**>(p);
+ JSString::writeBarrierPre(*np);
+ break;
+ }
+
+ case JSVAL_TYPE_OBJECT: {
+ JSObject** np = reinterpret_cast<JSObject**>(p);
+ JSObject::writeBarrierPre(*np);
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Bad type");
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Combined methods for NativeObject and UnboxedArrayObject accesses.
+/////////////////////////////////////////////////////////////////////
+
+static inline bool
+HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
+{
+ return obj->isNative() || obj->is<UnboxedArrayObject>();
+}
+
+static inline size_t
+GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
+{
+ if (obj->isNative())
+ return obj->as<NativeObject>().getDenseInitializedLength();
+ if (obj->is<UnboxedArrayObject>())
+ return obj->as<UnboxedArrayObject>().initializedLength();
+ return 0;
+}
+
+static inline size_t
+GetAnyBoxedOrUnboxedCapacity(JSObject* obj)
+{
+ if (obj->isNative())
+ return obj->as<NativeObject>().getDenseCapacity();
+ if (obj->is<UnboxedArrayObject>())
+ return obj->as<UnboxedArrayObject>().capacity();
+ return 0;
+}
+
+static inline Value
+GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
+{
+ if (obj->isNative())
+ return obj->as<NativeObject>().getDenseElement(index);
+ return obj->as<UnboxedArrayObject>().getElement(index);
+}
+
+static inline size_t
+GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
+{
+ if (obj->is<ArrayObject>())
+ return obj->as<ArrayObject>().length();
+ return obj->as<UnboxedArrayObject>().length();
+}
+
+static inline void
+SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
+{
+ if (obj->is<ArrayObject>()) {
+ MOZ_ASSERT(length >= obj->as<ArrayObject>().length());
+ obj->as<ArrayObject>().setLength(cx, length);
+ } else {
+ MOZ_ASSERT(length >= obj->as<UnboxedArrayObject>().length());
+ obj->as<UnboxedArrayObject>().setLength(cx, length);
+ }
+}
+
+static inline bool
+SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
+{
+ if (obj->isNative()) {
+ obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
+ return true;
+ }
+ return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
+}
+
+static inline bool
+InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
+{
+ if (obj->isNative()) {
+ obj->as<NativeObject>().initDenseElementWithType(cx, index, value);
+ return true;
+ }
+ return obj->as<UnboxedArrayObject>().initElement(cx, index, value);
+}
+
+/////////////////////////////////////////////////////////////////////
+// Template methods for NativeObject and UnboxedArrayObject accesses.
+/////////////////////////////////////////////////////////////////////
+
+static inline JSValueType
+GetBoxedOrUnboxedType(JSObject* obj)
+{
+ if (obj->isNative())
+ return JSVAL_TYPE_MAGIC;
+ return obj->as<UnboxedArrayObject>().elementType();
+}
+
+template <JSValueType Type>
+static inline bool
+HasBoxedOrUnboxedDenseElements(JSObject* obj)
+{
+ if (Type == JSVAL_TYPE_MAGIC)
+ return obj->isNative();
+ return obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().elementType() == Type;
+}
+
+template <JSValueType Type>
+static inline size_t
+GetBoxedOrUnboxedInitializedLength(JSObject* obj)
+{
+ if (Type == JSVAL_TYPE_MAGIC)
+ return obj->as<NativeObject>().getDenseInitializedLength();
+ return obj->as<UnboxedArrayObject>().initializedLength();
+}
+
+template <JSValueType Type>
+static inline DenseElementResult
+SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
+{
+ size_t oldInitlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
+ if (Type == JSVAL_TYPE_MAGIC) {
+ obj->as<NativeObject>().setDenseInitializedLength(initlen);
+ if (initlen < oldInitlen)
+ obj->as<NativeObject>().shrinkElements(cx, initlen);
+ } else {
+ obj->as<UnboxedArrayObject>().setInitializedLength(initlen);
+ if (initlen < oldInitlen)
+ obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen);
+ }
+ return DenseElementResult::Success;
+}
+
+template <JSValueType Type>
+static inline size_t
+GetBoxedOrUnboxedCapacity(JSObject* obj)
+{
+ if (Type == JSVAL_TYPE_MAGIC)
+ return obj->as<NativeObject>().getDenseCapacity();
+ return obj->as<UnboxedArrayObject>().capacity();
+}
+
+template <JSValueType Type>
+static inline Value
+GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
+{
+ if (Type == JSVAL_TYPE_MAGIC)
+ return obj->as<NativeObject>().getDenseElement(index);
+ return obj->as<UnboxedArrayObject>().getElementSpecific<Type>(index);
+}
+
+template <JSValueType Type>
+static inline void
+SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value)
+{
+ if (Type == JSVAL_TYPE_MAGIC)
+ obj->as<NativeObject>().setDenseElement(index, value);
+ else
+ obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
+}
+
+template <JSValueType Type>
+static inline bool
+SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
+{
+ if (Type == JSVAL_TYPE_MAGIC) {
+ obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
+ return true;
+ }
+ return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
+}
+
+template <JSValueType Type>
+static inline DenseElementResult
+EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
+{
+ if (Type == JSVAL_TYPE_MAGIC) {
+ if (!obj->as<ArrayObject>().ensureElements(cx, count))
+ return DenseElementResult::Failure;
+ } else {
+ if (obj->as<UnboxedArrayObject>().capacity() < count) {
+ if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
+ return DenseElementResult::Failure;
+ }
+ }
+ return DenseElementResult::Success;
+}
+
+template <JSValueType Type>
+static inline DenseElementResult
+SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
+ uint32_t start, const Value* vp, uint32_t count,
+ ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
+{
+ if (Type == JSVAL_TYPE_MAGIC) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+
+ if (nobj->denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
+
+ if (obj->is<ArrayObject>() &&
+ !obj->as<ArrayObject>().lengthIsWritable() &&
+ start + count >= obj->as<ArrayObject>().length())
+ {
+ return DenseElementResult::Incomplete;
+ }
+
+ DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
+ if (result != DenseElementResult::Success)
+ return result;
+
+ if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
+ obj->as<ArrayObject>().setLengthInt32(start + count);
+
+ if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
+ nobj->copyDenseElements(start, vp, count);
+ } else {
+ for (size_t i = 0; i < count; i++)
+ nobj->setDenseElementWithType(cx, start + i, vp[i]);
+ }
+
+ return DenseElementResult::Success;
+ }
+
+ UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
+
+ if (start > nobj->initializedLength())
+ return DenseElementResult::Incomplete;
+
+ if (start + count >= UnboxedArrayObject::MaximumCapacity)
+ return DenseElementResult::Incomplete;
+
+ if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count))
+ return DenseElementResult::Failure;
+
+ size_t oldInitlen = nobj->initializedLength();
+
+ // Overwrite any existing elements covered by the new range. If we fail
+ // after this point due to some incompatible type being written to the
+ // object's elements, afterwards the contents will be different from when
+ // we started. The caller must retry the operation using a generic path,
+ // which will overwrite the already-modified elements as well as the ones
+ // that were left alone.
+ size_t i = 0;
+ if (updateTypes == ShouldUpdateTypes::DontUpdate) {
+ for (size_t j = start; i < count && j < oldInitlen; i++, j++)
+ nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]);
+ } else {
+ for (size_t j = start; i < count && j < oldInitlen; i++, j++) {
+ if (!nobj->setElementSpecific<Type>(cx, j, vp[i]))
+ return DenseElementResult::Incomplete;
+ }
+ }
+
+ if (i != count) {
+ obj->as<UnboxedArrayObject>().setInitializedLength(start + count);
+ if (updateTypes == ShouldUpdateTypes::DontUpdate) {
+ for (; i < count; i++)
+ nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]);
+ } else {
+ for (; i < count; i++) {
+ if (!nobj->initElementSpecific<Type>(cx, start + i, vp[i])) {
+ nobj->setInitializedLengthNoBarrier(oldInitlen);
+ return DenseElementResult::Incomplete;
+ }
+ }
+ }
+ }
+
+ if (start + count >= nobj->length())
+ nobj->setLength(cx, start + count);
+
+ return DenseElementResult::Success;
+}
+
+template <JSValueType Type>
+static inline DenseElementResult
+MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart,
+ uint32_t length)
+{
+ MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
+
+ if (Type == JSVAL_TYPE_MAGIC) {
+ if (obj->as<NativeObject>().denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
+
+ if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
+ return DenseElementResult::Failure;
+ obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
+ } else {
+ uint8_t* data = obj->as<UnboxedArrayObject>().elements();
+ size_t elementSize = UnboxedTypeSize(Type);
+
+ if (UnboxedTypeNeedsPreBarrier(Type) &&
+ JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier())
+ {
+ // Trigger pre barriers on any elements we are overwriting. See
+ // NativeObject::moveDenseElements. No post barrier is needed as
+ // only whole cell post barriers are used with unboxed objects.
+ for (size_t i = 0; i < length; i++)
+ obj->as<UnboxedArrayObject>().triggerPreBarrier<Type>(dstStart + i);
+ }
+
+ memmove(data + dstStart * elementSize,
+ data + srcStart * elementSize,
+ length * elementSize);
+ }
+
+ return DenseElementResult::Success;
+}
+
+template <JSValueType DstType, JSValueType SrcType>
+static inline DenseElementResult
+CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
+ uint32_t dstStart, uint32_t srcStart, uint32_t length)
+{
+ MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
+ MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
+ MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
+ MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<SrcType>(src) >= srcStart + length);
+ MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length);
+
+ SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length);
+
+ if (DstType == JSVAL_TYPE_MAGIC) {
+ if (SrcType == JSVAL_TYPE_MAGIC) {
+ const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
+ dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
+ } else {
+ for (size_t i = 0; i < length; i++) {
+ Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
+ dst->as<NativeObject>().initDenseElement(dstStart + i, v);
+ }
+ }
+ } else if (DstType == SrcType) {
+ uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
+ uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
+ size_t elementSize = UnboxedTypeSize(DstType);
+
+ memcpy(dstData + dstStart * elementSize,
+ srcData + srcStart * elementSize,
+ length * elementSize);
+
+ // Add a store buffer entry if we might have copied a nursery pointer to dst.
+ if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
+ dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCell(dst);
+ } else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
+ uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
+ uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
+
+ for (size_t i = 0; i < length; i++) {
+ int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t));
+ *reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v;
+ }
+ } else {
+ for (size_t i = 0; i < length; i++) {
+ Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
+ dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v);
+ }
+ }
+
+ return DenseElementResult::Success;
+}
+
+/////////////////////////////////////////////////////////////////////
+// Dispatch to specialized methods based on the type of an object.
+/////////////////////////////////////////////////////////////////////
+
+// Goop to fix MSVC. See DispatchTraceKindTyped in TraceKind.h.
+// The clang-cl front end defines _MSC_VER, but still requires the explicit
+// template declaration, so we must test for __clang__ here as well.
+#if defined(_MSC_VER) && !defined(__clang__)
+# define DEPENDENT_TEMPLATE_HINT
+#else
+# define DEPENDENT_TEMPLATE_HINT template
+#endif
+
+// Function to dispatch a method specialized to whatever boxed or unboxed dense
+// elements which an input object has.
+template <typename F>
+DenseElementResult
+CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
+{
+ if (!HasAnyBoxedOrUnboxedDenseElements(obj))
+ return DenseElementResult::Incomplete;
+ switch (GetBoxedOrUnboxedType(obj)) {
+ case JSVAL_TYPE_MAGIC:
+ return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
+ case JSVAL_TYPE_BOOLEAN:
+ return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
+ case JSVAL_TYPE_INT32:
+ return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
+ case JSVAL_TYPE_DOUBLE:
+ return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
+ case JSVAL_TYPE_STRING:
+ return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
+ case JSVAL_TYPE_OBJECT:
+ return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
+ default:
+ MOZ_CRASH();
+ }
+}
+
+// As above, except the specialization can reflect the unboxed type of two objects.
+template <typename F>
+DenseElementResult
+CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
+{
+ if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
+ return DenseElementResult::Incomplete;
+
+#define SPECIALIZE_OBJ2(TYPE) \
+ switch (GetBoxedOrUnboxedType(obj2)) { \
+ case JSVAL_TYPE_MAGIC: \
+ return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_MAGIC>(); \
+ case JSVAL_TYPE_BOOLEAN: \
+ return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_BOOLEAN>(); \
+ case JSVAL_TYPE_INT32: \
+ return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_INT32>(); \
+ case JSVAL_TYPE_DOUBLE: \
+ return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_DOUBLE>(); \
+ case JSVAL_TYPE_STRING: \
+ return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_STRING>(); \
+ case JSVAL_TYPE_OBJECT: \
+ return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_OBJECT>(); \
+ default: \
+ MOZ_CRASH(); \
+ }
+
+ switch (GetBoxedOrUnboxedType(obj1)) {
+ case JSVAL_TYPE_MAGIC:
+ SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC)
+ case JSVAL_TYPE_BOOLEAN:
+ SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN)
+ case JSVAL_TYPE_INT32:
+ SPECIALIZE_OBJ2(JSVAL_TYPE_INT32)
+ case JSVAL_TYPE_DOUBLE:
+ SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE)
+ case JSVAL_TYPE_STRING:
+ SPECIALIZE_OBJ2(JSVAL_TYPE_STRING)
+ case JSVAL_TYPE_OBJECT:
+ SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT)
+ default:
+ MOZ_CRASH();
+ }
+
+#undef SPECIALIZE_OBJ2
+}
+
+#undef DEPENDENT_TEMPLATE_HINT
+
+#define DefineBoxedOrUnboxedFunctor1(Signature, A) \
+struct Signature ## Functor { \
+ A a; \
+ explicit Signature ## Functor(A a) \
+ : a(a) \
+ {} \
+ template <JSValueType Type> \
+ DenseElementResult operator()() { \
+ return Signature<Type>(a); \
+ } \
+}
+
+#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \
+struct Signature ## Functor { \
+ A a; B b; C c; \
+ Signature ## Functor(A a, B b, C c) \
+ : a(a), b(b), c(c) \
+ {} \
+ template <JSValueType Type> \
+ DenseElementResult operator()() { \
+ return Signature<Type>(a, b, c); \
+ } \
+}
+
+#define DefineBoxedOrUnboxedFunctor4(Signature, A, B, C, D) \
+struct Signature ## Functor { \
+ A a; B b; C c; D d; \
+ Signature ## Functor(A a, B b, C c, D d) \
+ : a(a), b(b), c(c), d(d) \
+ {} \
+ template <JSValueType Type> \
+ DenseElementResult operator()() { \
+ return Signature<Type>(a, b, c, d); \
+ } \
+}
+
+#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \
+struct Signature ## Functor { \
+ A a; B b; C c; D d; \
+ Signature ## Functor(A a, B b, C c, D d) \
+ : a(a), b(b), c(c), d(d) \
+ {} \
+ template <JSValueType TypeOne, JSValueType TypeTwo> \
+ DenseElementResult operator()() { \
+ return Signature<TypeOne, TypeTwo>(a, b, c, d); \
+ } \
+}
+
+#define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \
+struct Signature ## Functor { \
+ A a; B b; C c; D d; E e; \
+ Signature ## Functor(A a, B b, C c, D d, E e) \
+ : a(a), b(b), c(c), d(d), e(e) \
+ {} \
+ template <JSValueType Type> \
+ DenseElementResult operator()() { \
+ return Signature<Type>(a, b, c, d, e); \
+ } \
+}
+
+#define DefineBoxedOrUnboxedFunctor6(Signature, A, B, C, D, E, F) \
+struct Signature ## Functor { \
+ A a; B b; C c; D d; E e; F f; \
+ Signature ## Functor(A a, B b, C c, D d, E e, F f) \
+ : a(a), b(b), c(c), d(d), e(e), f(f) \
+ {} \
+ template <JSValueType Type> \
+ DenseElementResult operator()() { \
+ return Signature<Type>(a, b, c, d, e, f); \
+ } \
+}
+
+#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \
+struct Signature ## Functor { \
+ A a; B b; C c; D d; E e; F f; \
+ Signature ## Functor(A a, B b, C c, D d, E e, F f) \
+ : a(a), b(b), c(c), d(d), e(e), f(f) \
+ {} \
+ template <JSValueType TypeOne, JSValueType TypeTwo> \
+ DenseElementResult operator()() { \
+ return Signature<TypeOne, TypeTwo>(a, b, c, d, e, f); \
+ } \
+}
+
+DenseElementResult
+SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
+ uint32_t start, const Value* vp, uint32_t count,
+ ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
+
+DenseElementResult
+MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
+ uint32_t dstStart, uint32_t srcStart, uint32_t length);
+
+DenseElementResult
+CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
+ uint32_t dstStart, uint32_t srcStart, uint32_t length);
+
+void
+SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
+
+DenseElementResult
+EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
+
+} // namespace js
+
+#endif // vm_UnboxedObject_inl_h