mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1083600 - Use inline data for small transparent typed objects, r=sfink,nmatsakis.
This commit is contained in:
parent
f34ccc4039
commit
ca1120a47e
@ -539,6 +539,7 @@ struct CompartmentStats
|
||||
macro(Other, NotLiveGCThing, compartmentObject) \
|
||||
macro(Other, NotLiveGCThing, compartmentTables) \
|
||||
macro(Other, NotLiveGCThing, innerViewsTable) \
|
||||
macro(Other, NotLiveGCThing, lazyArrayBuffersTable) \
|
||||
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
|
||||
macro(Other, NotLiveGCThing, regexpCompartment) \
|
||||
macro(Other, NotLiveGCThing, savedStacksSet)
|
||||
|
@ -87,7 +87,7 @@ template<typename Elem>
|
||||
static Elem
|
||||
TypedObjectMemory(HandleValue v)
|
||||
{
|
||||
OutlineTypedObject &obj = v.toObject().as<OutlineTypedObject>();
|
||||
TypedObject &obj = v.toObject().as<TypedObject>();
|
||||
return reinterpret_cast<Elem>(obj.typedMem());
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ static bool SignMask(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
OutlineTypedObject &typedObj = args.thisv().toObject().as<OutlineTypedObject>();
|
||||
TypedObject &typedObj = args.thisv().toObject().as<TypedObject>();
|
||||
TypeDescr &descr = typedObj.typeDescr();
|
||||
if (descr.kind() != type::Simd || descr.as<SimdTypeDescr>().type() != SimdType::type) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
@ -322,7 +322,7 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<TypedObject*> result(cx, OutlineTypedObject::createZeroed(cx, descr, 0));
|
||||
Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0));
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
@ -450,7 +450,7 @@ js::CreateSimd(JSContext *cx, typename V::Elem *data)
|
||||
Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
|
||||
MOZ_ASSERT(typeDescr);
|
||||
|
||||
Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0));
|
||||
Rooted<TypedObject *> result(cx, TypedObject::createZeroed(cx, typeDescr, 0));
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1460,7 +1460,7 @@ js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
|
||||
int32_t
|
||||
TypedObject::offset() const
|
||||
{
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
if (is<InlineTypedObject>())
|
||||
return 0;
|
||||
return typedMem() - typedMemBase();
|
||||
}
|
||||
@ -1478,8 +1478,8 @@ TypedObject::typedMem() const
|
||||
{
|
||||
MOZ_ASSERT(isAttached());
|
||||
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return as<InlineOpaqueTypedObject>().inlineTypedMem();
|
||||
if (is<InlineTypedObject>())
|
||||
return as<InlineTypedObject>().inlineTypedMem();
|
||||
return as<OutlineTypedObject>().outOfLineTypedMem();
|
||||
}
|
||||
|
||||
@ -1492,12 +1492,22 @@ TypedObject::typedMemBase() const
|
||||
JSObject &owner = as<OutlineTypedObject>().owner();
|
||||
if (owner.is<ArrayBufferObject>())
|
||||
return owner.as<ArrayBufferObject>().dataPointer();
|
||||
return owner.as<InlineOpaqueTypedObject>().inlineTypedMem();
|
||||
return owner.as<InlineTypedObject>().inlineTypedMem();
|
||||
}
|
||||
|
||||
bool
|
||||
TypedObject::isAttached() const
|
||||
{
|
||||
if (is<InlineTransparentTypedObject>()) {
|
||||
LazyArrayBufferTable *table = compartment()->lazyArrayBuffers;
|
||||
if (table) {
|
||||
ArrayBufferObject *buffer =
|
||||
table->maybeBuffer(&const_cast<TypedObject *>(this)->as<InlineTransparentTypedObject>());
|
||||
if (buffer)
|
||||
return !buffer->isNeutered();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return true;
|
||||
if (!as<OutlineTypedObject>().outOfLineTypedMem())
|
||||
@ -1511,7 +1521,7 @@ TypedObject::isAttached() const
|
||||
bool
|
||||
TypedObject::maybeForwardedIsAttached() const
|
||||
{
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
if (is<InlineTypedObject>())
|
||||
return true;
|
||||
if (!as<OutlineTypedObject>().outOfLineTypedMem())
|
||||
return false;
|
||||
@ -1525,7 +1535,15 @@ TypedObject::maybeForwardedIsAttached() const
|
||||
TypedObject::GetBuffer(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setObject(args[0].toObject().as<TransparentTypedObject>().owner());
|
||||
JSObject &obj = args[0].toObject();
|
||||
ArrayBufferObject *buffer;
|
||||
if (obj.is<OutlineTransparentTypedObject>())
|
||||
buffer = obj.as<OutlineTransparentTypedObject>().getOrCreateBuffer(cx);
|
||||
else
|
||||
buffer = obj.as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
|
||||
if (!buffer)
|
||||
MOZ_CRASH();
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1549,7 +1567,7 @@ OutlineTypedObject::createUnattached(JSContext *cx,
|
||||
if (descr->opaque())
|
||||
return createUnattachedWithClass(cx, &OutlineOpaqueTypedObject::class_, descr, length);
|
||||
else
|
||||
return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length);
|
||||
return createUnattachedWithClass(cx, &OutlineTransparentTypedObject::class_, descr, length);
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
@ -1590,7 +1608,7 @@ OutlineTypedObject::createUnattachedWithClass(JSContext *cx,
|
||||
HandleTypeDescr type,
|
||||
int32_t length)
|
||||
{
|
||||
MOZ_ASSERT(clasp == &TransparentTypedObject::class_ ||
|
||||
MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ ||
|
||||
clasp == &OutlineOpaqueTypedObject::class_);
|
||||
|
||||
RootedObject proto(cx, PrototypeForTypeDescr(cx, type));
|
||||
@ -1618,6 +1636,9 @@ OutlineTypedObject::attach(JSContext *cx, ArrayBufferObject &buffer, int32_t off
|
||||
MOZ_ASSERT(offset >= 0);
|
||||
MOZ_ASSERT((size_t) (offset + size()) <= buffer.byteLength());
|
||||
|
||||
if (typeDescr().is<SizedTypeDescr>())
|
||||
buffer.setHasSizedObjectViews();
|
||||
|
||||
if (!buffer.addView(cx, this))
|
||||
CrashAtUnhandlableOOM("TypedObject::attach");
|
||||
|
||||
@ -1639,8 +1660,8 @@ OutlineTypedObject::attach(JSContext *cx, TypedObject &typedObj, int32_t offset)
|
||||
if (owner->is<ArrayBufferObject>()) {
|
||||
attach(cx, owner->as<ArrayBufferObject>(), offset);
|
||||
} else {
|
||||
MOZ_ASSERT(owner->is<InlineOpaqueTypedObject>());
|
||||
setOwnerAndData(owner, owner->as<InlineOpaqueTypedObject>().inlineTypedMem() + offset);
|
||||
MOZ_ASSERT(owner->is<InlineTypedObject>());
|
||||
setOwnerAndData(owner, owner->as<InlineTypedObject>().inlineTypedMem() + offset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1674,9 +1695,9 @@ OutlineTypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
|
||||
|
||||
int32_t length = TypedObjLengthFromType(*type);
|
||||
|
||||
const js::Class *clasp = typedObj->is<TransparentTypedObject>()
|
||||
? &TransparentTypedObject::class_
|
||||
: &OutlineOpaqueTypedObject::class_;
|
||||
const js::Class *clasp = typedObj->opaque()
|
||||
? &OutlineOpaqueTypedObject::class_
|
||||
: &OutlineTransparentTypedObject::class_;
|
||||
Rooted<OutlineTypedObject*> obj(cx);
|
||||
obj = createUnattachedWithClass(cx, clasp, type, length);
|
||||
if (!obj)
|
||||
@ -1690,11 +1711,10 @@ OutlineTypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
|
||||
TypedObject::createZeroed(JSContext *cx, HandleTypeDescr descr, int32_t length)
|
||||
{
|
||||
// If possible, create an object with inline data.
|
||||
if (descr->opaque() &&
|
||||
descr->is<SizedTypeDescr>() &&
|
||||
(size_t) descr->as<SizedTypeDescr>().size() <= InlineOpaqueTypedObject::MaximumSize)
|
||||
if (descr->is<SizedTypeDescr>() &&
|
||||
(size_t) descr->as<SizedTypeDescr>().size() <= InlineTypedObject::MaximumSize)
|
||||
{
|
||||
InlineOpaqueTypedObject *obj = InlineOpaqueTypedObject::create(cx, descr);
|
||||
InlineTypedObject *obj = InlineTypedObject::create(cx, descr);
|
||||
descr->as<SizedTypeDescr>().initInstances(cx->runtime(), obj->inlineTypedMem(), 1);
|
||||
return obj;
|
||||
}
|
||||
@ -1791,7 +1811,7 @@ OutlineTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
// Update the data pointer if the owner moved and the owner's data is
|
||||
// inline with it.
|
||||
if (owner != oldOwner &&
|
||||
(owner->is<InlineOpaqueTypedObject>() ||
|
||||
(owner->is<InlineTypedObject>() ||
|
||||
owner->as<ArrayBufferObject>().hasInlineData()))
|
||||
{
|
||||
mem += reinterpret_cast<uint8_t *>(owner) - reinterpret_cast<uint8_t *>(oldOwner);
|
||||
@ -2353,8 +2373,8 @@ OutlineTypedObject::neuter(void *newData)
|
||||
* Inline typed objects
|
||||
*/
|
||||
|
||||
/* static */ InlineOpaqueTypedObject *
|
||||
InlineOpaqueTypedObject::create(JSContext *cx, HandleTypeDescr descr)
|
||||
/* static */ InlineTypedObject *
|
||||
InlineTypedObject::create(JSContext *cx, HandleTypeDescr descr)
|
||||
{
|
||||
gc::AllocKind allocKind = allocKindForTypeDescriptor(descr);
|
||||
|
||||
@ -2362,24 +2382,21 @@ InlineOpaqueTypedObject::create(JSContext *cx, HandleTypeDescr descr)
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
RootedObject obj(cx, NewObjectWithClassProto(cx, &class_, proto, nullptr, allocKind));
|
||||
const Class *clasp = descr->opaque()
|
||||
? &InlineOpaqueTypedObject::class_
|
||||
: &InlineTransparentTypedObject::class_;
|
||||
|
||||
RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, proto, nullptr, allocKind));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
return &obj->as<InlineOpaqueTypedObject>();
|
||||
}
|
||||
|
||||
/* static */
|
||||
size_t
|
||||
InlineOpaqueTypedObject::offsetOfDataStart()
|
||||
{
|
||||
return NativeObject::getFixedSlotOffset(0);
|
||||
return &obj->as<InlineTypedObject>();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
InlineOpaqueTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
InlineTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
{
|
||||
InlineOpaqueTypedObject &typedObj = object->as<InlineOpaqueTypedObject>();
|
||||
InlineTypedObject &typedObj = object->as<InlineTypedObject>();
|
||||
|
||||
// When this is called for compacting GC, the related objects we touch here
|
||||
// may not have had their slots updated yet.
|
||||
@ -2388,6 +2405,119 @@ InlineOpaqueTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
descr.as<SizedTypeDescr>().traceInstances(trc, typedObj.inlineTypedMem(), 1);
|
||||
}
|
||||
|
||||
ArrayBufferObject *
|
||||
InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
|
||||
{
|
||||
LazyArrayBufferTable *&table = cx->compartment()->lazyArrayBuffers;
|
||||
if (!table) {
|
||||
table = cx->new_<LazyArrayBufferTable>(cx);
|
||||
if (!table)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArrayBufferObject *buffer = table->maybeBuffer(this);
|
||||
if (buffer)
|
||||
return buffer;
|
||||
|
||||
ArrayBufferObject::BufferContents contents =
|
||||
ArrayBufferObject::BufferContents::createPlain(inlineTypedMem());
|
||||
size_t nbytes = typeDescr().as<SizedTypeDescr>().size();
|
||||
|
||||
// Prevent GC under ArrayBufferObject::create, which might move this object
|
||||
// and its contents.
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
|
||||
buffer = ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
|
||||
if (!buffer)
|
||||
return nullptr;
|
||||
|
||||
// The owning object must always be the array buffer's first view. This
|
||||
// both prevents the memory from disappearing out from under the buffer
|
||||
// (the first view is held strongly by the buffer) and is used by the
|
||||
// buffer marking code to detect whether its data pointer needs to be
|
||||
// relocated.
|
||||
JS_ALWAYS_TRUE(buffer->addView(cx, this));
|
||||
|
||||
buffer->setForInlineTypedObject();
|
||||
buffer->setHasSizedObjectViews();
|
||||
|
||||
if (!table->addBuffer(cx, this, buffer))
|
||||
return nullptr;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ArrayBufferObject *
|
||||
OutlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
|
||||
{
|
||||
if (owner().is<ArrayBufferObject>())
|
||||
return &owner().as<ArrayBufferObject>();
|
||||
return owner().as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
|
||||
}
|
||||
|
||||
LazyArrayBufferTable::LazyArrayBufferTable(JSContext *cx)
|
||||
: map(cx)
|
||||
{
|
||||
if (!map.init())
|
||||
CrashAtUnhandlableOOM("LazyArrayBufferTable");
|
||||
}
|
||||
|
||||
LazyArrayBufferTable::~LazyArrayBufferTable()
|
||||
{
|
||||
WeakMapBase::removeWeakMapFromList(&map);
|
||||
}
|
||||
|
||||
ArrayBufferObject *
|
||||
LazyArrayBufferTable::maybeBuffer(InlineTransparentTypedObject *obj)
|
||||
{
|
||||
if (Map::Ptr p = map.lookup(obj))
|
||||
return &p->value()->as<ArrayBufferObject>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
LazyArrayBufferTable::addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer)
|
||||
{
|
||||
MOZ_ASSERT(!map.has(obj));
|
||||
if (!map.put(obj, buffer)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
MOZ_ASSERT(!IsInsideNursery(buffer));
|
||||
if (IsInsideNursery(obj)) {
|
||||
// Strip the barriers from the type before inserting into the store
|
||||
// buffer, as is done for DebugScopes::proxiedScopes.
|
||||
Map::Base *baseHashMap = static_cast<Map::Base *>(&map);
|
||||
|
||||
typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
|
||||
UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
|
||||
|
||||
typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
|
||||
cx->runtime()->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, obj));
|
||||
|
||||
// Also make sure the buffer is traced, so that its data pointer is
|
||||
// updated after the typed object moves.
|
||||
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
LazyArrayBufferTable::trace(JSTracer *trc)
|
||||
{
|
||||
map.trace(trc);
|
||||
}
|
||||
|
||||
size_t
|
||||
LazyArrayBufferTable::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
return mallocSizeOf(this) + map.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Typed object classes
|
||||
*/
|
||||
@ -2433,14 +2563,10 @@ InlineOpaqueTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(TransparentTypedObject,
|
||||
OutlineTypedObject::obj_trace);
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,
|
||||
OutlineTypedObject::obj_trace);
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,
|
||||
InlineOpaqueTypedObject::obj_trace);
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace);
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject, OutlineTypedObject::obj_trace);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject, InlineTypedObject::obj_trace);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject, InlineTypedObject::obj_trace);
|
||||
|
||||
static int32_t
|
||||
LengthForType(TypeDescr &descr)
|
||||
@ -2869,7 +2995,8 @@ js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
args.rval().setBoolean(!args[0].toObject().is<TransparentTypedObject>());
|
||||
JSObject &obj = args[0].toObject();
|
||||
args.rval().setBoolean(obj.is<TypedObject>() && obj.as<TypedObject>().opaque());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2882,7 +3009,8 @@ js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
args.rval().setBoolean(args[0].toObject().is<TransparentTypedObject>());
|
||||
JSObject &obj = args[0].toObject();
|
||||
args.rval().setBoolean(obj.is<TypedObject>() && !obj.as<TypedObject>().opaque());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define builtin_TypedObject_h
|
||||
|
||||
#include "jsobj.h"
|
||||
#include "jsweakmap.h"
|
||||
|
||||
#include "builtin/TypedObjectConstants.h"
|
||||
#include "vm/ArrayBufferObject.h"
|
||||
@ -62,19 +63,18 @@
|
||||
* Typed objects can be either transparent or opaque, depending on whether
|
||||
* their underlying buffer can be accessed. Transparent and opaque typed
|
||||
* objects have different classes, and can have different physical layouts.
|
||||
* The following layouts are currently possible:
|
||||
* The following layouts are possible:
|
||||
*
|
||||
* InlineOpaqueTypedObject: Typed objects whose data immediately follows the
|
||||
* object's header are inline typed objects. These do not have an associated
|
||||
* array buffer, so only opaque typed objects can be inline.
|
||||
* InlineTypedObject: Typed objects whose data immediately follows the object's
|
||||
* header are inline typed objects. The buffer for these objects is created
|
||||
* lazily and stored via the compartment's LazyArrayBufferTable, and points
|
||||
* back into the object's internal data.
|
||||
*
|
||||
* OutlineTypedObject: Transparent or opaque typed objects whose data is owned by
|
||||
* another object, which can be either an array buffer or an inline typed
|
||||
* object (opaque objects only). Outline typed objects may be attached or
|
||||
* unattached. An unattached typed object has no memory associated with it.
|
||||
* When first created, objects are always attached, but they can become
|
||||
* unattached if their buffer is neutered (note that this implies that typed
|
||||
* objects of opaque types can't be unattached).
|
||||
* OutlineTypedObject: Typed objects whose data is owned by another object,
|
||||
* which can be either an array buffer or an inline typed object. Outline
|
||||
* typed objects may be attached or unattached. An unattached typed object
|
||||
* has no data associated with it. When first created, objects are always
|
||||
* attached, but they can become unattached if their buffer is neutered.
|
||||
*
|
||||
* Note that whether a typed object is opaque is not directly
|
||||
* connected to its type. That is, opaque types are *always*
|
||||
@ -671,6 +671,8 @@ class TypedObject : public JSObject
|
||||
return typedMem() + offset;
|
||||
}
|
||||
|
||||
inline bool opaque() const;
|
||||
|
||||
// Creates a new typed object whose memory is freshly allocated and
|
||||
// initialized with zeroes (or, in the case of references, an appropriate
|
||||
// default value).
|
||||
@ -784,14 +786,16 @@ class OutlineTypedObject : public TypedObject
|
||||
static void obj_trace(JSTracer *trace, JSObject *object);
|
||||
};
|
||||
|
||||
// Class for a transparent typed object, whose owner is an array buffer.
|
||||
class TransparentTypedObject : public OutlineTypedObject
|
||||
// Class for a transparent typed object whose owner is an array buffer.
|
||||
class OutlineTransparentTypedObject : public OutlineTypedObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
ArrayBufferObject *getOrCreateBuffer(JSContext *cx);
|
||||
};
|
||||
|
||||
// Class for an opaque typed object, whose owner may be either an array buffer
|
||||
// Class for an opaque typed object whose owner may be either an array buffer
|
||||
// or an opaque inlined typed object.
|
||||
class OutlineOpaqueTypedObject : public OutlineTypedObject
|
||||
{
|
||||
@ -799,15 +803,13 @@ class OutlineOpaqueTypedObject : public OutlineTypedObject
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
// Class for an opaque typed object whose data is allocated inline.
|
||||
class InlineOpaqueTypedObject : public TypedObject
|
||||
// Class for a typed object whose data is allocated inline.
|
||||
class InlineTypedObject : public TypedObject
|
||||
{
|
||||
// Start of the inline data, which immediately follows the shape and type.
|
||||
uint8_t data_[1];
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static const size_t MaximumSize = NativeObject::MAX_FIXED_SLOTS * sizeof(Value);
|
||||
|
||||
static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
|
||||
@ -820,16 +822,35 @@ class InlineOpaqueTypedObject : public TypedObject
|
||||
}
|
||||
|
||||
uint8_t *inlineTypedMem() const {
|
||||
static_assert(offsetof(InlineOpaqueTypedObject, data_) == sizeof(JSObject),
|
||||
static_assert(offsetof(InlineTypedObject, data_) == sizeof(JSObject),
|
||||
"The data for an inline typed object must follow the shape and type.");
|
||||
return (uint8_t *) &data_;
|
||||
}
|
||||
|
||||
static void obj_trace(JSTracer *trace, JSObject *object);
|
||||
|
||||
static size_t offsetOfDataStart();
|
||||
static size_t offsetOfDataStart() {
|
||||
return offsetof(InlineTypedObject, data_);
|
||||
}
|
||||
|
||||
static InlineOpaqueTypedObject *create(JSContext *cx, HandleTypeDescr descr);
|
||||
static InlineTypedObject *create(JSContext *cx, HandleTypeDescr descr);
|
||||
};
|
||||
|
||||
// Class for a transparent typed object with inline data, which may have a
|
||||
// lazily allocated array buffer.
|
||||
class InlineTransparentTypedObject : public InlineTypedObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
ArrayBufferObject *getOrCreateBuffer(JSContext *cx);
|
||||
};
|
||||
|
||||
// Class for an opaque typed object with inline data and no array buffer.
|
||||
class InlineOpaqueTypedObject : public InlineTypedObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1044,7 +1065,8 @@ JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN)
|
||||
inline bool
|
||||
IsTypedObjectClass(const Class *class_)
|
||||
{
|
||||
return class_ == &TransparentTypedObject::class_ ||
|
||||
return class_ == &OutlineTransparentTypedObject::class_ ||
|
||||
class_ == &InlineTransparentTypedObject::class_ ||
|
||||
class_ == &OutlineOpaqueTypedObject::class_ ||
|
||||
class_ == &InlineOpaqueTypedObject::class_;
|
||||
}
|
||||
@ -1078,6 +1100,36 @@ IsTypeDescrClass(const Class* clasp)
|
||||
clasp == &UnsizedArrayTypeDescr::class_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
TypedObject::opaque() const
|
||||
{
|
||||
return is<OutlineOpaqueTypedObject>() || is<InlineOpaqueTypedObject>();
|
||||
}
|
||||
|
||||
// Inline transparent typed objects do not initially have an array buffer, but
|
||||
// can have that buffer created lazily if it is accessed later. This table
|
||||
// manages references from such typed objects to their buffers.
|
||||
class LazyArrayBufferTable
|
||||
{
|
||||
private:
|
||||
// The map from transparent typed objects to their lazily created buffer.
|
||||
// Keys in this map are InlineTransparentTypedObjects and values are
|
||||
// ArrayBufferObjects, but we don't enforce this in the type system due to
|
||||
// the extra marking code goop that requires.
|
||||
typedef WeakMap<PreBarrieredObject, RelocatablePtrObject> Map;
|
||||
Map map;
|
||||
|
||||
public:
|
||||
LazyArrayBufferTable(JSContext *cx);
|
||||
~LazyArrayBufferTable();
|
||||
|
||||
ArrayBufferObject *maybeBuffer(InlineTransparentTypedObject *obj);
|
||||
bool addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer);
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
JSObject *
|
||||
@ -1122,10 +1174,18 @@ template <>
|
||||
inline bool
|
||||
JSObject::is<js::OutlineTypedObject>() const
|
||||
{
|
||||
return getClass() == &js::TransparentTypedObject::class_ ||
|
||||
return getClass() == &js::OutlineTransparentTypedObject::class_ ||
|
||||
getClass() == &js::OutlineOpaqueTypedObject::class_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
JSObject::is<js::InlineTypedObject>() const
|
||||
{
|
||||
return getClass() == &js::InlineTransparentTypedObject::class_ ||
|
||||
getClass() == &js::InlineOpaqueTypedObject::class_;
|
||||
}
|
||||
|
||||
inline void
|
||||
js::TypedProto::initTypeDescrSlot(TypeDescr &descr)
|
||||
{
|
||||
|
@ -395,12 +395,13 @@ GetObjectAllocKindForCopy(const Nursery &nursery, JSObject *obj)
|
||||
|
||||
// Inlined opaque typed objects are followed by their data, so make sure we
|
||||
// copy it all over to the new object.
|
||||
if (obj->is<InlineOpaqueTypedObject>()) {
|
||||
if (obj->is<InlineTypedObject>()) {
|
||||
// Figure out the size of this object, from the prototype's TypeDescr.
|
||||
// The objects we are traversing here are all tenured, so we don't need
|
||||
// to check forwarding pointers.
|
||||
TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
|
||||
return InlineOpaqueTypedObject::allocKindForTypeDescriptor(descr);
|
||||
TypeDescr *descr = &obj->as<InlineTypedObject>().typeDescr();
|
||||
MOZ_ASSERT(!IsInsideNursery(descr));
|
||||
return InlineTypedObject::allocKindForTypeDescriptor(descr);
|
||||
}
|
||||
|
||||
// Outline typed objects have special requirements for their allocation kind.
|
||||
|
@ -536,6 +536,9 @@ js::gc::GCRuntime::markRuntime(JSTracer *trc,
|
||||
/* Mark debug scopes, if present */
|
||||
if (c->debugScopes)
|
||||
c->debugScopes->mark(trc);
|
||||
|
||||
if (c->lazyArrayBuffers)
|
||||
c->lazyArrayBuffers->trace(trc);
|
||||
}
|
||||
|
||||
MarkInterpreterActivations(&rt->mainThread, trc);
|
||||
|
35
js/src/jit-test/tests/TypedObject/inlinetransparent.js
Normal file
35
js/src/jit-test/tests/TypedObject/inlinetransparent.js
Normal file
@ -0,0 +1,35 @@
|
||||
if (!this.hasOwnProperty("TypedObject"))
|
||||
quit();
|
||||
|
||||
var TO = TypedObject;
|
||||
|
||||
var PointType = new TO.StructType({x: TO.int32, y: TO.int32});
|
||||
var LineType = new TO.StructType({from: PointType, to: PointType});
|
||||
|
||||
function testBasic(how) {
|
||||
var line = new LineType();
|
||||
var from = line.from;
|
||||
var to = line.to;
|
||||
TO.storage(to).buffer.expando = "hello";
|
||||
var dataview = new DataView(TO.storage(from).buffer);
|
||||
line.from.x = 12;
|
||||
line.to.x = 3;
|
||||
if (how == 1)
|
||||
minorgc();
|
||||
else if (how == 2)
|
||||
gc();
|
||||
assertEq(from.x, 12);
|
||||
assertEq(from.y, 0);
|
||||
assertEq(to.x, 3);
|
||||
assertEq(to.y, 0);
|
||||
assertEq(TO.storage(to).byteOffset, 8);
|
||||
dataview.setInt32(8, 10, true);
|
||||
assertEq(to.x, 10);
|
||||
assertEq(TO.storage(line).buffer.expando, "hello");
|
||||
}
|
||||
for (var i = 0; i < 5; i++)
|
||||
testBasic(0);
|
||||
for (var i = 0; i < 5; i++)
|
||||
testBasic(1);
|
||||
for (var i = 0; i < 5; i++)
|
||||
testBasic(2);
|
@ -5007,28 +5007,6 @@ CodeGenerator::visitTypedArrayElements(LTypedArrayElements *lir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
|
||||
Label inlineObject;
|
||||
masm.loadObjClass(obj, temp);
|
||||
masm.branchPtr(Assembler::Equal, temp, ImmPtr(&InlineOpaqueTypedObject::class_), &inlineObject);
|
||||
|
||||
masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfOwner()), temp);
|
||||
masm.unboxInt32(Address(temp, ArrayBufferObject::offsetOfFlagsSlot()), temp);
|
||||
|
||||
Imm32 flag(ArrayBufferObject::neuteredFlag());
|
||||
if (!bailoutTest32(Assembler::NonZero, temp, flag, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
masm.bind(&inlineObject);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitTypedObjectProto(LTypedObjectProto *lir)
|
||||
{
|
||||
@ -5065,12 +5043,13 @@ CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
|
||||
Label inlineObject, done;
|
||||
masm.loadObjClass(obj, out);
|
||||
masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineOpaqueTypedObject::class_), &inlineObject);
|
||||
masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineTransparentTypedObject::class_), &inlineObject);
|
||||
|
||||
masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), out);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&inlineObject);
|
||||
masm.computeEffectiveAddress(Address(obj, InlineOpaqueTypedObject::offsetOfDataStart()), out);
|
||||
masm.computeEffectiveAddress(Address(obj, InlineTypedObject::offsetOfDataStart()), out);
|
||||
masm.bind(&done);
|
||||
|
||||
return true;
|
||||
@ -5090,12 +5069,13 @@ CodeGenerator::visitSetTypedObjectOffset(LSetTypedObjectOffset *lir)
|
||||
Label inlineObject, done;
|
||||
masm.loadObjClass(temp0, temp1);
|
||||
masm.branchPtr(Assembler::Equal, temp1, ImmPtr(&InlineOpaqueTypedObject::class_), &inlineObject);
|
||||
masm.branchPtr(Assembler::Equal, temp1, ImmPtr(&InlineTransparentTypedObject::class_), &inlineObject);
|
||||
|
||||
masm.loadPrivate(Address(temp0, ArrayBufferObject::offsetOfDataSlot()), temp0);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&inlineObject);
|
||||
masm.addPtr(ImmWord(InlineOpaqueTypedObject::offsetOfDataStart()), temp0);
|
||||
masm.addPtr(ImmWord(InlineTypedObject::offsetOfDataStart()), temp0);
|
||||
|
||||
masm.bind(&done);
|
||||
|
||||
|
@ -181,7 +181,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitSetArrayLength(LSetArrayLength *lir);
|
||||
bool visitTypedArrayLength(LTypedArrayLength *lir);
|
||||
bool visitTypedArrayElements(LTypedArrayElements *lir);
|
||||
bool visitNeuterCheck(LNeuterCheck *lir);
|
||||
bool visitTypedObjectElements(LTypedObjectElements *lir);
|
||||
bool visitSetTypedObjectOffset(LSetTypedObjectOffset *lir);
|
||||
bool visitTypedObjectProto(LTypedObjectProto *ins);
|
||||
|
@ -7247,8 +7247,7 @@ IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypedObjectPrediction objPrediction,
|
||||
MDefinition **indexAsByteOffset,
|
||||
bool *canBeNeutered)
|
||||
MDefinition **indexAsByteOffset)
|
||||
{
|
||||
// Ensure index is an integer.
|
||||
MInstruction *idInt32 = MToInt32::New(alloc(), index);
|
||||
@ -7263,17 +7262,17 @@ IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
|
||||
if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
|
||||
length = constantInt(lenOfAll);
|
||||
|
||||
// If we are not loading the length from the object itself,
|
||||
// then we still need to check if the object was neutered.
|
||||
*canBeNeutered = true;
|
||||
// If we are not loading the length from the object itself, only
|
||||
// optimize if the array buffer can't have been neutered.
|
||||
types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
|
||||
if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_SIZED_OBJECT_NEUTERED))
|
||||
return false;
|
||||
} else if (objPrediction.kind() == type::UnsizedArray) {
|
||||
// Note: unsized arrays will have their length set to zero if they are
|
||||
// neutered, so we don't need to make sure that no neutering has
|
||||
// occurred which affects this object.
|
||||
length = MTypedObjectUnsizedLength::New(alloc(), obj);
|
||||
current->add(length->toInstruction());
|
||||
|
||||
// If we are loading the length from the object itself,
|
||||
// then we do not need an extra neuter check, because the length
|
||||
// will have been set to 0 when the object was neutered.
|
||||
*canBeNeutered = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -7304,31 +7303,25 @@ IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
|
||||
ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
|
||||
MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
|
||||
|
||||
bool canBeNeutered;
|
||||
MDefinition *indexAsByteOffset;
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
|
||||
&indexAsByteOffset, &canBeNeutered))
|
||||
{
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
|
||||
return true;
|
||||
}
|
||||
|
||||
return pushScalarLoadFromTypedObject(emitted, obj, indexAsByteOffset, elemType, canBeNeutered);
|
||||
return pushScalarLoadFromTypedObject(emitted, obj, indexAsByteOffset, elemType);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::pushScalarLoadFromTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *offset,
|
||||
ScalarTypeDescr::Type elemType,
|
||||
bool canBeNeutered)
|
||||
ScalarTypeDescr::Type elemType)
|
||||
{
|
||||
int32_t size = ScalarTypeDescr::size(elemType);
|
||||
MOZ_ASSERT(size == ScalarTypeDescr::alignment(elemType));
|
||||
|
||||
// Find location within the owner object.
|
||||
MDefinition *elements, *scaledOffset;
|
||||
loadTypedObjectElements(obj, offset, size, canBeNeutered,
|
||||
&elements, &scaledOffset);
|
||||
loadTypedObjectElements(obj, offset, size, &elements, &scaledOffset);
|
||||
|
||||
// Load the element.
|
||||
MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset, elemType);
|
||||
@ -7369,16 +7362,12 @@ IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted,
|
||||
MDefinition *type = loadTypedObjectType(obj);
|
||||
MDefinition *elemTypeObj = typeObjectForElementFromArrayStructType(type);
|
||||
|
||||
bool canBeNeutered;
|
||||
MDefinition *indexAsByteOffset;
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
|
||||
&indexAsByteOffset, &canBeNeutered))
|
||||
{
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
|
||||
return true;
|
||||
}
|
||||
|
||||
return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
|
||||
elemPrediction, elemTypeObj, canBeNeutered);
|
||||
elemPrediction, elemTypeObj);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -7386,12 +7375,11 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *offset,
|
||||
TypedObjectPrediction derivedPrediction,
|
||||
MDefinition *derivedTypeObj,
|
||||
bool canBeNeutered)
|
||||
MDefinition *derivedTypeObj)
|
||||
{
|
||||
// Find location within the owner object.
|
||||
MDefinition *owner, *ownerOffset;
|
||||
loadTypedObjectData(obj, offset, canBeNeutered, &owner, &ownerOffset);
|
||||
loadTypedObjectData(obj, offset, &owner, &ownerOffset);
|
||||
|
||||
// Create the derived typed object.
|
||||
MInstruction *derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
|
||||
@ -8159,16 +8147,12 @@ IonBuilder::setElemTryScalarElemOfTypedObject(bool *emitted,
|
||||
ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
|
||||
MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
|
||||
|
||||
bool canBeNeutered;
|
||||
MDefinition *indexAsByteOffset;
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
|
||||
&indexAsByteOffset, &canBeNeutered))
|
||||
{
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Store the element
|
||||
if (!storeScalarTypedObjectValue(obj, indexAsByteOffset, elemType, canBeNeutered, false, value))
|
||||
if (!storeScalarTypedObjectValue(obj, indexAsByteOffset, elemType, false, value))
|
||||
return false;
|
||||
|
||||
current->push(value);
|
||||
@ -8543,7 +8527,7 @@ IonBuilder::jsop_setelem_typed_object(Scalar::Type arrayType, SetElemSafety safe
|
||||
MIRType_Int32, MMul::Integer);
|
||||
current->add(byteOffset);
|
||||
|
||||
if (!storeScalarTypedObjectValue(object, byteOffset, arrayType, false, racy, value))
|
||||
if (!storeScalarTypedObjectValue(object, byteOffset, arrayType, racy, value))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -9325,9 +9309,13 @@ IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedO
|
||||
// Must always be loading the same scalar type
|
||||
Scalar::Type fieldType = fieldPrediction.scalarType();
|
||||
|
||||
// Don't optimize if the typed object might be neutered.
|
||||
types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
|
||||
if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_SIZED_OBJECT_NEUTERED))
|
||||
return true;
|
||||
|
||||
// OK, perform the optimization.
|
||||
return pushScalarLoadFromTypedObject(emitted, typedObj, constantInt(fieldOffset),
|
||||
fieldType, true);
|
||||
return pushScalarLoadFromTypedObject(emitted, typedObj, constantInt(fieldOffset), fieldType);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -9338,6 +9326,11 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
|
||||
size_t fieldIndex,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
{
|
||||
// Don't optimize if the typed object might be neutered.
|
||||
types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
|
||||
if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_SIZED_OBJECT_NEUTERED))
|
||||
return true;
|
||||
|
||||
// OK, perform the optimization
|
||||
|
||||
// Identify the type object for the field.
|
||||
@ -9345,7 +9338,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
|
||||
MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
|
||||
|
||||
return pushDerivedTypedObject(emitted, typedObj, constantInt(fieldOffset),
|
||||
fieldPrediction, fieldTypeObj, true);
|
||||
fieldPrediction, fieldTypeObj);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -9992,9 +9985,14 @@ IonBuilder::setPropTryScalarPropOfTypedObject(bool *emitted,
|
||||
// Must always be loading the same scalar type
|
||||
Scalar::Type fieldType = fieldPrediction.scalarType();
|
||||
|
||||
// Don't optimize if the typed object might be neutered.
|
||||
types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
|
||||
if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_SIZED_OBJECT_NEUTERED))
|
||||
return true;
|
||||
|
||||
// OK! Perform the optimization.
|
||||
|
||||
if (!storeScalarTypedObjectValue(obj, constantInt(fieldOffset), fieldType, true, false, value))
|
||||
if (!storeScalarTypedObjectValue(obj, constantInt(fieldOffset), fieldType, false, value))
|
||||
return false;
|
||||
|
||||
current->push(value);
|
||||
@ -10958,7 +10956,6 @@ IonBuilder::loadTypedObjectType(MDefinition *typedObj)
|
||||
void
|
||||
IonBuilder::loadTypedObjectData(MDefinition *typedObj,
|
||||
MDefinition *offset,
|
||||
bool canBeNeutered,
|
||||
MDefinition **owner,
|
||||
MDefinition **ownerOffset)
|
||||
{
|
||||
@ -10985,12 +10982,6 @@ IonBuilder::loadTypedObjectData(MDefinition *typedObj,
|
||||
return;
|
||||
}
|
||||
|
||||
if (canBeNeutered) {
|
||||
MNeuterCheck *chk = MNeuterCheck::New(alloc(), typedObj);
|
||||
current->add(chk);
|
||||
typedObj = chk;
|
||||
}
|
||||
|
||||
*owner = typedObj;
|
||||
*ownerOffset = offset;
|
||||
}
|
||||
@ -11004,12 +10995,11 @@ void
|
||||
IonBuilder::loadTypedObjectElements(MDefinition *typedObj,
|
||||
MDefinition *offset,
|
||||
int32_t unit,
|
||||
bool canBeNeutered,
|
||||
MDefinition **ownerElements,
|
||||
MDefinition **ownerScaledOffset)
|
||||
{
|
||||
MDefinition *owner, *ownerOffset;
|
||||
loadTypedObjectData(typedObj, offset, canBeNeutered, &owner, &ownerOffset);
|
||||
loadTypedObjectData(typedObj, offset, &owner, &ownerOffset);
|
||||
|
||||
// Load the element data.
|
||||
MTypedObjectElements *elements = MTypedObjectElements::New(alloc(), owner);
|
||||
@ -11096,15 +11086,13 @@ bool
|
||||
IonBuilder::storeScalarTypedObjectValue(MDefinition *typedObj,
|
||||
MDefinition *byteOffset,
|
||||
ScalarTypeDescr::Type type,
|
||||
bool canBeNeutered,
|
||||
bool racy,
|
||||
MDefinition *value)
|
||||
{
|
||||
// Find location within the owner object.
|
||||
MDefinition *elements, *scaledOffset;
|
||||
size_t alignment = ScalarTypeDescr::alignment(type);
|
||||
loadTypedObjectElements(typedObj, byteOffset, alignment, canBeNeutered,
|
||||
&elements, &scaledOffset);
|
||||
loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset);
|
||||
|
||||
// Clamp value to [0, 255] when type is Uint8Clamped
|
||||
MDefinition *toWrite = value;
|
||||
|
@ -471,13 +471,11 @@ class IonBuilder
|
||||
MDefinition *loadTypedObjectType(MDefinition *value);
|
||||
void loadTypedObjectData(MDefinition *typedObj,
|
||||
MDefinition *offset,
|
||||
bool canBeNeutered,
|
||||
MDefinition **owner,
|
||||
MDefinition **ownerOffset);
|
||||
void loadTypedObjectElements(MDefinition *typedObj,
|
||||
MDefinition *offset,
|
||||
int32_t unit,
|
||||
bool canBeNeutered,
|
||||
MDefinition **ownerElements,
|
||||
MDefinition **ownerScaledOffset);
|
||||
MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj);
|
||||
@ -486,26 +484,22 @@ class IonBuilder
|
||||
bool storeScalarTypedObjectValue(MDefinition *typedObj,
|
||||
MDefinition *offset,
|
||||
ScalarTypeDescr::Type type,
|
||||
bool canBeNeutered,
|
||||
bool racy,
|
||||
MDefinition *value);
|
||||
bool checkTypedObjectIndexInBounds(int32_t elemSize,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypedObjectPrediction objTypeDescrs,
|
||||
MDefinition **indexAsByteOffset,
|
||||
bool *canBeNeutered);
|
||||
MDefinition **indexAsByteOffset);
|
||||
bool pushDerivedTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *offset,
|
||||
TypedObjectPrediction derivedTypeDescrs,
|
||||
MDefinition *derivedTypeObj,
|
||||
bool canBeNeutered);
|
||||
MDefinition *derivedTypeObj);
|
||||
bool pushScalarLoadFromTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *offset,
|
||||
ScalarTypeDescr::Type type,
|
||||
bool canBeNeutered);
|
||||
ScalarTypeDescr::Type type);
|
||||
MDefinition *neuterCheck(MDefinition *obj);
|
||||
|
||||
// jsop_setelem() helpers.
|
||||
@ -768,7 +762,8 @@ class IonBuilder
|
||||
InliningStatus inlineDump(CallInfo &callInfo);
|
||||
InliningStatus inlineHasClass(CallInfo &callInfo, const Class *clasp,
|
||||
const Class *clasp2 = nullptr,
|
||||
const Class *clasp3 = nullptr);
|
||||
const Class *clasp3 = nullptr,
|
||||
const Class *clasp4 = nullptr);
|
||||
InliningStatus inlineIsConstructing(CallInfo &callInfo);
|
||||
|
||||
// Testing functions.
|
||||
|
@ -4258,24 +4258,6 @@ class LSetTypedObjectOffset : public LInstructionHelper<0, 2, 2>
|
||||
}
|
||||
};
|
||||
|
||||
// Check whether a typed object has a neutered owner buffer.
|
||||
class LNeuterCheck : public LInstructionHelper<0, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(NeuterCheck)
|
||||
|
||||
LNeuterCheck(const LAllocation &object, const LDefinition &temp) {
|
||||
setOperand(0, object);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
const LAllocation *object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Bailout if index >= length.
|
||||
class LBoundsCheck : public LInstructionHelper<0, 2, 0>
|
||||
{
|
||||
|
@ -211,7 +211,6 @@
|
||||
_(PostWriteBarrierV) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(NeuterCheck) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckRange) \
|
||||
_(BoundsCheckLower) \
|
||||
|
@ -2590,16 +2590,6 @@ LIRGenerator::visitNot(MNot *ins)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitNeuterCheck(MNeuterCheck *ins)
|
||||
{
|
||||
LNeuterCheck *chk = new(alloc()) LNeuterCheck(useRegister(ins->object()),
|
||||
temp());
|
||||
if (!assignSnapshot(chk, Bailout_Neutered))
|
||||
return false;
|
||||
return redefine(ins, ins->input()) && add(chk, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitBoundsCheck(MBoundsCheck *ins)
|
||||
{
|
||||
|
@ -186,7 +186,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitSetArrayLength(MSetArrayLength *ins);
|
||||
bool visitTypedArrayLength(MTypedArrayLength *ins);
|
||||
bool visitTypedArrayElements(MTypedArrayElements *ins);
|
||||
bool visitNeuterCheck(MNeuterCheck *lir);
|
||||
bool visitTypedObjectElements(MTypedObjectElements *ins);
|
||||
bool visitSetTypedObjectOffset(MSetTypedObjectOffset *ins);
|
||||
bool visitTypedObjectProto(MTypedObjectProto *ins);
|
||||
|
@ -201,11 +201,14 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
||||
// TypedObject intrinsics.
|
||||
if (native == intrinsic_ObjectIsTypedObject)
|
||||
return inlineHasClass(callInfo,
|
||||
&TransparentTypedObject::class_,
|
||||
&OutlineTransparentTypedObject::class_,
|
||||
&OutlineOpaqueTypedObject::class_,
|
||||
&InlineTransparentTypedObject::class_,
|
||||
&InlineOpaqueTypedObject::class_);
|
||||
if (native == intrinsic_ObjectIsTransparentTypedObject)
|
||||
return inlineHasClass(callInfo, &TransparentTypedObject::class_);
|
||||
return inlineHasClass(callInfo,
|
||||
&OutlineTransparentTypedObject::class_,
|
||||
&InlineTransparentTypedObject::class_);
|
||||
if (native == intrinsic_ObjectIsOpaqueTypedObject)
|
||||
return inlineHasClass(callInfo,
|
||||
&OutlineOpaqueTypedObject::class_,
|
||||
@ -1827,7 +1830,9 @@ IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo)
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineHasClass(CallInfo &callInfo, const Class *clasp1, const Class *clasp2, const Class *clasp3)
|
||||
IonBuilder::inlineHasClass(CallInfo &callInfo,
|
||||
const Class *clasp1, const Class *clasp2,
|
||||
const Class *clasp3, const Class *clasp4)
|
||||
{
|
||||
if (callInfo.constructing() || callInfo.argc() != 1)
|
||||
return InliningStatus_NotInlined;
|
||||
@ -1840,15 +1845,18 @@ IonBuilder::inlineHasClass(CallInfo &callInfo, const Class *clasp1, const Class
|
||||
types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
|
||||
const Class *knownClass = types ? types->getKnownClass() : nullptr;
|
||||
if (knownClass) {
|
||||
pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2 || knownClass == clasp3));
|
||||
pushConstant(BooleanValue(knownClass == clasp1 ||
|
||||
knownClass == clasp2 ||
|
||||
knownClass == clasp3 ||
|
||||
knownClass == clasp4));
|
||||
} else {
|
||||
MHasClass *hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1);
|
||||
current->add(hasClass1);
|
||||
|
||||
if (!clasp2 && !clasp3) {
|
||||
if (!clasp2 && !clasp3 && !clasp4) {
|
||||
current->push(hasClass1);
|
||||
} else {
|
||||
const Class *remaining[] = { clasp2, clasp3 };
|
||||
const Class *remaining[] = { clasp2, clasp3, clasp4 };
|
||||
MDefinition *last = hasClass1;
|
||||
for (size_t i = 0; i < ArrayLength(remaining); i++) {
|
||||
MHasClass *hasClass = MHasClass::New(alloc(), callInfo.getArg(0), remaining[i]);
|
||||
|
@ -7351,42 +7351,6 @@ class MTypedArrayElements
|
||||
ALLOW_CLONE(MTypedArrayElements)
|
||||
};
|
||||
|
||||
// Checks whether a typed object is neutered.
|
||||
class MNeuterCheck
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
private:
|
||||
explicit MNeuterCheck(MDefinition *object)
|
||||
: MUnaryInstruction(object)
|
||||
{
|
||||
MOZ_ASSERT(object->type() == MIRType_Object);
|
||||
setResultType(MIRType_Object);
|
||||
setResultTypeSet(object->resultTypeSet());
|
||||
setGuard();
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(NeuterCheck)
|
||||
|
||||
static MNeuterCheck *New(TempAllocator &alloc, MDefinition *object) {
|
||||
return new(alloc) MNeuterCheck(object);
|
||||
}
|
||||
|
||||
MDefinition *object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
};
|
||||
|
||||
// Load a binary data object's "elements", which is just its opaque
|
||||
// binary data space. Eventually this should probably be
|
||||
// unified with `MTypedArrayElements`.
|
||||
|
@ -166,7 +166,6 @@ namespace jit {
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(Not) \
|
||||
_(NeuterCheck) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckLower) \
|
||||
_(InArray) \
|
||||
|
@ -252,7 +252,6 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
|
||||
SAFE_OP(InitializedLength)
|
||||
WRITE_GUARDED_OP(SetInitializedLength, elements)
|
||||
SAFE_OP(Not)
|
||||
SAFE_OP(NeuterCheck)
|
||||
SAFE_OP(BoundsCheck)
|
||||
SAFE_OP(BoundsCheckLower)
|
||||
SAFE_OP(LoadElement)
|
||||
|
@ -57,6 +57,7 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options =
|
||||
globalWriteBarriered(false),
|
||||
propertyTree(thisForCtor()),
|
||||
selfHostingScriptSource(nullptr),
|
||||
lazyArrayBuffers(nullptr),
|
||||
gcIncomingGrayPointers(nullptr),
|
||||
gcWeakMapList(nullptr),
|
||||
gcPreserveJitCode(options.preserveJitCode()),
|
||||
@ -83,6 +84,7 @@ JSCompartment::~JSCompartment()
|
||||
js_delete(scriptCountsMap);
|
||||
js_delete(debugScriptMap);
|
||||
js_delete(debugScopes);
|
||||
js_delete(lazyArrayBuffers);
|
||||
js_free(enumerators);
|
||||
|
||||
runtime_->numCompartments--;
|
||||
@ -869,6 +871,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t *compartmentObject,
|
||||
size_t *compartmentTables,
|
||||
size_t *innerViewsArg,
|
||||
size_t *lazyArrayBuffersArg,
|
||||
size_t *crossCompartmentWrappersArg,
|
||||
size_t *regexpCompartment,
|
||||
size_t *savedStacksSet)
|
||||
@ -881,6 +884,8 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
+ newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
|
||||
+ lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
|
||||
*innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
|
||||
if (lazyArrayBuffers)
|
||||
*lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
|
||||
*crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
|
||||
*regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
|
||||
*savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
|
||||
|
@ -132,6 +132,7 @@ struct TypeInferenceSizes;
|
||||
namespace js {
|
||||
class AutoDebugModeInvalidation;
|
||||
class DebugScopes;
|
||||
class LazyArrayBufferTable;
|
||||
class WeakMapBase;
|
||||
}
|
||||
|
||||
@ -249,6 +250,7 @@ struct JSCompartment
|
||||
size_t *compartmentObject,
|
||||
size_t *compartmentTables,
|
||||
size_t *innerViews,
|
||||
size_t *lazyArrayBuffers,
|
||||
size_t *crossCompartmentWrappers,
|
||||
size_t *regexpCompartment,
|
||||
size_t *savedStacksSet);
|
||||
@ -292,10 +294,12 @@ struct JSCompartment
|
||||
*/
|
||||
js::ReadBarrieredScriptSourceObject selfHostingScriptSource;
|
||||
|
||||
// Information mapping objects which own their own storage to other objects
|
||||
// sharing that storage.
|
||||
// Map from array buffers to views sharing that storage.
|
||||
js::InnerViewTable innerViews;
|
||||
|
||||
// Map from typed objects to array buffers lazily created for them.
|
||||
js::LazyArrayBufferTable *lazyArrayBuffers;
|
||||
|
||||
/* During GC, stores the index of this compartment in rt->compartments. */
|
||||
unsigned gcIndex;
|
||||
|
||||
|
@ -482,23 +482,30 @@ enum MOZ_ENUM_TYPE(uint32_t) {
|
||||
*/
|
||||
OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000,
|
||||
|
||||
/*
|
||||
* For a global object, whether any array buffers in this compartment with
|
||||
* sized typed object views have been neutered. Sized typed objects have
|
||||
* different neutering checks from other array buffer views.
|
||||
*/
|
||||
OBJECT_FLAG_SIZED_OBJECT_NEUTERED = 0x00400000,
|
||||
|
||||
/*
|
||||
* Whether objects with this type should be allocated directly in the
|
||||
* tenured heap.
|
||||
*/
|
||||
OBJECT_FLAG_PRE_TENURE = 0x00400000,
|
||||
OBJECT_FLAG_PRE_TENURE = 0x00800000,
|
||||
|
||||
/* Whether objects with this type might have copy on write elements. */
|
||||
OBJECT_FLAG_COPY_ON_WRITE = 0x00800000,
|
||||
OBJECT_FLAG_COPY_ON_WRITE = 0x01000000,
|
||||
|
||||
/*
|
||||
* Whether all properties of this object are considered unknown.
|
||||
* If set, all other flags in DYNAMIC_MASK will also be set.
|
||||
*/
|
||||
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x01000000,
|
||||
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x02000000,
|
||||
|
||||
/* Flags which indicate dynamic properties of represented objects. */
|
||||
OBJECT_FLAG_DYNAMIC_MASK = 0x01ff0000,
|
||||
OBJECT_FLAG_DYNAMIC_MASK = 0x03ff0000,
|
||||
|
||||
/* Mask for objects created with unknown properties. */
|
||||
OBJECT_FLAG_UNKNOWN_MASK =
|
||||
@ -507,8 +514,8 @@ enum MOZ_ENUM_TYPE(uint32_t) {
|
||||
|
||||
// Mask/shift for this type object's generation. If out of sync with the
|
||||
// TypeZone's generation, this TypeObject hasn't been swept yet.
|
||||
OBJECT_FLAG_GENERATION_MASK = 0x02000000,
|
||||
OBJECT_FLAG_GENERATION_SHIFT = 25,
|
||||
OBJECT_FLAG_GENERATION_MASK = 0x04000000,
|
||||
OBJECT_FLAG_GENERATION_SHIFT = 26,
|
||||
};
|
||||
typedef uint32_t TypeObjectFlags;
|
||||
|
||||
|
@ -283,8 +283,10 @@ JSObject::isUnqualifiedVarObj()
|
||||
return lastProperty()->hasObjectFlag(js::BaseShape::UNQUALIFIED_VAROBJ);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
inline bool
|
||||
ClassCanHaveFixedData(const js::Class *clasp)
|
||||
ClassCanHaveFixedData(const Class *clasp)
|
||||
{
|
||||
// Normally, the number of fixed slots given an object is the maximum
|
||||
// permitted for its size class. For array buffers and non-shared typed
|
||||
@ -293,10 +295,11 @@ ClassCanHaveFixedData(const js::Class *clasp)
|
||||
// buffer's data.
|
||||
return !clasp->isNative()
|
||||
|| clasp == &js::ArrayBufferObject::class_
|
||||
|| clasp == &js::InlineOpaqueTypedObject::class_
|
||||
|| js::IsTypedArrayClass(clasp);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
/* static */ inline JSObject *
|
||||
JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
|
||||
js::HandleShape shape, js::HandleTypeObject type)
|
||||
@ -304,7 +307,7 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi
|
||||
MOZ_ASSERT(shape && type);
|
||||
MOZ_ASSERT(type->clasp() == shape->getObjectClass());
|
||||
MOZ_ASSERT(type->clasp() != &js::ArrayObject::class_);
|
||||
MOZ_ASSERT_IF(!ClassCanHaveFixedData(type->clasp()),
|
||||
MOZ_ASSERT_IF(!js::ClassCanHaveFixedData(type->clasp()),
|
||||
js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
|
||||
MOZ_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
|
||||
MOZ_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
|
||||
|
@ -121,7 +121,7 @@ const Class ArrayBufferObject::class_ = {
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
nullptr, /* trace */
|
||||
ArrayBufferObject::trace,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
{
|
||||
nullptr, /* outerObject */
|
||||
@ -514,6 +514,24 @@ ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer,
|
||||
if (buffer->isAsmJS() && !OnDetachAsmJSArrayBuffer(cx, buffer))
|
||||
return false;
|
||||
|
||||
// When neutering buffers where we don't know all views, the new data must
|
||||
// match the old data. All missing views are sized typed objects, which do
|
||||
// not have a length property to mutate when neutering.
|
||||
MOZ_ASSERT_IF(buffer->forInlineTypedObject(),
|
||||
newContents.data() == buffer->dataPointer());
|
||||
|
||||
// When neutering a buffer with sized typed object views, any jitcode which
|
||||
// accesses such views needs to be deoptimized so that neuter checks are
|
||||
// performed. This is done by setting a compartment wide flag indicating
|
||||
// that buffers with sized object views have been neutered.
|
||||
if (buffer->hasSizedObjectViews()) {
|
||||
// Make sure the global object's type has been instantiated, so the
|
||||
// flag change will be observed.
|
||||
if (!cx->global()->getType(cx))
|
||||
CrashAtUnhandlableOOM("ArrayBufferObject::neuter");
|
||||
types::MarkTypeObjectFlags(cx, cx->global(), types::OBJECT_FLAG_SIZED_OBJECT_NEUTERED);
|
||||
}
|
||||
|
||||
// Neuter all views on the buffer, clear out the list of views and the
|
||||
// buffer's data.
|
||||
|
||||
@ -522,9 +540,16 @@ ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer,
|
||||
buffer->neuterView(cx, (*views)[i], newContents);
|
||||
cx->compartment()->innerViews.removeViews(buffer);
|
||||
}
|
||||
if (buffer->firstView())
|
||||
buffer->neuterView(cx, buffer->firstView(), newContents);
|
||||
buffer->setFirstView(nullptr);
|
||||
if (buffer->firstView()) {
|
||||
if (buffer->forInlineTypedObject()) {
|
||||
// The buffer points to inline data in its first view, so to keep
|
||||
// this pointer alive we don't clear out the first view.
|
||||
MOZ_ASSERT(buffer->firstView()->is<InlineTransparentTypedObject>());
|
||||
} else {
|
||||
buffer->neuterView(cx, buffer->firstView(), newContents);
|
||||
buffer->setFirstView(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (newContents.data() != buffer->dataPointer())
|
||||
buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newContents);
|
||||
@ -567,6 +592,8 @@ ArrayBufferObject::changeViewContents(JSContext *cx, ArrayBufferViewObject *view
|
||||
void
|
||||
ArrayBufferObject::changeContents(JSContext *cx, BufferContents newContents)
|
||||
{
|
||||
MOZ_ASSERT(!forInlineTypedObject());
|
||||
|
||||
// Change buffer contents.
|
||||
uint8_t* oldDataPointer = dataPointer();
|
||||
setNewOwnedData(cx->runtime()->defaultFreeOp(), newContents);
|
||||
@ -583,6 +610,11 @@ ArrayBufferObject::changeContents(JSContext *cx, BufferContents newContents)
|
||||
/* static */ bool
|
||||
ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer)
|
||||
{
|
||||
if (buffer->forInlineTypedObject()) {
|
||||
JS_ReportError(cx, "ArrayBuffer can't be used by asm.js");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buffer->ownsData()) {
|
||||
BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
|
||||
if (!contents)
|
||||
@ -613,6 +645,11 @@ ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buf
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer->forInlineTypedObject()) {
|
||||
JS_ReportError(cx, "ArrayBuffer can't be used by asm.js");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the entire reserved region (with all pages inaccessible).
|
||||
void *data;
|
||||
# ifdef XP_WIN
|
||||
@ -740,6 +777,7 @@ ArrayBufferObject::setFlags(uint32_t flags)
|
||||
|
||||
ArrayBufferObject *
|
||||
ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, BufferContents contents,
|
||||
OwnsState ownsState /* = OwnsData */,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
{
|
||||
MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
|
||||
@ -753,18 +791,21 @@ ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, BufferContents content
|
||||
size_t nslots = reservedSlots;
|
||||
bool allocated = false;
|
||||
if (contents) {
|
||||
// The ABO is taking ownership, so account the bytes against the zone.
|
||||
size_t nAllocated = nbytes;
|
||||
if (contents.kind() == MAPPED)
|
||||
nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
|
||||
cx->zone()->updateMallocCounter(nAllocated);
|
||||
if (ownsState == OwnsData) {
|
||||
// The ABO is taking ownership, so account the bytes against the zone.
|
||||
size_t nAllocated = nbytes;
|
||||
if (contents.kind() == MAPPED)
|
||||
nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
|
||||
cx->zone()->updateMallocCounter(nAllocated);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(ownsState == OwnsData);
|
||||
size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
|
||||
if (nbytes <= usableSlots * sizeof(Value)) {
|
||||
int newSlots = (nbytes - 1) / sizeof(Value) + 1;
|
||||
MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
|
||||
nslots = reservedSlots + newSlots;
|
||||
contents = BufferContents::createUnowned(nullptr);
|
||||
contents = BufferContents::createPlain(nullptr);
|
||||
} else {
|
||||
contents = AllocateArrayBufferContents(cx, nbytes);
|
||||
if (!contents)
|
||||
@ -789,9 +830,9 @@ ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, BufferContents content
|
||||
if (!contents) {
|
||||
void *data = obj->inlineDataPointer();
|
||||
memset(data, 0, nbytes);
|
||||
obj->initialize(nbytes, BufferContents::createUnowned(data), DoesntOwnData);
|
||||
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
|
||||
} else {
|
||||
obj->initialize(nbytes, contents, OwnsData);
|
||||
obj->initialize(nbytes, contents, ownsState);
|
||||
}
|
||||
|
||||
return obj;
|
||||
@ -801,7 +842,7 @@ ArrayBufferObject *
|
||||
ArrayBufferObject::create(JSContext *cx, uint32_t nbytes,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
{
|
||||
return create(cx, nbytes, BufferContents::createUnowned(nullptr));
|
||||
return create(cx, nbytes, BufferContents::createPlain(nullptr));
|
||||
}
|
||||
|
||||
JSObject *
|
||||
@ -866,7 +907,7 @@ ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffe
|
||||
BufferContents oldContents(buffer->dataPointer(), buffer->bufferKind());
|
||||
BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
|
||||
if (!newContents)
|
||||
return BufferContents::createUnowned(nullptr);
|
||||
return BufferContents::createPlain(nullptr);
|
||||
|
||||
if (hasStealableContents) {
|
||||
// Return the old contents and give the neutered buffer a pointer to
|
||||
@ -875,7 +916,7 @@ ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffe
|
||||
buffer->setOwnsData(DoesntOwnData);
|
||||
if (!ArrayBufferObject::neuter(cx, buffer, newContents)) {
|
||||
js_free(newContents.data());
|
||||
return BufferContents::createUnowned(nullptr);
|
||||
return BufferContents::createPlain(nullptr);
|
||||
}
|
||||
return oldContents;
|
||||
}
|
||||
@ -885,7 +926,7 @@ ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffe
|
||||
memcpy(newContents.data(), oldContents.data(), buffer->byteLength());
|
||||
if (!ArrayBufferObject::neuter(cx, buffer, oldContents)) {
|
||||
js_free(newContents.data());
|
||||
return BufferContents::createUnowned(nullptr);
|
||||
return BufferContents::createPlain(nullptr);
|
||||
}
|
||||
return newContents;
|
||||
}
|
||||
@ -924,6 +965,23 @@ ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj)
|
||||
buffer.releaseData(fop);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ArrayBufferObject::trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
// If this buffer is associated with an inline typed object,
|
||||
// fix up the data pointer if the typed object was moved.
|
||||
ArrayBufferObject &buf = obj->as<ArrayBufferObject>();
|
||||
|
||||
if (!buf.forInlineTypedObject())
|
||||
return;
|
||||
|
||||
JSObject *view = buf.firstView();
|
||||
MOZ_ASSERT(view && view->is<InlineTransparentTypedObject>());
|
||||
|
||||
gc::MarkObjectUnbarriered(trc, &view, "array buffer inline typed object owner");
|
||||
buf.setSlot(DATA_SLOT, PrivateValue(view->as<InlineTransparentTypedObject>().inlineTypedMem()));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ArrayBufferObject::objectMoved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
@ -1279,7 +1337,7 @@ JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *data)
|
||||
MOZ_ASSERT_IF(!data, nbytes == 0);
|
||||
ArrayBufferObject::BufferContents contents =
|
||||
ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
|
||||
return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject);
|
||||
return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, TenuredObject);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
@ -1336,7 +1394,7 @@ JS_NewMappedArrayBufferWithContents(JSContext *cx, size_t nbytes, void *data)
|
||||
MOZ_ASSERT(data);
|
||||
ArrayBufferObject::BufferContents contents =
|
||||
ArrayBufferObject::BufferContents::create<ArrayBufferObject::MAPPED>(data);
|
||||
return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject);
|
||||
return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, TenuredObject);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void *)
|
||||
|
@ -81,8 +81,8 @@ class ArrayBufferObjectMaybeShared : public NativeObject
|
||||
* This class holds the underlying raw buffer that the various
|
||||
* ArrayBufferViewObject subclasses (DataViewObject and the TypedArrays)
|
||||
* access. It can be created explicitly and passed to an ArrayBufferViewObject
|
||||
* subclass, or can be created implicitly by constructing a TypedArrayObject
|
||||
* with a size.
|
||||
* subclass, or can be created lazily when it is first accessed for a
|
||||
* TypedArrayObject or TypedObject that doesn't have an explicit buffer.
|
||||
*
|
||||
* ArrayBufferObject (or really the underlying memory) /is not racy/: the
|
||||
* memory is private to a single worker.
|
||||
@ -104,6 +104,11 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
public:
|
||||
|
||||
enum OwnsState {
|
||||
DoesntOwnData = 0,
|
||||
OwnsData = 1,
|
||||
};
|
||||
|
||||
enum BufferKind {
|
||||
PLAIN = 0, // malloced or inline data
|
||||
ASMJS_MALLOCED = 1,
|
||||
@ -125,7 +130,21 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
// when no longer in use. Releasing the pointer may be done by either
|
||||
// freeing or unmapping it, and how to do this is determined by the
|
||||
// buffer's other flags.
|
||||
//
|
||||
// Array buffers which do not own their data include buffers that
|
||||
// allocate their data inline, and buffers that are created lazily for
|
||||
// typed objects with inline storage, in which case the buffer points
|
||||
// directly to the typed object's storage.
|
||||
OWNS_DATA = 0x8,
|
||||
|
||||
// This array buffer was created lazily for a typed object with inline
|
||||
// data. This implies both that the typed object owns the buffer's data
|
||||
// and that the list of views sharing this buffer's data might be
|
||||
// incomplete. Any missing views will be sized typed objects.
|
||||
FOR_INLINE_TYPED_OBJECT = 0x10,
|
||||
|
||||
// Views of this buffer might include sized typed objects.
|
||||
SIZED_OBJECT_VIEWS = 0x20
|
||||
};
|
||||
|
||||
public:
|
||||
@ -151,7 +170,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
return BufferContents(static_cast<uint8_t*>(data), Kind);
|
||||
}
|
||||
|
||||
static BufferContents createUnowned(void *data)
|
||||
static BufferContents createPlain(void *data)
|
||||
{
|
||||
return BufferContents(static_cast<uint8_t*>(data), PLAIN);
|
||||
}
|
||||
@ -181,6 +200,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes,
|
||||
BufferContents contents,
|
||||
OwnsState ownsState = OwnsData,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
@ -197,6 +217,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
template<typename T>
|
||||
static bool createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
static void trace(JSTracer *trc, JSObject *obj);
|
||||
static void objectMoved(JSObject *obj, const JSObject *old);
|
||||
|
||||
static BufferContents stealContents(JSContext *cx,
|
||||
@ -227,9 +248,9 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
// ArrayBufferObjects (strongly) store the first view added to them, while
|
||||
// later views are (weakly) stored in the compartment's InnerViewTable
|
||||
// below. Buffers typically have at least one view, so this slot optimizes
|
||||
// for the common case. Avoid entries in the InnerViewTable saves memory
|
||||
// and non-incrementalized sweep time.
|
||||
// below. Buffers usually only have one view, so this slot optimizes for
|
||||
// the common case. Avoiding entries in the InnerViewTable saves memory and
|
||||
// non-incrementalized sweep time.
|
||||
ArrayBufferViewObject *firstView();
|
||||
|
||||
bool addView(JSContext *cx, JSObject *view);
|
||||
@ -301,12 +322,14 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
static uint32_t neuteredFlag() { return NEUTERED; }
|
||||
|
||||
protected:
|
||||
enum OwnsState {
|
||||
DoesntOwnData = 0,
|
||||
OwnsData = 1,
|
||||
};
|
||||
void setForInlineTypedObject() {
|
||||
setFlags(flags() | FOR_INLINE_TYPED_OBJECT);
|
||||
}
|
||||
void setHasSizedObjectViews() {
|
||||
setFlags(flags() | SIZED_OBJECT_VIEWS);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setDataPointer(BufferContents contents, OwnsState ownsState);
|
||||
void setByteLength(size_t length);
|
||||
|
||||
@ -318,6 +341,9 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
|
||||
}
|
||||
|
||||
bool forInlineTypedObject() const { return flags() & FOR_INLINE_TYPED_OBJECT; }
|
||||
bool hasSizedObjectViews() const { return flags() & SIZED_OBJECT_VIEWS; }
|
||||
|
||||
void setIsAsmJSMalloced() { setFlags((flags() & ~KIND_MASK) | ASMJS_MALLOCED); }
|
||||
void setIsNeutered() { setFlags(flags() | NEUTERED); }
|
||||
|
||||
|
@ -337,6 +337,7 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
|
||||
&cStats.compartmentObject,
|
||||
&cStats.compartmentTables,
|
||||
&cStats.innerViewsTable,
|
||||
&cStats.lazyArrayBuffersTable,
|
||||
&cStats.crossCompartmentWrappersTable,
|
||||
&cStats.regexpCompartment,
|
||||
&cStats.savedStacksSet);
|
||||
|
@ -2240,6 +2240,10 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
|
||||
cStats.innerViewsTable,
|
||||
"The table for array buffer inner views.");
|
||||
|
||||
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("lazy-array-buffers"),
|
||||
cStats.lazyArrayBuffersTable,
|
||||
"The table for typed object lazy array buffers.");
|
||||
|
||||
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
|
||||
cStats.crossCompartmentWrappersTable,
|
||||
"The cross-compartment wrapper table.");
|
||||
|
Loading…
Reference in New Issue
Block a user