840 lines
31 KiB
C++
840 lines
31 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* 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
|