Bug 1083600 - Use inline data for small transparent typed objects, r=sfink,nmatsakis.

This commit is contained in:
Brian Hackett 2014-10-29 11:14:53 -07:00
parent f34ccc4039
commit ca1120a47e
27 changed files with 510 additions and 272 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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.

View File

@ -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);

View 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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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>
{

View File

@ -211,7 +211,6 @@
_(PostWriteBarrierV) \
_(InitializedLength) \
_(SetInitializedLength) \
_(NeuterCheck) \
_(BoundsCheck) \
_(BoundsCheckRange) \
_(BoundsCheckLower) \

View File

@ -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)
{

View File

@ -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);

View File

@ -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]);

View File

@ -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`.

View File

@ -166,7 +166,6 @@ namespace jit {
_(InitializedLength) \
_(SetInitializedLength) \
_(Not) \
_(NeuterCheck) \
_(BoundsCheck) \
_(BoundsCheckLower) \
_(InArray) \

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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 *)

View File

@ -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); }

View File

@ -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);

View File

@ -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.");