mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-25 20:29:33 +00:00
532 lines
18 KiB
C++
532 lines
18 KiB
C++
/* -*- 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/Runtime.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, AutoEnterAnalysis& enter, 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
|
|
|
|
namespace JS {
|
|
|
|
template <>
|
|
struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout>
|
|
{};
|
|
|
|
} /* namespace JS */
|
|
|
|
#endif /* vm_UnboxedObject_h */
|