blob: a885cfb6b61f2b4e80eeb809edafa633f3751a57 [file] [log] [blame]
/* -*- 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 "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 (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>().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)) {
// Trigger pre barriers on any elements we are overwriting. See
// moveDenseElements::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