blob: b08fcb5e1bd98dc68147f4d0a4c964b8d324020d [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_h
#define vm_UnboxedObject_h
#include "jsgc.h"
#include "jsobj.h"
#include "vm/TypeInference.h"
namespace js {
// Memory required for an unboxed value of a given type. Returns zero for types
// which can't be used for unboxed objects.
static inline size_t
UnboxedTypeSize(JSValueType type)
{
switch (type) {
case JSVAL_TYPE_BOOLEAN: return 1;
case JSVAL_TYPE_INT32: return 4;
case JSVAL_TYPE_DOUBLE: return 8;
case JSVAL_TYPE_STRING: return sizeof(void*);
case JSVAL_TYPE_OBJECT: return sizeof(void*);
default: return 0;
}
}
static inline bool
UnboxedTypeNeedsPreBarrier(JSValueType type)
{
return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
}
static inline bool
UnboxedTypeNeedsPostBarrier(JSValueType type)
{
return type == JSVAL_TYPE_OBJECT;
}
// Class tracking information specific to unboxed objects.
class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
{
public:
struct Property {
PropertyName* name;
uint32_t offset;
JSValueType type;
Property()
: name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC)
{}
};
typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
private:
// If objects in this group have ever been converted to native objects,
// these store the corresponding native group and initial shape for such
// objects. Type information for this object is reflected in nativeGroup.
HeapPtrObjectGroup nativeGroup_;
HeapPtrShape nativeShape_;
// Any script/pc which the associated group is created for.
HeapPtrScript allocationScript_;
jsbytecode* allocationPc_;
// If nativeGroup is set and this object originally had a TypeNewScript or
// was keyed to an allocation site, this points to the group which replaced
// this one. This link is only needed to keep the replacement group from
// being GC'ed. If it were GC'ed and a new one regenerated later, that new
// group might have a different allocation kind from this group.
HeapPtrObjectGroup replacementGroup_;
// The following members are only used for unboxed plain objects.
// All properties on objects with this layout, in enumeration order.
PropertyVector properties_;
// Byte size of the data for objects with this layout.
size_t size_;
// Any 'new' script information associated with this layout.
TypeNewScript* newScript_;
// List for use in tracing objects with this layout. This has the same
// structure as the trace list on a TypeDescr.
int32_t* traceList_;
// If this layout has been used to construct script or JSON constant
// objects, this code might be filled in to more quickly fill in objects
// from an array of values.
HeapPtrJitCode constructorCode_;
// The following members are only used for unboxed arrays.
// The type of array elements.
JSValueType elementType_;
public:
UnboxedLayout()
: nativeGroup_(nullptr), nativeShape_(nullptr),
allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr),
elementType_(JSVAL_TYPE_MAGIC)
{}
bool initProperties(const PropertyVector& properties, size_t size) {
size_ = size;
return properties_.appendAll(properties);
}
void initArray(JSValueType elementType) {
elementType_ = elementType;
}
~UnboxedLayout() {
if (newScript_)
newScript_->clear();
js_delete(newScript_);
js_free(traceList_);
nativeGroup_.init(nullptr);
nativeShape_.init(nullptr);
replacementGroup_.init(nullptr);
constructorCode_.init(nullptr);
}
bool isArray() const {
return elementType_ != JSVAL_TYPE_MAGIC;
}
void detachFromCompartment();
const PropertyVector& properties() const {
return properties_;
}
TypeNewScript* newScript() const {
return newScript_;
}
void setNewScript(TypeNewScript* newScript, bool writeBarrier = true);
JSScript* allocationScript() const {
return allocationScript_;
}
jsbytecode* allocationPc() const {
return allocationPc_;
}
void setAllocationSite(JSScript* script, jsbytecode* pc) {
allocationScript_ = script;
allocationPc_ = pc;
}
const int32_t* traceList() const {
return traceList_;
}
void setTraceList(int32_t* traceList) {
traceList_ = traceList;
}
const Property* lookup(JSAtom* atom) const {
for (size_t i = 0; i < properties_.length(); i++) {
if (properties_[i].name == atom)
return &properties_[i];
}
return nullptr;
}
const Property* lookup(jsid id) const {
if (JSID_IS_STRING(id))
return lookup(JSID_TO_ATOM(id));
return nullptr;
}
size_t size() const {
return size_;
}
ObjectGroup* nativeGroup() const {
return nativeGroup_;
}
Shape* nativeShape() const {
return nativeShape_;
}
jit::JitCode* constructorCode() const {
return constructorCode_;
}
void setConstructorCode(jit::JitCode* code) {
constructorCode_ = code;
}
JSValueType elementType() const {
return elementType_;
}
inline gc::AllocKind getAllocKind() const;
void trace(JSTracer* trc);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
static bool makeNativeGroup(JSContext* cx, ObjectGroup* group);
static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group);
};
// Class for expando objects holding extra properties given to an unboxed plain
// object. These objects behave identically to normal native plain objects, and
// have a separate Class to distinguish them for memory usage reporting.
class UnboxedExpandoObject : public NativeObject
{
public:
static const Class class_;
};
// Class for a plain object using an unboxed representation. The physical
// layout of these objects is identical to that of an InlineTypedObject, though
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
// how their properties are stored.
class UnboxedPlainObject : public JSObject
{
// Optional object which stores extra properties on this object. This is
// not automatically barriered to avoid problems if the object is converted
// to a native. See ensureExpando().
UnboxedExpandoObject* expando_;
// Start of the inline data, which immediately follows the group and extra properties.
uint8_t data_[1];
public:
static const Class class_;
static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
MutableHandleShape propp);
static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
Handle<JSPropertyDescriptor> desc,
ObjectOpResult& result);
static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
HandleId id, MutableHandleValue vp);
static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
HandleValue receiver, ObjectOpResult& result);
static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<JSPropertyDescriptor> desc);
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly);
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
inline const UnboxedLayout& layout() const;
const UnboxedLayout& layoutDontCheckGeneration() const {
return group()->unboxedLayoutDontCheckGeneration();
}
uint8_t* data() {
return &data_[0];
}
UnboxedExpandoObject* maybeExpando() const {
return expando_;
}
void initExpando() {
expando_ = nullptr;
}
// For use during GC.
JSObject** addressOfExpando() {
return reinterpret_cast<JSObject**>(&expando_);
}
bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const;
static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj);
bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v);
Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false);
static bool convertToNative(JSContext* cx, JSObject* obj);
static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group,
NewObjectKind newKind);
static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
NewObjectKind newKind, IdValuePair* properties);
void fillAfterConvert(ExclusiveContext* cx,
const AutoValueVector& values, size_t* valueCursor);
static void trace(JSTracer* trc, JSObject* object);
static size_t offsetOfExpando() {
return offsetof(UnboxedPlainObject, expando_);
}
static size_t offsetOfData() {
return offsetof(UnboxedPlainObject, data_[0]);
}
};
// Try to construct an UnboxedLayout for each of the preliminary objects,
// provided they all match the template shape. If successful, converts the
// preliminary objects and their group to the new unboxed representation.
bool
TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape,
ObjectGroup* group, PreliminaryObjectArray* objects);
inline gc::AllocKind
UnboxedLayout::getAllocKind() const
{
MOZ_ASSERT(size());
return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
}
// Class for an array object using an unboxed representation.
class UnboxedArrayObject : public JSObject
{
// Elements pointer for the object.
uint8_t* elements_;
// The nominal array length. This always fits in an int32_t.
uint32_t length_;
// Value indicating the allocated capacity and initialized length of the
// array. The top CapacityBits bits are an index into CapacityArray, which
// indicates the elements capacity. The low InitializedLengthBits store the
// initialized length of the array.
uint32_t capacityIndexAndInitializedLength_;
// If the elements are inline, they will point here.
uint8_t inlineElements_[1];
public:
static const uint32_t CapacityBits = 6;
static const uint32_t CapacityShift = 26;
static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift;
static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1;
static const uint32_t MaximumCapacity = InitializedLengthMask;
static const uint32_t MinimumDynamicCapacity = 8;
static const uint32_t CapacityArray[];
// Capacity index which indicates the array's length is also its capacity.
static const uint32_t CapacityMatchesLengthIndex = 0;
private:
static inline uint32_t computeCapacity(uint32_t index, uint32_t length) {
if (index == CapacityMatchesLengthIndex)
return length;
return CapacityArray[index];
}
static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
static uint32_t exactCapacityIndex(uint32_t capacity);
public:
static const Class class_;
static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
MutableHandleShape propp);
static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
Handle<JSPropertyDescriptor> desc,
ObjectOpResult& result);
static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
HandleId id, MutableHandleValue vp);
static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
HandleValue receiver, ObjectOpResult& result);
static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<JSPropertyDescriptor> desc);
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly);
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
inline const UnboxedLayout& layout() const;
const UnboxedLayout& layoutDontCheckGeneration() const {
return group()->unboxedLayoutDontCheckGeneration();
}
JSValueType elementType() const {
return layoutDontCheckGeneration().elementType();
}
uint32_t elementSize() const {
return UnboxedTypeSize(elementType());
}
static bool convertToNative(JSContext* cx, JSObject* obj);
static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group,
uint32_t length, NewObjectKind newKind,
uint32_t maxLength = MaximumCapacity);
static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
ObjectGroup* group, Shape* shape);
bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group);
void fillAfterConvert(ExclusiveContext* cx,
const AutoValueVector& values, size_t* valueCursor);
static void trace(JSTracer* trc, JSObject* object);
static void objectMoved(JSObject* obj, const JSObject* old);
static void finalize(FreeOp* fop, JSObject* obj);
static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
gc::AllocKind allocKind);
uint8_t* elements() {
return elements_;
}
bool hasInlineElements() const {
return elements_ == &inlineElements_[0];
}
uint32_t length() const {
return length_;
}
uint32_t initializedLength() const {
return capacityIndexAndInitializedLength_ & InitializedLengthMask;
}
uint32_t capacityIndex() const {
return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift;
}
uint32_t capacity() const {
return computeCapacity(capacityIndex(), length());
}
bool containsProperty(ExclusiveContext* cx, jsid id);
bool setElement(ExclusiveContext* cx, size_t index, const Value& v);
bool initElement(ExclusiveContext* cx, size_t index, const Value& v);
void initElementNoTypeChange(size_t index, const Value& v);
Value getElement(size_t index);
template <JSValueType Type> inline bool setElementSpecific(ExclusiveContext* cx, size_t index,
const Value& v);
template <JSValueType Type> inline void setElementNoTypeChangeSpecific(size_t index, const Value& v);
template <JSValueType Type> inline bool initElementSpecific(ExclusiveContext* cx, size_t index,
const Value& v);
template <JSValueType Type> inline void initElementNoTypeChangeSpecific(size_t index, const Value& v);
template <JSValueType Type> inline Value getElementSpecific(size_t index);
template <JSValueType Type> inline void triggerPreBarrier(size_t index);
bool growElements(ExclusiveContext* cx, size_t cap);
void shrinkElements(ExclusiveContext* cx, size_t cap);
static uint32_t offsetOfElements() {
return offsetof(UnboxedArrayObject, elements_);
}
static uint32_t offsetOfLength() {
return offsetof(UnboxedArrayObject, length_);
}
static uint32_t offsetOfCapacityIndexAndInitializedLength() {
return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_);
}
static uint32_t offsetOfInlineElements() {
return offsetof(UnboxedArrayObject, inlineElements_);
}
void setLengthInt32(uint32_t length) {
MOZ_ASSERT(length <= INT32_MAX);
length_ = length;
}
inline void setLength(ExclusiveContext* cx, uint32_t len);
inline void setInitializedLength(uint32_t initlen);
inline void setInitializedLengthNoBarrier(uint32_t initlen) {
MOZ_ASSERT(initlen <= InitializedLengthMask);
capacityIndexAndInitializedLength_ =
(capacityIndexAndInitializedLength_ & CapacityMask) | initlen;
}
private:
void setInlineElements() {
elements_ = &inlineElements_[0];
}
void setCapacityIndex(uint32_t index) {
MOZ_ASSERT(index <= (CapacityMask >> CapacityShift));
capacityIndexAndInitializedLength_ =
(index << CapacityShift) | initializedLength();
}
};
} // namespace js
#endif /* vm_UnboxedObject_h */