/* -*- 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