зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1116855
- Add default-disabled unboxed objects for use by interpreted constructors, r=jandem.
This commit is contained in:
Родитель
530fecb909
Коммит
a8ab778b40
|
@ -727,20 +727,13 @@ class InlineTypedObject : public TypedObject
|
||||||
uint8_t data_[1];
|
uint8_t data_[1];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const size_t MaximumSize =
|
static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
|
||||||
sizeof(NativeObject) - sizeof(TypedObject) + NativeObject::MAX_FIXED_SLOTS * sizeof(Value);
|
|
||||||
|
|
||||||
static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
|
static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
|
||||||
size_t nbytes = descr->size();
|
size_t nbytes = descr->size();
|
||||||
MOZ_ASSERT(nbytes <= MaximumSize);
|
MOZ_ASSERT(nbytes <= MaximumSize);
|
||||||
|
|
||||||
if (nbytes <= sizeof(NativeObject) - sizeof(TypedObject))
|
return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
|
||||||
return gc::FINALIZE_OBJECT0;
|
|
||||||
nbytes -= sizeof(NativeObject) - sizeof(TypedObject);
|
|
||||||
|
|
||||||
size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
|
|
||||||
MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
|
|
||||||
return gc::GetGCObjectKind(dataSlots);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *inlineTypedMem() const {
|
uint8_t *inlineTypedMem() const {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
#include "vm/Symbol.h"
|
#include "vm/Symbol.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
#include "vm/TypedArrayObject.h"
|
||||||
|
#include "vm/UnboxedObject.h"
|
||||||
|
|
||||||
#include "jscompartmentinlines.h"
|
#include "jscompartmentinlines.h"
|
||||||
#include "jsinferinlines.h"
|
#include "jsinferinlines.h"
|
||||||
|
@ -1438,6 +1439,9 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
||||||
if (type->newScript())
|
if (type->newScript())
|
||||||
type->newScript()->trace(gcmarker);
|
type->newScript()->trace(gcmarker);
|
||||||
|
|
||||||
|
if (type->maybeUnboxedLayout())
|
||||||
|
type->unboxedLayout().trace(gcmarker);
|
||||||
|
|
||||||
if (TypeDescr *descr = type->maybeTypeDescr())
|
if (TypeDescr *descr = type->maybeTypeDescr())
|
||||||
PushMarkStack(gcmarker, descr);
|
PushMarkStack(gcmarker, descr);
|
||||||
|
|
||||||
|
@ -1464,6 +1468,9 @@ gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
|
||||||
if (type->newScript())
|
if (type->newScript())
|
||||||
type->newScript()->trace(trc);
|
type->newScript()->trace(trc);
|
||||||
|
|
||||||
|
if (type->maybeUnboxedLayout())
|
||||||
|
type->unboxedLayout().trace(trc);
|
||||||
|
|
||||||
if (JSObject *descr = type->maybeTypeDescr()) {
|
if (JSObject *descr = type->maybeTypeDescr()) {
|
||||||
MarkObjectUnbarriered(trc, &descr, "type_descr");
|
MarkObjectUnbarriered(trc, &descr, "type_descr");
|
||||||
type->setTypeDescr(&descr->as<TypeDescr>());
|
type->setTypeDescr(&descr->as<TypeDescr>());
|
||||||
|
@ -1700,6 +1707,9 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
||||||
HeapSlot *vp, *end;
|
HeapSlot *vp, *end;
|
||||||
JSObject *obj;
|
JSObject *obj;
|
||||||
|
|
||||||
|
const int32_t *unboxedTraceList;
|
||||||
|
uint8_t *unboxedMemory;
|
||||||
|
|
||||||
uintptr_t addr = stack.pop();
|
uintptr_t addr = stack.pop();
|
||||||
uintptr_t tag = addr & StackTagMask;
|
uintptr_t tag = addr & StackTagMask;
|
||||||
addr &= ~StackTagMask;
|
addr &= ~StackTagMask;
|
||||||
|
@ -1745,28 +1755,23 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
scan_typed_obj:
|
scan_unboxed:
|
||||||
{
|
{
|
||||||
TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
|
while (*unboxedTraceList != -1) {
|
||||||
if (!descr->hasTraceList())
|
JSString *str = *reinterpret_cast<JSString **>(unboxedMemory + *unboxedTraceList);
|
||||||
return;
|
|
||||||
const int32_t *list = descr->traceList();
|
|
||||||
uint8_t *memory = obj->as<InlineOpaqueTypedObject>().inlineTypedMem();
|
|
||||||
while (*list != -1) {
|
|
||||||
JSString *str = *reinterpret_cast<JSString **>(memory + *list);
|
|
||||||
markAndScanString(obj, str);
|
markAndScanString(obj, str);
|
||||||
list++;
|
unboxedTraceList++;
|
||||||
}
|
}
|
||||||
list++;
|
unboxedTraceList++;
|
||||||
while (*list != -1) {
|
while (*unboxedTraceList != -1) {
|
||||||
JSObject *obj2 = *reinterpret_cast<JSObject **>(memory + *list);
|
JSObject *obj2 = *reinterpret_cast<JSObject **>(unboxedMemory + *unboxedTraceList);
|
||||||
if (obj2 && markObject(obj, obj2))
|
if (obj2 && markObject(obj, obj2))
|
||||||
pushObject(obj2);
|
pushObject(obj2);
|
||||||
list++;
|
unboxedTraceList++;
|
||||||
}
|
}
|
||||||
list++;
|
unboxedTraceList++;
|
||||||
while (*list != -1) {
|
while (*unboxedTraceList != -1) {
|
||||||
const Value &v = *reinterpret_cast<Value *>(memory + *list);
|
const Value &v = *reinterpret_cast<Value *>(unboxedMemory + *unboxedTraceList);
|
||||||
if (v.isString()) {
|
if (v.isString()) {
|
||||||
markAndScanString(obj, v.toString());
|
markAndScanString(obj, v.toString());
|
||||||
} else if (v.isObject()) {
|
} else if (v.isObject()) {
|
||||||
|
@ -1776,7 +1781,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
||||||
} else if (v.isSymbol()) {
|
} else if (v.isSymbol()) {
|
||||||
markAndScanSymbol(obj, v.toSymbol());
|
markAndScanSymbol(obj, v.toSymbol());
|
||||||
}
|
}
|
||||||
list++;
|
unboxedTraceList++;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1806,8 +1811,22 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
||||||
MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook &&
|
MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook &&
|
||||||
(!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())),
|
(!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())),
|
||||||
clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
|
clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
|
||||||
if (clasp->trace == InlineTypedObject::obj_trace)
|
if (clasp->trace == InlineTypedObject::obj_trace) {
|
||||||
goto scan_typed_obj;
|
TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
|
||||||
|
if (!descr->hasTraceList())
|
||||||
|
return;
|
||||||
|
unboxedTraceList = descr->traceList();
|
||||||
|
unboxedMemory = obj->as<InlineOpaqueTypedObject>().inlineTypedMem();
|
||||||
|
goto scan_unboxed;
|
||||||
|
}
|
||||||
|
if (clasp == &UnboxedPlainObject::class_) {
|
||||||
|
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||||
|
unboxedTraceList = layout.traceList();
|
||||||
|
if (!unboxedTraceList)
|
||||||
|
return;
|
||||||
|
unboxedMemory = obj->as<UnboxedPlainObject>().data();
|
||||||
|
goto scan_unboxed;
|
||||||
|
}
|
||||||
clasp->trace(this, obj);
|
clasp->trace(this, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -420,6 +420,12 @@ GetObjectAllocKindForCopy(const Nursery &nursery, JSObject *obj)
|
||||||
// Proxies have finalizers and are not nursery allocated.
|
// Proxies have finalizers and are not nursery allocated.
|
||||||
MOZ_ASSERT(!IsProxy(obj));
|
MOZ_ASSERT(!IsProxy(obj));
|
||||||
|
|
||||||
|
// Unboxed plain objects are sized according to the data they store.
|
||||||
|
if (obj->is<UnboxedPlainObject>()) {
|
||||||
|
size_t nbytes = obj->as<UnboxedPlainObject>().layout().size();
|
||||||
|
return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
|
||||||
|
}
|
||||||
|
|
||||||
// Inlined typed objects are followed by their data, so make sure we copy
|
// Inlined typed objects are followed by their data, so make sure we copy
|
||||||
// it all over to the new object.
|
// it all over to the new object.
|
||||||
if (obj->is<InlineTypedObject>()) {
|
if (obj->is<InlineTypedObject>()) {
|
||||||
|
@ -435,7 +441,7 @@ GetObjectAllocKindForCopy(const Nursery &nursery, JSObject *obj)
|
||||||
if (obj->is<OutlineTypedObject>())
|
if (obj->is<OutlineTypedObject>())
|
||||||
return FINALIZE_OBJECT0;
|
return FINALIZE_OBJECT0;
|
||||||
|
|
||||||
// The only non-native objects in existence are proxies and typed objects.
|
// All nursery allocatable non-native objects are handled above.
|
||||||
MOZ_ASSERT(obj->isNative());
|
MOZ_ASSERT(obj->isNative());
|
||||||
|
|
||||||
AllocKind kind = GetGCObjectFixedSlotsKind(obj->as<NativeObject>().numFixedSlots());
|
AllocKind kind = GetGCObjectFixedSlotsKind(obj->as<NativeObject>().numFixedSlots());
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
function Foo(a, b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidate_foo() {
|
||||||
|
var a = [];
|
||||||
|
var counter = 0;
|
||||||
|
for (var i = 0; i < 50; i++)
|
||||||
|
a.push(new Foo(i, i + 1));
|
||||||
|
Object.defineProperty(Foo.prototype, "a", {configurable: true, set: function() { counter++; }});
|
||||||
|
for (var i = 0; i < 50; i++)
|
||||||
|
a.push(new Foo(i, i + 1));
|
||||||
|
delete Foo.prototype.a;
|
||||||
|
var total = 0;
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
assertEq('a' in a[i], i < 50);
|
||||||
|
total += a[i].b;
|
||||||
|
}
|
||||||
|
assertEq(total, 2550);
|
||||||
|
assertEq(counter, 50);
|
||||||
|
}
|
||||||
|
invalidate_foo();
|
||||||
|
|
||||||
|
function Bar(a, b, fn) {
|
||||||
|
this.a = a;
|
||||||
|
if (b == 30)
|
||||||
|
Object.defineProperty(Bar.prototype, "b", {configurable: true, set: fn});
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidate_bar() {
|
||||||
|
var a = [];
|
||||||
|
var counter = 0;
|
||||||
|
function fn() { counter++; }
|
||||||
|
for (var i = 0; i < 50; i++)
|
||||||
|
a.push(new Bar(i, i + 1, fn));
|
||||||
|
delete Bar.prototype.b;
|
||||||
|
var total = 0;
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
assertEq('a' in a[i], true);
|
||||||
|
assertEq('b' in a[i], i < 29);
|
||||||
|
total += a[i].a;
|
||||||
|
}
|
||||||
|
assertEq(total, 1225);
|
||||||
|
assertEq(counter, 21);
|
||||||
|
}
|
||||||
|
invalidate_bar();
|
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
// Test various ways of converting an unboxed object to native.
|
||||||
|
|
||||||
|
function Foo(a, b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxyObj = {
|
||||||
|
get: function(recipient, name) {
|
||||||
|
return recipient[name] + 2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var a = [];
|
||||||
|
for (var i = 0; i < 50; i++)
|
||||||
|
a.push(new Foo(i, i + 1));
|
||||||
|
|
||||||
|
var prop = "a";
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (; i < 5; i++)
|
||||||
|
a[i].c = i;
|
||||||
|
for (; i < 10; i++)
|
||||||
|
Object.defineProperty(a[i], 'c', {value: i});
|
||||||
|
for (; i < 15; i++)
|
||||||
|
a[i] = new Proxy(a[i], proxyObj);
|
||||||
|
for (; i < 20; i++)
|
||||||
|
a[i].a = 3.5;
|
||||||
|
for (; i < 25; i++)
|
||||||
|
delete a[i].b;
|
||||||
|
for (; i < 30; i++)
|
||||||
|
a[prop] = 4;
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
for (i = 0; i < a.length; i++) {
|
||||||
|
if ('a' in a[i])
|
||||||
|
total += a[i].a;
|
||||||
|
if ('b' in a[i])
|
||||||
|
total += a[i].b;
|
||||||
|
if ('c' in a[i])
|
||||||
|
total += a[i].c;
|
||||||
|
}
|
||||||
|
assertEq(total, 2382.5);
|
||||||
|
}
|
||||||
|
f();
|
|
@ -9083,23 +9083,27 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
|
||||||
// as a constructor, for later use during Ion compilation.
|
// as a constructor, for later use during Ion compilation.
|
||||||
RootedPlainObject templateObject(cx);
|
RootedPlainObject templateObject(cx);
|
||||||
if (constructing) {
|
if (constructing) {
|
||||||
templateObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
|
JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
|
||||||
if (!templateObject)
|
if (!thisObject)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If we are calling a constructor for which the new script
|
if (thisObject->is<PlainObject>()) {
|
||||||
// properties analysis has not been performed yet, don't attach a
|
templateObject = &thisObject->as<PlainObject>();
|
||||||
// stub. After the analysis is performed, CreateThisForFunction may
|
|
||||||
// start returning objects with a different type, and the Ion
|
// If we are calling a constructor for which the new script
|
||||||
// compiler might get confused.
|
// properties analysis has not been performed yet, don't attach a
|
||||||
if (templateObject->type()->newScript() &&
|
// stub. After the analysis is performed, CreateThisForFunction may
|
||||||
!templateObject->type()->newScript()->analyzed())
|
// start returning objects with a different type, and the Ion
|
||||||
{
|
// compiler might get confused.
|
||||||
// Clear the object just created from the preliminary objects
|
if (templateObject->type()->newScript() &&
|
||||||
// on the TypeNewScript, as it will not be used or filled in by
|
!templateObject->type()->newScript()->analyzed())
|
||||||
// running code.
|
{
|
||||||
templateObject->type()->newScript()->unregisterNewObject(templateObject);
|
// Clear the object just created from the preliminary objects
|
||||||
return true;
|
// on the TypeNewScript, as it will not be used or filled in by
|
||||||
|
// running code.
|
||||||
|
templateObject->type()->newScript()->unregisterNewObject(templateObject);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5381,6 +5381,10 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
|
||||||
if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
|
if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
types::TypeObjectKey *templateObjectType = types::TypeObjectKey::get(templateObject->type());
|
||||||
|
if (templateObjectType->hasFlags(constraints(), types::OBJECT_FLAG_NEW_SCRIPT_CLEARED))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript());
|
types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript());
|
||||||
if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject)))
|
if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject)))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -1497,6 +1497,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
|
||||||
ion_(false),
|
ion_(false),
|
||||||
asmJS_(false),
|
asmJS_(false),
|
||||||
nativeRegExp_(false),
|
nativeRegExp_(false),
|
||||||
|
unboxedObjects_(false),
|
||||||
werror_(false),
|
werror_(false),
|
||||||
strictMode_(false),
|
strictMode_(false),
|
||||||
extraWarnings_(false),
|
extraWarnings_(false),
|
||||||
|
@ -1540,6 +1541,12 @@ class JS_PUBLIC_API(RuntimeOptions) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool unboxedObjects() const { return unboxedObjects_; }
|
||||||
|
RuntimeOptions &setUnboxedObjects(bool flag) {
|
||||||
|
unboxedObjects_ = flag;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool werror() const { return werror_; }
|
bool werror() const { return werror_; }
|
||||||
RuntimeOptions &setWerror(bool flag) {
|
RuntimeOptions &setWerror(bool flag) {
|
||||||
werror_ = flag;
|
werror_ = flag;
|
||||||
|
@ -1585,6 +1592,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
|
||||||
bool ion_ : 1;
|
bool ion_ : 1;
|
||||||
bool asmJS_ : 1;
|
bool asmJS_ : 1;
|
||||||
bool nativeRegExp_ : 1;
|
bool nativeRegExp_ : 1;
|
||||||
|
bool unboxedObjects_ : 1;
|
||||||
bool werror_ : 1;
|
bool werror_ : 1;
|
||||||
bool strictMode_ : 1;
|
bool strictMode_ : 1;
|
||||||
bool extraWarnings_ : 1;
|
bool extraWarnings_ : 1;
|
||||||
|
|
|
@ -192,6 +192,22 @@ GetGCObjectFixedSlotsKind(size_t numFixedSlots)
|
||||||
return slotsToThingKind[numFixedSlots];
|
return slotsToThingKind[numFixedSlots];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the best kind to use when allocating an object that needs a specific
|
||||||
|
// number of bytes.
|
||||||
|
static inline AllocKind
|
||||||
|
GetGCObjectKindForBytes(size_t nbytes)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(nbytes <= JSObject::MAX_BYTE_SIZE);
|
||||||
|
|
||||||
|
if (nbytes <= sizeof(NativeObject))
|
||||||
|
return FINALIZE_OBJECT0;
|
||||||
|
nbytes -= sizeof(NativeObject);
|
||||||
|
|
||||||
|
size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
|
||||||
|
MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
|
||||||
|
return GetGCObjectKind(dataSlots);
|
||||||
|
}
|
||||||
|
|
||||||
static inline AllocKind
|
static inline AllocKind
|
||||||
GetBackgroundAllocKind(AllocKind kind)
|
GetBackgroundAllocKind(AllocKind kind)
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "vm/HelperThreads.h"
|
#include "vm/HelperThreads.h"
|
||||||
#include "vm/Opcodes.h"
|
#include "vm/Opcodes.h"
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
|
#include "vm/UnboxedObject.h"
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jsgcinlines.h"
|
#include "jsgcinlines.h"
|
||||||
|
@ -3274,6 +3275,41 @@ TypeObject::markUnknown(ExclusiveContext *cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeNewScript *
|
||||||
|
TypeObject::anyNewScript()
|
||||||
|
{
|
||||||
|
if (newScript())
|
||||||
|
return newScript();
|
||||||
|
if (maybeUnboxedLayout())
|
||||||
|
return unboxedLayout().newScript();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TypeObject::detachNewScript(bool writeBarrier)
|
||||||
|
{
|
||||||
|
// Clear the TypeNewScript from this TypeObject and, if it has been
|
||||||
|
// analyzed, remove it from the newTypeObjects table so that it will not be
|
||||||
|
// produced by calling 'new' on the associated function anymore.
|
||||||
|
// The TypeNewScript is not actually destroyed.
|
||||||
|
TypeNewScript *newScript = anyNewScript();
|
||||||
|
MOZ_ASSERT(newScript);
|
||||||
|
|
||||||
|
if (newScript->analyzed()) {
|
||||||
|
NewTypeObjectTable &newTypeObjects = newScript->function()->compartment()->newTypeObjects;
|
||||||
|
NewTypeObjectTable::Ptr p =
|
||||||
|
newTypeObjects.lookup(NewTypeObjectTable::Lookup(nullptr, proto(), newScript->function()));
|
||||||
|
MOZ_ASSERT(p->object == this);
|
||||||
|
|
||||||
|
newTypeObjects.remove(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->newScript())
|
||||||
|
setAddendum(Addendum_None, nullptr, writeBarrier);
|
||||||
|
else
|
||||||
|
unboxedLayout().setNewScript(nullptr, writeBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TypeObject::maybeClearNewScriptOnOOM()
|
TypeObject::maybeClearNewScriptOnOOM()
|
||||||
{
|
{
|
||||||
|
@ -3282,53 +3318,55 @@ TypeObject::maybeClearNewScriptOnOOM()
|
||||||
if (!isMarked())
|
if (!isMarked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!newScript())
|
TypeNewScript *newScript = anyNewScript();
|
||||||
|
if (!newScript)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned i = 0; i < getPropertyCount(); i++) {
|
addFlags(OBJECT_FLAG_NEW_SCRIPT_CLEARED);
|
||||||
Property *prop = getProperty(i);
|
|
||||||
if (!prop)
|
|
||||||
continue;
|
|
||||||
if (prop->types.definiteProperty())
|
|
||||||
prop->types.setNonDataPropertyIgnoringConstraints();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method is called during GC sweeping, so there is no write barrier
|
// This method is called during GC sweeping, so don't trigger pre barriers.
|
||||||
// that needs to be triggered.
|
detachNewScript(/* writeBarrier = */ false);
|
||||||
js_delete(newScript());
|
|
||||||
addendum_ = nullptr;
|
js_delete(newScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TypeObject::clearNewScript(ExclusiveContext *cx)
|
TypeObject::clearNewScript(ExclusiveContext *cx)
|
||||||
{
|
{
|
||||||
if (!newScript())
|
TypeNewScript *newScript = anyNewScript();
|
||||||
|
if (!newScript)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TypeNewScript *newScript = this->newScript();
|
// Invalidate any Ion code constructing objects of this type.
|
||||||
setNewScript(nullptr);
|
setFlags(cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED);
|
||||||
|
|
||||||
|
// Mark the constructing function as having its 'new' script cleared, so we
|
||||||
|
// will not try to construct another one later.
|
||||||
|
if (!newScript->function()->setNewScriptCleared(cx))
|
||||||
|
cx->recoverFromOutOfMemory();
|
||||||
|
|
||||||
|
detachNewScript(/* writeBarrier = */ true);
|
||||||
|
|
||||||
AutoEnterAnalysis enter(cx);
|
AutoEnterAnalysis enter(cx);
|
||||||
|
|
||||||
/*
|
|
||||||
* Any definite properties we added due to analysis of the new script when
|
|
||||||
* the type object was created are now invalid: objects with the same type
|
|
||||||
* can be created by using 'new' on a different script or through some
|
|
||||||
* other mechanism (e.g. Object.create). Rather than clear out the definite
|
|
||||||
* bits on the object's properties, just mark such properties as having
|
|
||||||
* been deleted/reconfigured, which will have the same effect on JITs
|
|
||||||
* wanting to use the definite bits to optimize property accesses.
|
|
||||||
*/
|
|
||||||
for (unsigned i = 0; i < getPropertyCount(); i++) {
|
|
||||||
Property *prop = getProperty(i);
|
|
||||||
if (!prop)
|
|
||||||
continue;
|
|
||||||
if (prop->types.definiteProperty())
|
|
||||||
prop->types.setNonDataProperty(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cx->isJSContext()) {
|
if (cx->isJSContext()) {
|
||||||
newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this);
|
bool found = newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this);
|
||||||
|
|
||||||
|
// If we managed to rollback any partially initialized objects, then
|
||||||
|
// any definite properties we added due to analysis of the new script
|
||||||
|
// are now invalid, so remove them. If there weren't any partially
|
||||||
|
// initialized objects then we don't need to change type information,
|
||||||
|
// as no more objects of this type will be created and the 'new' script
|
||||||
|
// analysis was still valid when older objects were created.
|
||||||
|
if (found) {
|
||||||
|
for (unsigned i = 0; i < getPropertyCount(); i++) {
|
||||||
|
Property *prop = getProperty(i);
|
||||||
|
if (!prop)
|
||||||
|
continue;
|
||||||
|
if (prop->types.definiteProperty())
|
||||||
|
prop->types.setNonDataProperty(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Threads with an ExclusiveContext are not allowed to run scripts.
|
// Threads with an ExclusiveContext are not allowed to run scripts.
|
||||||
MOZ_ASSERT(!cx->perThreadData->runtimeIfOnOwnerThread() ||
|
MOZ_ASSERT(!cx->perThreadData->runtimeIfOnOwnerThread() ||
|
||||||
|
@ -3770,6 +3808,63 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// PreliminaryObjectArray
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void
|
||||||
|
PreliminaryObjectArray::registerNewObject(JSObject *res)
|
||||||
|
{
|
||||||
|
// The preliminary object pointers are weak, and won't be swept properly
|
||||||
|
// during nursery collections, so the preliminary objects need to be
|
||||||
|
// initially tenured.
|
||||||
|
MOZ_ASSERT(!IsInsideNursery(res));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < COUNT; i++) {
|
||||||
|
if (!objects[i]) {
|
||||||
|
objects[i] = res;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_CRASH("There should be room for registering the new object");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PreliminaryObjectArray::unregisterNewObject(JSObject *res)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < COUNT; i++) {
|
||||||
|
if (objects[i] == res) {
|
||||||
|
objects[i] = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_CRASH("The object should be one of the preliminary objects");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PreliminaryObjectArray::full() const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < COUNT; i++) {
|
||||||
|
if (!objects[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PreliminaryObjectArray::sweep()
|
||||||
|
{
|
||||||
|
// All objects in the array are weak, so clear any that are about to be
|
||||||
|
// destroyed.
|
||||||
|
for (size_t i = 0; i < COUNT; i++) {
|
||||||
|
JSObject **ptr = &objects[i];
|
||||||
|
if (*ptr && IsObjectAboutToBeFinalized(ptr))
|
||||||
|
*ptr = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// TypeNewScript
|
// TypeNewScript
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
@ -3781,6 +3876,7 @@ TypeNewScript::make(JSContext *cx, TypeObject *type, JSFunction *fun)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||||
MOZ_ASSERT(!type->newScript());
|
MOZ_ASSERT(!type->newScript());
|
||||||
|
MOZ_ASSERT(!type->maybeUnboxedLayout());
|
||||||
|
|
||||||
if (type->unknownProperties())
|
if (type->unknownProperties())
|
||||||
return;
|
return;
|
||||||
|
@ -3789,14 +3885,12 @@ TypeNewScript::make(JSContext *cx, TypeObject *type, JSFunction *fun)
|
||||||
if (!newScript)
|
if (!newScript)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
newScript->fun = fun;
|
newScript->function_ = fun;
|
||||||
|
|
||||||
PlainObject **preliminaryObjects =
|
newScript->preliminaryObjects = type->zone()->new_<PreliminaryObjectArray>();
|
||||||
type->zone()->pod_calloc<PlainObject *>(PRELIMINARY_OBJECT_COUNT);
|
if (!newScript->preliminaryObjects)
|
||||||
if (!preliminaryObjects)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
newScript->preliminaryObjects = preliminaryObjects;
|
|
||||||
type->setNewScript(newScript.forget());
|
type->setNewScript(newScript.forget());
|
||||||
|
|
||||||
gc::TraceTypeNewScript(type);
|
gc::TraceTypeNewScript(type);
|
||||||
|
@ -3816,40 +3910,19 @@ TypeNewScript::registerNewObject(PlainObject *res)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!analyzed());
|
MOZ_ASSERT(!analyzed());
|
||||||
|
|
||||||
// The preliminary object pointers are weak, and won't be swept properly
|
|
||||||
// during nursery collections, so the preliminary objects need to be
|
|
||||||
// initially tenured.
|
|
||||||
MOZ_ASSERT(!IsInsideNursery(res));
|
|
||||||
|
|
||||||
// New script objects must have the maximum number of fixed slots, so that
|
// New script objects must have the maximum number of fixed slots, so that
|
||||||
// we can adjust their shape later to match the number of fixed slots used
|
// we can adjust their shape later to match the number of fixed slots used
|
||||||
// by the template object we eventually create.
|
// by the template object we eventually create.
|
||||||
MOZ_ASSERT(res->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS);
|
MOZ_ASSERT(res->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS);
|
||||||
|
|
||||||
for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
|
preliminaryObjects->registerNewObject(res);
|
||||||
if (!preliminaryObjects[i]) {
|
|
||||||
preliminaryObjects[i] = res;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_CRASH("There should be room for registering the new object");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TypeNewScript::unregisterNewObject(PlainObject *res)
|
TypeNewScript::unregisterNewObject(PlainObject *res)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!analyzed());
|
MOZ_ASSERT(!analyzed());
|
||||||
|
preliminaryObjects->unregisterNewObject(res);
|
||||||
for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
|
|
||||||
if (preliminaryObjects[i] == res) {
|
|
||||||
preliminaryObjects[i] = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The object should be one of the preliminary objects.
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return whether shape consists entirely of plain data properties.
|
// Return whether shape consists entirely of plain data properties.
|
||||||
|
@ -3950,14 +4023,10 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force) {
|
// Don't perform the analyses until sufficient preliminary objects have
|
||||||
// Don't perform the analyses until sufficient preliminary objects have
|
// been allocated.
|
||||||
// been allocated.
|
if (!force && !preliminaryObjects->full())
|
||||||
for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
|
return true;
|
||||||
if (!preliminaryObjects[i])
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoEnterAnalysis enter(cx);
|
AutoEnterAnalysis enter(cx);
|
||||||
|
|
||||||
|
@ -3968,10 +4037,11 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
// the preliminary objects.
|
// the preliminary objects.
|
||||||
Shape *prefixShape = nullptr;
|
Shape *prefixShape = nullptr;
|
||||||
size_t maxSlotSpan = 0;
|
size_t maxSlotSpan = 0;
|
||||||
for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
|
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
|
||||||
PlainObject *obj = preliminaryObjects[i];
|
JSObject *objBase = preliminaryObjects->get(i);
|
||||||
if (!obj)
|
if (!objBase)
|
||||||
continue;
|
continue;
|
||||||
|
PlainObject *obj = &objBase->as<PlainObject>();
|
||||||
|
|
||||||
// For now, we require all preliminary objects to have only simple
|
// For now, we require all preliminary objects to have only simple
|
||||||
// lineages of plain data properties.
|
// lineages of plain data properties.
|
||||||
|
@ -4007,10 +4077,11 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
// template object. Also recompute the prefix shape, as it reflects the
|
// template object. Also recompute the prefix shape, as it reflects the
|
||||||
// old number of fixed slots.
|
// old number of fixed slots.
|
||||||
Shape *newPrefixShape = nullptr;
|
Shape *newPrefixShape = nullptr;
|
||||||
for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
|
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
|
||||||
PlainObject *obj = preliminaryObjects[i];
|
JSObject *objBase = preliminaryObjects->get(i);
|
||||||
if (!obj)
|
if (!objBase)
|
||||||
continue;
|
continue;
|
||||||
|
PlainObject *obj = &objBase->as<PlainObject>();
|
||||||
if (!ChangeObjectFixedSlotCount(cx, obj, kind))
|
if (!ChangeObjectFixedSlotCount(cx, obj, kind))
|
||||||
return false;
|
return false;
|
||||||
if (newPrefixShape) {
|
if (newPrefixShape) {
|
||||||
|
@ -4032,7 +4103,7 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
Vector<Initializer> initializerVector(cx);
|
Vector<Initializer> initializerVector(cx);
|
||||||
|
|
||||||
RootedPlainObject templateRoot(cx, templateObject());
|
RootedPlainObject templateRoot(cx, templateObject());
|
||||||
if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, type, templateRoot, &initializerVector))
|
if (!jit::AnalyzeNewScriptDefiniteProperties(cx, function(), type, templateRoot, &initializerVector))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!type->newScript())
|
if (!type->newScript())
|
||||||
|
@ -4077,9 +4148,28 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
|
PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
js_free(preliminaryObjects);
|
// Try to use an unboxed representation for the type.
|
||||||
|
if (!TryConvertToUnboxedLayout(cx, templateObject()->lastProperty(), type, preliminaryObjects))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
js_delete(preliminaryObjects);
|
||||||
preliminaryObjects = nullptr;
|
preliminaryObjects = nullptr;
|
||||||
|
|
||||||
|
if (type->maybeUnboxedLayout()) {
|
||||||
|
// An unboxed layout was constructed for the type, and this has already
|
||||||
|
// been hooked into it.
|
||||||
|
MOZ_ASSERT(type->unboxedLayout().newScript() == this);
|
||||||
|
destroyNewScript.type = nullptr;
|
||||||
|
|
||||||
|
// Clear out the template object. This is not used for TypeNewScripts
|
||||||
|
// with an unboxed layout, and additionally this template is now a
|
||||||
|
// mutant object with a non-native class and native shape, and must be
|
||||||
|
// collected by the next GC.
|
||||||
|
templateObject_ = nullptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
|
if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
|
||||||
// The definite properties analysis found exactly the properties that
|
// The definite properties analysis found exactly the properties that
|
||||||
// are held in common by the preliminary objects. No further analysis
|
// are held in common by the preliminary objects. No further analysis
|
||||||
|
@ -4112,11 +4202,11 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
NewTypeObjectTable &table = cx->compartment()->newTypeObjects;
|
NewTypeObjectTable &table = cx->compartment()->newTypeObjects;
|
||||||
NewTypeObjectTable::Lookup lookup(type->clasp(), type->proto(), fun);
|
NewTypeObjectTable::Lookup lookup(nullptr, type->proto(), function());
|
||||||
|
|
||||||
MOZ_ASSERT(table.lookup(lookup)->object == type);
|
MOZ_ASSERT(table.lookup(lookup)->object == type);
|
||||||
table.remove(lookup);
|
table.remove(lookup);
|
||||||
table.putNew(lookup, NewTypeObjectEntry(initialType, fun));
|
table.putNew(lookup, NewTypeObjectEntry(initialType, function()));
|
||||||
|
|
||||||
templateObject()->setType(initialType);
|
templateObject()->setType(initialType);
|
||||||
|
|
||||||
|
@ -4135,7 +4225,7 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type)
|
TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type)
|
||||||
{
|
{
|
||||||
// If we cleared this new script while in the middle of initializing an
|
// If we cleared this new script while in the middle of initializing an
|
||||||
|
@ -4144,17 +4234,19 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *ty
|
||||||
// We can't detect the possibility of this statically while remaining
|
// We can't detect the possibility of this statically while remaining
|
||||||
// robust, but the new script keeps track of where each property is
|
// robust, but the new script keeps track of where each property is
|
||||||
// initialized so we can walk the stack and fix up any such objects.
|
// initialized so we can walk the stack and fix up any such objects.
|
||||||
|
// Return whether any objects were modified.
|
||||||
|
|
||||||
if (!initializerList)
|
if (!initializerList)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
RootedFunction function(cx, fun);
|
bool found = false;
|
||||||
|
|
||||||
|
RootedFunction function(cx, this->function());
|
||||||
Vector<uint32_t, 32> pcOffsets(cx);
|
Vector<uint32_t, 32> pcOffsets(cx);
|
||||||
for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
|
for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
|
||||||
pcOffsets.append(iter.script()->pcToOffset(iter.pc()));
|
pcOffsets.append(iter.script()->pcToOffset(iter.pc()));
|
||||||
|
|
||||||
// This frame has no this.
|
if (!iter.isConstructing() || !iter.matchCallee(cx, function))
|
||||||
if (!iter.isConstructing() || iter.matchCallee(cx, function))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Value thisv = iter.thisv(cx);
|
Value thisv = iter.thisv(cx);
|
||||||
|
@ -4165,6 +4257,12 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *ty
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thisv.toObject().is<UnboxedPlainObject>() &&
|
||||||
|
!thisv.toObject().as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
{
|
||||||
|
CrashAtUnhandlableOOM("rollbackPartiallyInitializedObjects");
|
||||||
|
}
|
||||||
|
|
||||||
// Found a matching frame.
|
// Found a matching frame.
|
||||||
RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
|
RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
|
||||||
|
|
||||||
|
@ -4217,15 +4315,19 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *ty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!finished)
|
if (!finished) {
|
||||||
(void) NativeObject::rollbackProperties(cx, obj, numProperties);
|
(void) NativeObject::rollbackProperties(cx, obj, numProperties);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TypeNewScript::trace(JSTracer *trc)
|
TypeNewScript::trace(JSTracer *trc)
|
||||||
{
|
{
|
||||||
MarkObject(trc, &fun, "TypeNewScript_function");
|
MarkObject(trc, &function_, "TypeNewScript_function");
|
||||||
|
|
||||||
if (templateObject_)
|
if (templateObject_)
|
||||||
MarkObject(trc, &templateObject_, "TypeNewScript_templateObject");
|
MarkObject(trc, &templateObject_, "TypeNewScript_templateObject");
|
||||||
|
@ -4240,15 +4342,8 @@ TypeNewScript::trace(JSTracer *trc)
|
||||||
void
|
void
|
||||||
TypeNewScript::sweep()
|
TypeNewScript::sweep()
|
||||||
{
|
{
|
||||||
// preliminaryObjects only holds weak pointers, so clear any objects that
|
if (preliminaryObjects)
|
||||||
// are about to be destroyed.
|
preliminaryObjects->sweep();
|
||||||
if (preliminaryObjects) {
|
|
||||||
for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
|
|
||||||
PlainObject **ptr = &preliminaryObjects[i];
|
|
||||||
if (*ptr && IsObjectAboutToBeFinalized(ptr))
|
|
||||||
*ptr = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
@ -4362,7 +4457,7 @@ NewTypeObjectEntry::hash(const Lookup &lookup)
|
||||||
NewTypeObjectEntry::match(const NewTypeObjectEntry &key, const Lookup &lookup)
|
NewTypeObjectEntry::match(const NewTypeObjectEntry &key, const Lookup &lookup)
|
||||||
{
|
{
|
||||||
return key.object->proto() == lookup.matchProto &&
|
return key.object->proto() == lookup.matchProto &&
|
||||||
key.object->clasp() == lookup.clasp &&
|
(!lookup.clasp || key.object->clasp() == lookup.clasp) &&
|
||||||
key.associated == lookup.associated;
|
key.associated == lookup.associated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4431,7 +4526,8 @@ class NewTypeObjectsSetRef : public BufferableRef
|
||||||
NewTypeObjectTable::Ptr p =
|
NewTypeObjectTable::Ptr p =
|
||||||
set->lookup(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto),
|
set->lookup(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto),
|
||||||
associated));
|
associated));
|
||||||
MOZ_ASSERT(p); // newTypeObjects set must still contain original entry.
|
if (!p)
|
||||||
|
return;
|
||||||
|
|
||||||
set->rekeyAs(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated),
|
set->rekeyAs(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated),
|
||||||
NewTypeObjectTable::Lookup(clasp, TaggedProto(proto), associated), *p);
|
NewTypeObjectTable::Lookup(clasp, TaggedProto(proto), associated), *p);
|
||||||
|
@ -4465,13 +4561,20 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSObject *as
|
||||||
MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>());
|
MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>());
|
||||||
MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject()));
|
MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject()));
|
||||||
|
|
||||||
|
// A null lookup clasp is used for 'new' type objects with an associated
|
||||||
|
// function. The type starts out as a plain object but might mutate into an
|
||||||
|
// unboxed plain object.
|
||||||
|
MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>()));
|
||||||
|
|
||||||
NewTypeObjectTable &newTypeObjects = compartment()->newTypeObjects;
|
NewTypeObjectTable &newTypeObjects = compartment()->newTypeObjects;
|
||||||
|
|
||||||
if (!newTypeObjects.initialized() && !newTypeObjects.init())
|
if (!newTypeObjects.initialized() && !newTypeObjects.init())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Canonicalize new functions to use the original one associated with its script.
|
|
||||||
if (associated && associated->is<JSFunction>()) {
|
if (associated && associated->is<JSFunction>()) {
|
||||||
|
MOZ_ASSERT(!clasp);
|
||||||
|
|
||||||
|
// Canonicalize new functions to use the original one associated with its script.
|
||||||
JSFunction *fun = &associated->as<JSFunction>();
|
JSFunction *fun = &associated->as<JSFunction>();
|
||||||
if (fun->hasScript())
|
if (fun->hasScript())
|
||||||
associated = fun->nonLazyScript()->functionNonDelazifying();
|
associated = fun->nonLazyScript()->functionNonDelazifying();
|
||||||
|
@ -4479,13 +4582,23 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSObject *as
|
||||||
associated = fun->lazyScript()->functionNonDelazifying();
|
associated = fun->lazyScript()->functionNonDelazifying();
|
||||||
else
|
else
|
||||||
associated = nullptr;
|
associated = nullptr;
|
||||||
|
|
||||||
|
// If we have previously cleared the 'new' script information for this
|
||||||
|
// function, don't try to construct another one.
|
||||||
|
if (associated && associated->wasNewScriptCleared())
|
||||||
|
associated = nullptr;
|
||||||
|
|
||||||
|
if (!associated)
|
||||||
|
clasp = &PlainObject::class_;
|
||||||
}
|
}
|
||||||
|
|
||||||
NewTypeObjectTable::AddPtr p =
|
NewTypeObjectTable::AddPtr p =
|
||||||
newTypeObjects.lookupForAdd(NewTypeObjectTable::Lookup(clasp, proto, associated));
|
newTypeObjects.lookupForAdd(NewTypeObjectTable::Lookup(clasp, proto, associated));
|
||||||
if (p) {
|
if (p) {
|
||||||
TypeObject *type = p->object;
|
TypeObject *type = p->object;
|
||||||
MOZ_ASSERT(type->clasp() == clasp);
|
MOZ_ASSERT_IF(clasp, type->clasp() == clasp);
|
||||||
|
MOZ_ASSERT_IF(!clasp, type->clasp() == &PlainObject::class_ ||
|
||||||
|
type->clasp() == &UnboxedPlainObject::class_);
|
||||||
MOZ_ASSERT(type->proto() == proto);
|
MOZ_ASSERT(type->proto() == proto);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -4496,11 +4609,13 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSObject *as
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
TypeObjectFlags initialFlags = 0;
|
TypeObjectFlags initialFlags = 0;
|
||||||
if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN))
|
if (!proto.isObject() || proto.toObject()->isNewTypeUnknown())
|
||||||
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
|
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
|
||||||
|
|
||||||
Rooted<TaggedProto> protoRoot(this, proto);
|
Rooted<TaggedProto> protoRoot(this, proto);
|
||||||
TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags);
|
TypeObject *type = compartment()->types.newTypeObject(this,
|
||||||
|
clasp ? clasp : &PlainObject::class_,
|
||||||
|
protoRoot, initialFlags);
|
||||||
if (!type)
|
if (!type)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -4727,6 +4842,9 @@ TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom)
|
||||||
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
||||||
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
||||||
|
|
||||||
|
if (maybeUnboxedLayout() && unboxedLayout().newScript())
|
||||||
|
unboxedLayout().newScript()->sweep();
|
||||||
|
|
||||||
if (newScript())
|
if (newScript())
|
||||||
newScript()->sweep();
|
newScript()->sweep();
|
||||||
|
|
||||||
|
@ -4942,53 +5060,16 @@ JSCompartment::fixupNewTypeObjectTable(NewTypeObjectTable &table)
|
||||||
needRekey = true;
|
needRekey = true;
|
||||||
}
|
}
|
||||||
if (needRekey) {
|
if (needRekey) {
|
||||||
NewTypeObjectTable::Lookup lookup(entry.object->clasp(),
|
const Class *clasp = entry.object->clasp();
|
||||||
proto,
|
if (entry.associated && entry.associated->is<JSFunction>())
|
||||||
entry.associated);
|
clasp = nullptr;
|
||||||
|
NewTypeObjectTable::Lookup lookup(clasp, proto, entry.associated);
|
||||||
e.rekeyFront(lookup, entry);
|
e.rekeyFront(lookup, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
TypeNewScript::fixupAfterMovingGC()
|
|
||||||
{
|
|
||||||
if (fun && IsForwarded(fun.get()))
|
|
||||||
fun = Forwarded(fun.get());
|
|
||||||
/* preliminaryObjects are handled by sweep(). */
|
|
||||||
if (templateObject_ && IsForwarded(templateObject_.get()))
|
|
||||||
templateObject_ = Forwarded(templateObject_.get());
|
|
||||||
if (initializedShape_ && IsForwarded(initializedShape_.get()))
|
|
||||||
initializedShape_ = Forwarded(initializedShape_.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TypeObject::fixupAfterMovingGC()
|
|
||||||
{
|
|
||||||
if (proto().isObject() && IsForwarded(proto_.get()))
|
|
||||||
proto_ = Forwarded(proto_.get());
|
|
||||||
if (singleton_ && !lazy() && IsForwarded(singleton_.get()))
|
|
||||||
singleton_ = Forwarded(singleton_.get());
|
|
||||||
if (addendum_) {
|
|
||||||
switch (addendumKind()) {
|
|
||||||
case Addendum_NewScript:
|
|
||||||
newScript()->fixupAfterMovingGC();
|
|
||||||
break;
|
|
||||||
case Addendum_TypeDescr:
|
|
||||||
if (IsForwarded(&typeDescr()))
|
|
||||||
addendum_ = Forwarded(&typeDescr());
|
|
||||||
break;
|
|
||||||
case Addendum_InterpretedFunction:
|
|
||||||
if (IsForwarded(maybeInterpretedFunction()))
|
|
||||||
addendum_ = Forwarded(maybeInterpretedFunction());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -5016,8 +5097,11 @@ JSCompartment::checkTypeObjectTableAfterMovingGC(NewTypeObjectTable &table)
|
||||||
CheckGCThingAfterMovingGC(proto.toObject());
|
CheckGCThingAfterMovingGC(proto.toObject());
|
||||||
CheckGCThingAfterMovingGC(entry.associated);
|
CheckGCThingAfterMovingGC(entry.associated);
|
||||||
|
|
||||||
NewTypeObjectTable::Lookup
|
const Class *clasp = entry.object->clasp();
|
||||||
lookup(entry.object->clasp(), proto, entry.associated);
|
if (entry.associated && entry.associated->is<JSFunction>())
|
||||||
|
clasp = nullptr;
|
||||||
|
|
||||||
|
NewTypeObjectTable::Lookup lookup(clasp, proto, entry.associated);
|
||||||
NewTypeObjectTable::Ptr ptr = table.lookup(lookup);
|
NewTypeObjectTable::Ptr ptr = table.lookup(lookup);
|
||||||
MOZ_ASSERT(ptr.found() && &*ptr == &e.front());
|
MOZ_ASSERT(ptr.found() && &*ptr == &e.front());
|
||||||
}
|
}
|
||||||
|
@ -5126,8 +5210,12 @@ TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||||
size_t
|
size_t
|
||||||
TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||||
{
|
{
|
||||||
TypeNewScript *newScript = newScriptDontCheckGeneration();
|
size_t n = 0;
|
||||||
return newScript ? newScript->sizeOfIncludingThis(mallocSizeOf) : 0;
|
if (TypeNewScript *newScript = newScriptDontCheckGeneration())
|
||||||
|
n += newScript->sizeOfIncludingThis(mallocSizeOf);
|
||||||
|
if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration())
|
||||||
|
n += layout->sizeOfIncludingThis(mallocSizeOf);
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeZone::TypeZone(Zone *zone)
|
TypeZone::TypeZone(Zone *zone)
|
||||||
|
@ -5291,19 +5379,21 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const
|
||||||
#endif /* DEBUG */
|
#endif /* DEBUG */
|
||||||
|
|
||||||
void
|
void
|
||||||
TypeObject::setAddendum(AddendumKind kind, void *addendum)
|
TypeObject::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!needsSweep());
|
MOZ_ASSERT(!needsSweep());
|
||||||
MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
|
MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
|
||||||
MOZ_ASSERT(addendumKind() == 0 || addendumKind() == kind);
|
|
||||||
|
|
||||||
// Manually trigger barriers if we are clearing a TypeNewScript. Other
|
if (writeBarrier) {
|
||||||
// kinds of addendums are immutable.
|
// Manually trigger barriers if we are clearing a TypeNewScript. Other
|
||||||
if (newScript()) {
|
// kinds of addendums are immutable.
|
||||||
MOZ_ASSERT(kind == Addendum_NewScript);
|
if (newScript())
|
||||||
TypeNewScript::writeBarrierPre(newScript());
|
TypeNewScript::writeBarrierPre(newScript());
|
||||||
|
else
|
||||||
|
MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK;
|
||||||
flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
|
flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
|
||||||
addendum_ = addendum;
|
addendum_ = addendum;
|
||||||
}
|
}
|
||||||
|
|
136
js/src/jsinfer.h
136
js/src/jsinfer.h
|
@ -27,6 +27,7 @@
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
class TypeDescr;
|
class TypeDescr;
|
||||||
|
class UnboxedLayout;
|
||||||
|
|
||||||
class TaggedProto
|
class TaggedProto
|
||||||
{
|
{
|
||||||
|
@ -406,23 +407,26 @@ enum : uint32_t {
|
||||||
/* Whether objects with this type might have copy on write elements. */
|
/* Whether objects with this type might have copy on write elements. */
|
||||||
OBJECT_FLAG_COPY_ON_WRITE = 0x01000000,
|
OBJECT_FLAG_COPY_ON_WRITE = 0x01000000,
|
||||||
|
|
||||||
|
/* Whether this type has had its 'new' script cleared in the past. */
|
||||||
|
OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Whether all properties of this object are considered unknown.
|
* Whether all properties of this object are considered unknown.
|
||||||
* If set, all other flags in DYNAMIC_MASK will also be set.
|
* If set, all other flags in DYNAMIC_MASK will also be set.
|
||||||
*/
|
*/
|
||||||
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x02000000,
|
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000,
|
||||||
|
|
||||||
/* Flags which indicate dynamic properties of represented objects. */
|
/* Flags which indicate dynamic properties of represented objects. */
|
||||||
OBJECT_FLAG_DYNAMIC_MASK = 0x03ff0000,
|
OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000,
|
||||||
|
|
||||||
// Mask/shift for the kind of addendum attached to this type object.
|
// Mask/shift for the kind of addendum attached to this type object.
|
||||||
OBJECT_FLAG_ADDENDUM_MASK = 0x0c000000,
|
OBJECT_FLAG_ADDENDUM_MASK = 0x38000000,
|
||||||
OBJECT_FLAG_ADDENDUM_SHIFT = 26,
|
OBJECT_FLAG_ADDENDUM_SHIFT = 27,
|
||||||
|
|
||||||
// Mask/shift for this type object's generation. If out of sync with the
|
// Mask/shift for this type object's generation. If out of sync with the
|
||||||
// TypeZone's generation, this TypeObject hasn't been swept yet.
|
// TypeZone's generation, this TypeObject hasn't been swept yet.
|
||||||
OBJECT_FLAG_GENERATION_MASK = 0x10000000,
|
OBJECT_FLAG_GENERATION_MASK = 0x40000000,
|
||||||
OBJECT_FLAG_GENERATION_SHIFT = 28,
|
OBJECT_FLAG_GENERATION_SHIFT = 30,
|
||||||
};
|
};
|
||||||
typedef uint32_t TypeObjectFlags;
|
typedef uint32_t TypeObjectFlags;
|
||||||
|
|
||||||
|
@ -635,7 +639,6 @@ class HeapTypeSet : public ConstraintTypeSet
|
||||||
public:
|
public:
|
||||||
/* Mark this type set as representing a non-data property. */
|
/* Mark this type set as representing a non-data property. */
|
||||||
inline void setNonDataProperty(ExclusiveContext *cx);
|
inline void setNonDataProperty(ExclusiveContext *cx);
|
||||||
inline void setNonDataPropertyIgnoringConstraints(); // Variant for use during GC.
|
|
||||||
|
|
||||||
/* Mark this type set as representing a non-writable property. */
|
/* Mark this type set as representing a non-writable property. */
|
||||||
inline void setNonWritableProperty(ExclusiveContext *cx);
|
inline void setNonWritableProperty(ExclusiveContext *cx);
|
||||||
|
@ -794,6 +797,38 @@ struct Property
|
||||||
static jsid getKey(Property *p) { return p->id; }
|
static jsid getKey(Property *p) { return p->id; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For types where only a small number of objects have been allocated, this
|
||||||
|
// structure keeps track of all objects with the type in existence. Once
|
||||||
|
// COUNT objects have been allocated, this structure is cleared and the objects
|
||||||
|
// are analyzed, to perform the new script properties analyses or determine if
|
||||||
|
// an unboxed representation can be used.
|
||||||
|
class PreliminaryObjectArray
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const uint32_t COUNT = 20;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// All objects with the type which have been allocated. The pointers in
|
||||||
|
// this array are weak.
|
||||||
|
JSObject *objects[COUNT];
|
||||||
|
|
||||||
|
public:
|
||||||
|
PreliminaryObjectArray() {
|
||||||
|
mozilla::PodZero(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerNewObject(JSObject *res);
|
||||||
|
void unregisterNewObject(JSObject *res);
|
||||||
|
|
||||||
|
JSObject *get(size_t i) const {
|
||||||
|
MOZ_ASSERT(i < COUNT);
|
||||||
|
return objects[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool full() const;
|
||||||
|
void sweep();
|
||||||
|
};
|
||||||
|
|
||||||
// New script properties analyses overview.
|
// New script properties analyses overview.
|
||||||
//
|
//
|
||||||
// When constructing objects using 'new' on a script, we attempt to determine
|
// When constructing objects using 'new' on a script, we attempt to determine
|
||||||
|
@ -851,21 +886,18 @@ class TypeNewScript
|
||||||
// Scripted function which this information was computed for.
|
// Scripted function which this information was computed for.
|
||||||
// If instances of the associated type object are created without calling
|
// If instances of the associated type object are created without calling
|
||||||
// 'new' on this function, the new script information is cleared.
|
// 'new' on this function, the new script information is cleared.
|
||||||
HeapPtrFunction fun;
|
HeapPtrFunction function_;
|
||||||
|
|
||||||
// If fewer than PRELIMINARY_OBJECT_COUNT instances of the type are
|
// Any preliminary objects with the type. The analyses are not performed
|
||||||
// created, this array holds pointers to each of those objects. When the
|
// until this array is cleared.
|
||||||
// threshold has been reached, the definite and acquired properties
|
PreliminaryObjectArray *preliminaryObjects;
|
||||||
// analyses are performed and this array is cleared. The pointers in this
|
|
||||||
// array are weak.
|
|
||||||
static const uint32_t PRELIMINARY_OBJECT_COUNT = 20;
|
|
||||||
PlainObject **preliminaryObjects;
|
|
||||||
|
|
||||||
// After the new script properties analyses have been performed, a template
|
// After the new script properties analyses have been performed, a template
|
||||||
// object to use for newly constructed objects. The shape of this object
|
// object to use for newly constructed objects. The shape of this object
|
||||||
// reflects all definite properties the object will have, and the
|
// reflects all definite properties the object will have, and the
|
||||||
// allocation kind to use. Note that this is actually a PlainObject, but is
|
// allocation kind to use. This is null if the new objects have an unboxed
|
||||||
// JSObject here to avoid cyclic include dependencies.
|
// layout, in which case the UnboxedLayout provides the initial structure
|
||||||
|
// of the object.
|
||||||
HeapPtrPlainObject templateObject_;
|
HeapPtrPlainObject templateObject_;
|
||||||
|
|
||||||
// Order in which definite properties become initialized. We need this in
|
// Order in which definite properties become initialized. We need this in
|
||||||
|
@ -892,22 +924,14 @@ class TypeNewScript
|
||||||
public:
|
public:
|
||||||
TypeNewScript() { mozilla::PodZero(this); }
|
TypeNewScript() { mozilla::PodZero(this); }
|
||||||
~TypeNewScript() {
|
~TypeNewScript() {
|
||||||
js_free(preliminaryObjects);
|
js_delete(preliminaryObjects);
|
||||||
js_free(initializerList);
|
js_free(initializerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void writeBarrierPre(TypeNewScript *newScript);
|
static inline void writeBarrierPre(TypeNewScript *newScript);
|
||||||
|
|
||||||
bool analyzed() const {
|
bool analyzed() const {
|
||||||
if (preliminaryObjects) {
|
return preliminaryObjects == nullptr;
|
||||||
MOZ_ASSERT(!templateObject());
|
|
||||||
MOZ_ASSERT(!initializerList);
|
|
||||||
MOZ_ASSERT(!initializedShape());
|
|
||||||
MOZ_ASSERT(!initializedType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(templateObject());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlainObject *templateObject() const {
|
PlainObject *templateObject() const {
|
||||||
|
@ -922,15 +946,18 @@ class TypeNewScript
|
||||||
return initializedType_;
|
return initializedType_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSFunction *function() const {
|
||||||
|
return function_;
|
||||||
|
}
|
||||||
|
|
||||||
void trace(JSTracer *trc);
|
void trace(JSTracer *trc);
|
||||||
void sweep();
|
void sweep();
|
||||||
void fixupAfterMovingGC();
|
|
||||||
|
|
||||||
void registerNewObject(PlainObject *res);
|
void registerNewObject(PlainObject *res);
|
||||||
void unregisterNewObject(PlainObject *res);
|
void unregisterNewObject(PlainObject *res);
|
||||||
bool maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, bool force = false);
|
bool maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, bool force = false);
|
||||||
|
|
||||||
void rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type);
|
bool rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type);
|
||||||
|
|
||||||
static void make(JSContext *cx, TypeObject *type, JSFunction *fun);
|
static void make(JSContext *cx, TypeObject *type, JSFunction *fun);
|
||||||
|
|
||||||
|
@ -981,7 +1008,6 @@ struct TypeObject : public gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
void setClasp(const Class *clasp) {
|
void setClasp(const Class *clasp) {
|
||||||
MOZ_ASSERT(singleton());
|
|
||||||
clasp_ = clasp;
|
clasp_ = clasp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1029,6 +1055,11 @@ struct TypeObject : public gc::TenuredCell
|
||||||
// function, the addendum stores a TypeNewScript.
|
// function, the addendum stores a TypeNewScript.
|
||||||
Addendum_NewScript,
|
Addendum_NewScript,
|
||||||
|
|
||||||
|
// When objects with this type have an unboxed representation, the
|
||||||
|
// addendum stores an UnboxedLayout (which might have a TypeNewScript
|
||||||
|
// as well, if the type is also constructed using 'new').
|
||||||
|
Addendum_UnboxedLayout,
|
||||||
|
|
||||||
// When used by typed objects, the addendum stores a TypeDescr.
|
// When used by typed objects, the addendum stores a TypeDescr.
|
||||||
Addendum_TypeDescr
|
Addendum_TypeDescr
|
||||||
};
|
};
|
||||||
|
@ -1037,7 +1068,7 @@ struct TypeObject : public gc::TenuredCell
|
||||||
// format is indicated by the object's addendum kind.
|
// format is indicated by the object's addendum kind.
|
||||||
void *addendum_;
|
void *addendum_;
|
||||||
|
|
||||||
void setAddendum(AddendumKind kind, void *addendum);
|
void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true);
|
||||||
|
|
||||||
AddendumKind addendumKind() const {
|
AddendumKind addendumKind() const {
|
||||||
return (AddendumKind)
|
return (AddendumKind)
|
||||||
|
@ -1045,11 +1076,20 @@ struct TypeObject : public gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeNewScript *newScriptDontCheckGeneration() const {
|
TypeNewScript *newScriptDontCheckGeneration() const {
|
||||||
return addendumKind() == Addendum_NewScript
|
if (addendumKind() == Addendum_NewScript)
|
||||||
? reinterpret_cast<TypeNewScript *>(addendum_)
|
return reinterpret_cast<TypeNewScript *>(addendum_);
|
||||||
: nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const {
|
||||||
|
if (addendumKind() == Addendum_UnboxedLayout)
|
||||||
|
return reinterpret_cast<UnboxedLayout *>(addendum_);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeNewScript *anyNewScript();
|
||||||
|
void detachNewScript(bool writeBarrier);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TypeObjectFlags flags() {
|
TypeObjectFlags flags() {
|
||||||
|
@ -1076,6 +1116,20 @@ struct TypeObject : public gc::TenuredCell
|
||||||
setAddendum(Addendum_NewScript, newScript);
|
setAddendum(Addendum_NewScript, newScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnboxedLayout *maybeUnboxedLayout() {
|
||||||
|
maybeSweep(nullptr);
|
||||||
|
return maybeUnboxedLayoutDontCheckGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnboxedLayout &unboxedLayout() {
|
||||||
|
MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
|
||||||
|
return *maybeUnboxedLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUnboxedLayout(UnboxedLayout *layout) {
|
||||||
|
setAddendum(Addendum_UnboxedLayout, layout);
|
||||||
|
}
|
||||||
|
|
||||||
TypeDescr *maybeTypeDescr() {
|
TypeDescr *maybeTypeDescr() {
|
||||||
// Note: there is no need to sweep when accessing the type descriptor
|
// Note: there is no need to sweep when accessing the type descriptor
|
||||||
// of an object, as it is strongly held and immutable.
|
// of an object, as it is strongly held and immutable.
|
||||||
|
@ -1115,8 +1169,8 @@ struct TypeObject : public gc::TenuredCell
|
||||||
* values that can be read out of that property in actual JS objects.
|
* values that can be read out of that property in actual JS objects.
|
||||||
* In native objects, property types account for plain data properties
|
* In native objects, property types account for plain data properties
|
||||||
* (those with a slot and no getter or setter hook) and dense elements.
|
* (those with a slot and no getter or setter hook) and dense elements.
|
||||||
* In typed objects, property types account for object and value properties
|
* In typed objects and unboxed objects, property types account for object
|
||||||
* and elements in the object.
|
* and value properties and elements in the object.
|
||||||
*
|
*
|
||||||
* For accesses on these properties, the correspondence is as follows:
|
* For accesses on these properties, the correspondence is as follows:
|
||||||
*
|
*
|
||||||
|
@ -1139,9 +1193,10 @@ struct TypeObject : public gc::TenuredCell
|
||||||
* 2. Array lengths are special cased by the compiler and VM and are not
|
* 2. Array lengths are special cased by the compiler and VM and are not
|
||||||
* reflected in property types.
|
* reflected in property types.
|
||||||
*
|
*
|
||||||
* 3. In typed objects, the initial values of properties (null pointers and
|
* 3. In typed objects (but not unboxed objects), the initial values of
|
||||||
* undefined values) are not reflected in the property types. These
|
* properties (null pointers and undefined values) are not reflected in
|
||||||
* values are always possible when reading the property.
|
* the property types. These values are always possible when reading the
|
||||||
|
* property.
|
||||||
*
|
*
|
||||||
* We establish these by using write barriers on calls to setProperty and
|
* We establish these by using write barriers on calls to setProperty and
|
||||||
* defineProperty which are on native properties, and on any jitcode which
|
* defineProperty which are on native properties, and on any jitcode which
|
||||||
|
@ -1239,11 +1294,10 @@ struct TypeObject : public gc::TenuredCell
|
||||||
flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
|
flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fixupAfterMovingGC();
|
|
||||||
|
|
||||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||||
|
|
||||||
inline void finalize(FreeOp *fop);
|
inline void finalize(FreeOp *fop);
|
||||||
|
void fixupAfterMovingGC() {}
|
||||||
|
|
||||||
static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
|
static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "vm/SharedTypedArrayObject.h"
|
#include "vm/SharedTypedArrayObject.h"
|
||||||
#include "vm/StringObject.h"
|
#include "vm/StringObject.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
#include "vm/TypedArrayObject.h"
|
||||||
|
#include "vm/UnboxedObject.h"
|
||||||
|
|
||||||
#include "jscntxtinlines.h"
|
#include "jscntxtinlines.h"
|
||||||
|
|
||||||
|
@ -1081,19 +1082,13 @@ HeapTypeSet::newPropertyState(ExclusiveContext *cxArg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
|
||||||
HeapTypeSet::setNonDataPropertyIgnoringConstraints()
|
|
||||||
{
|
|
||||||
flags |= TYPE_FLAG_NON_DATA_PROPERTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
HeapTypeSet::setNonDataProperty(ExclusiveContext *cx)
|
HeapTypeSet::setNonDataProperty(ExclusiveContext *cx)
|
||||||
{
|
{
|
||||||
if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
|
if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setNonDataPropertyIgnoringConstraints();
|
flags |= TYPE_FLAG_NON_DATA_PROPERTY;
|
||||||
newPropertyState(cx);
|
newPropertyState(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1200,6 +1195,7 @@ inline void
|
||||||
TypeObject::finalize(FreeOp *fop)
|
TypeObject::finalize(FreeOp *fop)
|
||||||
{
|
{
|
||||||
fop->delete_(newScriptDontCheckGeneration());
|
fop->delete_(newScriptDontCheckGeneration());
|
||||||
|
fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t
|
inline uint32_t
|
||||||
|
@ -1294,10 +1290,10 @@ TypeObject::getProperty(unsigned i)
|
||||||
inline void
|
inline void
|
||||||
TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
|
TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
|
||||||
{
|
{
|
||||||
if (!newScript->fun->runtimeFromAnyThread()->needsIncrementalBarrier())
|
if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JS::Zone *zone = newScript->fun->zoneFromAnyThread();
|
JS::Zone *zone = newScript->function()->zoneFromAnyThread();
|
||||||
if (zone->needsIncrementalBarrier())
|
if (zone->needsIncrementalBarrier())
|
||||||
newScript->trace(zone->barrierTracer());
|
newScript->trace(zone->barrierTracer());
|
||||||
}
|
}
|
||||||
|
|
|
@ -901,6 +901,9 @@ js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const P
|
||||||
return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval);
|
return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj->is<UnboxedPlainObject>() && !obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (obj->getOps()->lookupGeneric) {
|
if (obj->getOps()->lookupGeneric) {
|
||||||
if (obj->is<ProxyObject>()) {
|
if (obj->is<ProxyObject>()) {
|
||||||
Rooted<PropertyDescriptor> pd(cx);
|
Rooted<PropertyDescriptor> pd(cx);
|
||||||
|
@ -963,6 +966,9 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj->is<UnboxedPlainObject>() && !obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (obj->getOps()->lookupGeneric) {
|
if (obj->getOps()->lookupGeneric) {
|
||||||
if (obj->is<ProxyObject>()) {
|
if (obj->is<ProxyObject>()) {
|
||||||
Rooted<PropertyDescriptor> pd(cx);
|
Rooted<PropertyDescriptor> pd(cx);
|
||||||
|
@ -1491,10 +1497,13 @@ js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee)
|
||||||
return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
|
return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline PlainObject *
|
static inline JSObject *
|
||||||
CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
|
CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
|
||||||
NewObjectKind newKind)
|
NewObjectKind newKind)
|
||||||
{
|
{
|
||||||
|
if (type->maybeUnboxedLayout() && newKind != SingletonObject)
|
||||||
|
return UnboxedPlainObject::create(cx, type, newKind);
|
||||||
|
|
||||||
if (types::TypeNewScript *newScript = type->newScript()) {
|
if (types::TypeNewScript *newScript = type->newScript()) {
|
||||||
if (newScript->analyzed()) {
|
if (newScript->analyzed()) {
|
||||||
// The definite properties analysis has been performed for this
|
// The definite properties analysis has been performed for this
|
||||||
|
@ -1540,14 +1549,14 @@ CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *pa
|
||||||
return NewObjectWithType<PlainObject>(cx, type, parent, allocKind, newKind);
|
return NewObjectWithType<PlainObject>(cx, type, parent, allocKind, newKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlainObject *
|
JSObject *
|
||||||
js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
|
js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
|
||||||
NewObjectKind newKind /* = GenericObject */)
|
NewObjectKind newKind /* = GenericObject */)
|
||||||
{
|
{
|
||||||
RootedPlainObject res(cx);
|
RootedObject res(cx);
|
||||||
|
|
||||||
if (proto) {
|
if (proto) {
|
||||||
RootedTypeObject type(cx, cx->getNewType(&PlainObject::class_, TaggedProto(proto),
|
RootedTypeObject type(cx, cx->getNewType(nullptr, TaggedProto(proto),
|
||||||
&callee->as<JSFunction>()));
|
&callee->as<JSFunction>()));
|
||||||
if (!type)
|
if (!type)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1559,7 +1568,7 @@ js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject
|
||||||
if (regenerate) {
|
if (regenerate) {
|
||||||
// The script was analyzed successfully and may have changed
|
// The script was analyzed successfully and may have changed
|
||||||
// the new type table, so refetch the type.
|
// the new type table, so refetch the type.
|
||||||
type = cx->getNewType(&PlainObject::class_, TaggedProto(proto),
|
type = cx->getNewType(nullptr, TaggedProto(proto),
|
||||||
&callee->as<JSFunction>());
|
&callee->as<JSFunction>());
|
||||||
MOZ_ASSERT(type && type->newScript());
|
MOZ_ASSERT(type && type->newScript());
|
||||||
}
|
}
|
||||||
|
@ -1581,7 +1590,7 @@ js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlainObject *
|
JSObject *
|
||||||
js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind)
|
js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind)
|
||||||
{
|
{
|
||||||
RootedValue protov(cx);
|
RootedValue protov(cx);
|
||||||
|
@ -1592,10 +1601,10 @@ js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newK
|
||||||
proto = &protov.toObject();
|
proto = &protov.toObject();
|
||||||
else
|
else
|
||||||
proto = nullptr;
|
proto = nullptr;
|
||||||
PlainObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
|
JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
|
||||||
|
|
||||||
if (obj && newKind == SingletonObject) {
|
if (obj && newKind == SingletonObject) {
|
||||||
RootedPlainObject nobj(cx, obj);
|
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
|
||||||
|
|
||||||
/* Reshape the singleton before passing it as the 'this' value. */
|
/* Reshape the singleton before passing it as the 'this' value. */
|
||||||
NativeObject::clear(cx, nobj);
|
NativeObject::clear(cx, nobj);
|
||||||
|
@ -3796,6 +3805,7 @@ JSObject::dump()
|
||||||
if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown");
|
if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown");
|
||||||
if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
|
if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
|
||||||
if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
|
if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
|
||||||
|
if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared");
|
||||||
|
|
||||||
if (obj->isNative()) {
|
if (obj->isNative()) {
|
||||||
NativeObject *nobj = &obj->as<NativeObject>();
|
NativeObject *nobj = &obj->as<NativeObject>();
|
||||||
|
|
|
@ -426,6 +426,14 @@ class JSObject : public js::gc::Cell
|
||||||
}
|
}
|
||||||
static bool setNewTypeUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj);
|
static bool setNewTypeUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj);
|
||||||
|
|
||||||
|
// Mark an object as having its 'new' script information cleared.
|
||||||
|
bool wasNewScriptCleared() const {
|
||||||
|
return lastProperty()->hasObjectFlag(js::BaseShape::NEW_SCRIPT_CLEARED);
|
||||||
|
}
|
||||||
|
bool setNewScriptCleared(js::ExclusiveContext *cx) {
|
||||||
|
return setFlag(cx, js::BaseShape::NEW_SCRIPT_CLEARED);
|
||||||
|
}
|
||||||
|
|
||||||
/* Set a new prototype for an object with a singleton type. */
|
/* Set a new prototype for an object with a singleton type. */
|
||||||
bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle<js::TaggedProto> proto);
|
bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle<js::TaggedProto> proto);
|
||||||
|
|
||||||
|
@ -599,6 +607,9 @@ class JSObject : public js::gc::Cell
|
||||||
static size_t offsetOfType() { return offsetof(JSObject, type_); }
|
static size_t offsetOfType() { return offsetof(JSObject, type_); }
|
||||||
js::HeapPtrTypeObject *addressOfType() { return &type_; }
|
js::HeapPtrTypeObject *addressOfType() { return &type_; }
|
||||||
|
|
||||||
|
// Maximum size in bytes of a JSObject.
|
||||||
|
static const size_t MAX_BYTE_SIZE = 4 * sizeof(void *) + 16 * sizeof(JS::Value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSObject() = delete;
|
JSObject() = delete;
|
||||||
JSObject(const JSObject &other) = delete;
|
JSObject(const JSObject &other) = delete;
|
||||||
|
@ -1154,12 +1165,12 @@ GetInitialHeap(NewObjectKind newKind, const Class *clasp)
|
||||||
|
|
||||||
// Specialized call for constructing |this| with a known function callee,
|
// Specialized call for constructing |this| with a known function callee,
|
||||||
// and a known prototype.
|
// and a known prototype.
|
||||||
extern PlainObject *
|
extern JSObject *
|
||||||
CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto,
|
CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto,
|
||||||
NewObjectKind newKind = GenericObject);
|
NewObjectKind newKind = GenericObject);
|
||||||
|
|
||||||
// Specialized call for constructing |this| with a known function callee.
|
// Specialized call for constructing |this| with a known function callee.
|
||||||
extern PlainObject *
|
extern JSObject *
|
||||||
CreateThisForFunction(JSContext *cx, js::HandleObject callee, NewObjectKind newKind);
|
CreateThisForFunction(JSContext *cx, js::HandleObject callee, NewObjectKind newKind);
|
||||||
|
|
||||||
// Generic call for constructing |this|.
|
// Generic call for constructing |this|.
|
||||||
|
|
|
@ -271,6 +271,7 @@ UNIFIED_SOURCES += [
|
||||||
'vm/Symbol.cpp',
|
'vm/Symbol.cpp',
|
||||||
'vm/TypedArrayObject.cpp',
|
'vm/TypedArrayObject.cpp',
|
||||||
'vm/UbiNode.cpp',
|
'vm/UbiNode.cpp',
|
||||||
|
'vm/UnboxedObject.cpp',
|
||||||
'vm/Unicode.cpp',
|
'vm/Unicode.cpp',
|
||||||
'vm/Value.cpp',
|
'vm/Value.cpp',
|
||||||
'vm/WeakMapPtr.cpp',
|
'vm/WeakMapPtr.cpp',
|
||||||
|
|
|
@ -5529,11 +5529,13 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
|
||||||
bool enableIon = !op.getBoolOption("no-ion");
|
bool enableIon = !op.getBoolOption("no-ion");
|
||||||
bool enableAsmJS = !op.getBoolOption("no-asmjs");
|
bool enableAsmJS = !op.getBoolOption("no-asmjs");
|
||||||
bool enableNativeRegExp = !op.getBoolOption("no-native-regexp");
|
bool enableNativeRegExp = !op.getBoolOption("no-native-regexp");
|
||||||
|
bool enableUnboxedObjects = op.getBoolOption("unboxed-objects");
|
||||||
|
|
||||||
JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
|
JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
|
||||||
.setIon(enableIon)
|
.setIon(enableIon)
|
||||||
.setAsmJS(enableAsmJS)
|
.setAsmJS(enableAsmJS)
|
||||||
.setNativeRegExp(enableNativeRegExp);
|
.setNativeRegExp(enableNativeRegExp)
|
||||||
|
.setUnboxedObjects(enableUnboxedObjects);
|
||||||
|
|
||||||
if (const char *str = op.getStringOption("ion-scalar-replacement")) {
|
if (const char *str = op.getStringOption("ion-scalar-replacement")) {
|
||||||
if (strcmp(str, "on") == 0)
|
if (strcmp(str, "on") == 0)
|
||||||
|
@ -5875,6 +5877,7 @@ main(int argc, char **argv, char **envp)
|
||||||
|| !op.addBoolOption('\0', "no-ion", "Disable IonMonkey")
|
|| !op.addBoolOption('\0', "no-ion", "Disable IonMonkey")
|
||||||
|| !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
|
|| !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
|
||||||
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
|
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
|
||||||
|
|| !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed objects")
|
||||||
|| !op.addStringOption('\0', "ion-scalar-replacement", "on/off",
|
|| !op.addStringOption('\0', "ion-scalar-replacement", "on/off",
|
||||||
"Scalar Replacement (default: on, off to disable)")
|
"Scalar Replacement (default: on, off to disable)")
|
||||||
|| !op.addStringOption('\0', "ion-gvn", "[mode]",
|
|| !op.addStringOption('\0', "ion-gvn", "[mode]",
|
||||||
|
|
|
@ -367,6 +367,20 @@ NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
|
||||||
shape_ = shape;
|
shape_ = shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NativeObject::setLastPropertyMakeNonNative(Shape *shape)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!inDictionaryMode());
|
||||||
|
MOZ_ASSERT(!shape->getObjectClass()->isNative());
|
||||||
|
MOZ_ASSERT(shape->compartment() == compartment());
|
||||||
|
MOZ_ASSERT(shape->slotSpan() == 0);
|
||||||
|
MOZ_ASSERT(shape->numFixedSlots() == 0);
|
||||||
|
MOZ_ASSERT(!hasDynamicElements());
|
||||||
|
MOZ_ASSERT(!hasDynamicSlots());
|
||||||
|
|
||||||
|
shape_ = shape;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
NativeObject::setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span)
|
NativeObject::setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span)
|
||||||
{
|
{
|
||||||
|
|
|
@ -373,6 +373,8 @@ class NativeObject : public JSObject
|
||||||
|
|
||||||
static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
|
static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
|
||||||
"verify numFixedSlots() bitfield is big enough");
|
"verify numFixedSlots() bitfield is big enough");
|
||||||
|
static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE,
|
||||||
|
"inconsistent maximum object size");
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -410,6 +412,11 @@ class NativeObject : public JSObject
|
||||||
// the new properties.
|
// the new properties.
|
||||||
void setLastPropertyShrinkFixedSlots(Shape *shape);
|
void setLastPropertyShrinkFixedSlots(Shape *shape);
|
||||||
|
|
||||||
|
// As for setLastProperty(), but changes the class associated with the
|
||||||
|
// object to a non-native one. This leaves the object with a type and shape
|
||||||
|
// that are (temporarily) inconsistent.
|
||||||
|
void setLastPropertyMakeNonNative(Shape *shape);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void checkShapeConsistency();
|
void checkShapeConsistency();
|
||||||
|
|
|
@ -1078,7 +1078,6 @@ NativeObject::rollbackProperties(ExclusiveContext *cx, HandleNativeObject obj, u
|
||||||
uint32_t slot = obj->lastProperty()->slot();
|
uint32_t slot = obj->lastProperty()->slot();
|
||||||
if (slot < slotSpan)
|
if (slot < slotSpan)
|
||||||
break;
|
break;
|
||||||
MOZ_ASSERT(obj->getSlot(slot).isUndefined());
|
|
||||||
}
|
}
|
||||||
if (!obj->removeProperty(cx, obj->lastProperty()->propid()))
|
if (!obj->removeProperty(cx, obj->lastProperty()->propid()))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -406,7 +406,11 @@ class BaseShape : public gc::TenuredCell
|
||||||
QUALIFIED_VAROBJ = 0x2000,
|
QUALIFIED_VAROBJ = 0x2000,
|
||||||
UNQUALIFIED_VAROBJ = 0x4000,
|
UNQUALIFIED_VAROBJ = 0x4000,
|
||||||
|
|
||||||
OBJECT_FLAG_MASK = 0x7ff8
|
// For a function used as an interpreted constructor, whether a 'new'
|
||||||
|
// type had constructor information cleared.
|
||||||
|
NEW_SCRIPT_CLEARED = 0x8000,
|
||||||
|
|
||||||
|
OBJECT_FLAG_MASK = 0xfff8
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -0,0 +1,689 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "vm/UnboxedObject.h"
|
||||||
|
|
||||||
|
#include "jsinferinlines.h"
|
||||||
|
#include "jsobjinlines.h"
|
||||||
|
|
||||||
|
#include "vm/Shape-inl.h"
|
||||||
|
|
||||||
|
using mozilla::ArrayLength;
|
||||||
|
using mozilla::DebugOnly;
|
||||||
|
using mozilla::PodCopy;
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// UnboxedLayout
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void
|
||||||
|
UnboxedLayout::trace(JSTracer *trc)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < properties_.length(); i++)
|
||||||
|
MarkStringUnbarriered(trc, &properties_[i].name, "unboxed_layout_name");
|
||||||
|
|
||||||
|
if (newScript())
|
||||||
|
newScript()->trace(trc);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||||
|
{
|
||||||
|
return mallocSizeOf(this)
|
||||||
|
+ properties_.sizeOfExcludingThis(mallocSizeOf)
|
||||||
|
+ (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
|
||||||
|
+ mallocSizeOf(traceList());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UnboxedLayout::setNewScript(types::TypeNewScript *newScript, bool writeBarrier /* = true */)
|
||||||
|
{
|
||||||
|
if (newScript_ && writeBarrier)
|
||||||
|
types::TypeNewScript::writeBarrierPre(newScript_);
|
||||||
|
newScript_ = newScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// UnboxedPlainObject
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool
|
||||||
|
UnboxedPlainObject::setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v)
|
||||||
|
{
|
||||||
|
uint8_t *p = &data_[property.offset];
|
||||||
|
|
||||||
|
switch (property.type) {
|
||||||
|
case JSVAL_TYPE_BOOLEAN:
|
||||||
|
if (v.isBoolean()) {
|
||||||
|
*p = v.toBoolean();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case JSVAL_TYPE_INT32:
|
||||||
|
if (v.isInt32()) {
|
||||||
|
*reinterpret_cast<int32_t*>(p) = v.toInt32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case JSVAL_TYPE_DOUBLE:
|
||||||
|
if (v.isNumber()) {
|
||||||
|
*reinterpret_cast<double*>(p) = v.toNumber();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case JSVAL_TYPE_STRING:
|
||||||
|
if (v.isString()) {
|
||||||
|
*reinterpret_cast<HeapPtrString*>(p) = v.toString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case JSVAL_TYPE_OBJECT:
|
||||||
|
if (v.isObjectOrNull()) {
|
||||||
|
// Update property types when writing object properties. Types for
|
||||||
|
// other properties were captured when the unboxed layout was
|
||||||
|
// created.
|
||||||
|
types::AddTypePropertyId(cx, this, NameToId(property.name), v);
|
||||||
|
|
||||||
|
*reinterpret_cast<HeapPtrObject*>(p) = v.toObjectOrNull();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid type for unboxed value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value
|
||||||
|
UnboxedPlainObject::getValue(const UnboxedLayout::Property &property)
|
||||||
|
{
|
||||||
|
uint8_t *p = &data_[property.offset];
|
||||||
|
|
||||||
|
switch (property.type) {
|
||||||
|
case JSVAL_TYPE_BOOLEAN:
|
||||||
|
return BooleanValue(*p != 0);
|
||||||
|
|
||||||
|
case JSVAL_TYPE_INT32:
|
||||||
|
return Int32Value(*reinterpret_cast<int32_t*>(p));
|
||||||
|
|
||||||
|
case JSVAL_TYPE_DOUBLE:
|
||||||
|
return DoubleValue(*reinterpret_cast<double*>(p));
|
||||||
|
|
||||||
|
case JSVAL_TYPE_STRING:
|
||||||
|
return StringValue(*reinterpret_cast<JSString**>(p));
|
||||||
|
|
||||||
|
case JSVAL_TYPE_OBJECT:
|
||||||
|
return ObjectOrNullValue(*reinterpret_cast<JSObject**>(p));
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid type for unboxed value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj)
|
||||||
|
{
|
||||||
|
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||||
|
const int32_t *list = layout.traceList();
|
||||||
|
if (!list)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t *data = obj->as<UnboxedPlainObject>().data();
|
||||||
|
while (*list != -1) {
|
||||||
|
HeapPtrString *heap = reinterpret_cast<HeapPtrString *>(data + *list);
|
||||||
|
MarkString(trc, heap, "unboxed_string");
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
while (*list != -1) {
|
||||||
|
HeapPtrObject *heap = reinterpret_cast<HeapPtrObject *>(data + *list);
|
||||||
|
if (*heap)
|
||||||
|
MarkObject(trc, heap, "unboxed_object");
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unboxed objects don't have Values to trace.
|
||||||
|
MOZ_ASSERT(*(list + 1) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
UnboxedPlainObject::convertToNative(JSContext *cx)
|
||||||
|
{
|
||||||
|
// Immediately clear any new script on this object's type,
|
||||||
|
// as rollbackPartiallyInitializedObjects() will be confused by the type
|
||||||
|
// changes we make in this function.
|
||||||
|
type()->clearNewScript(cx);
|
||||||
|
|
||||||
|
// clearNewScript() can reentrantly invoke this method.
|
||||||
|
if (!is<UnboxedPlainObject>())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Rooted<UnboxedPlainObject *> obj(cx, this);
|
||||||
|
Rooted<TaggedProto> proto(cx, getTaggedProto());
|
||||||
|
|
||||||
|
size_t nfixed = gc::GetGCKindSlots(obj->layout().getAllocKind());
|
||||||
|
|
||||||
|
AutoValueVector values(cx);
|
||||||
|
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto,
|
||||||
|
getMetadata(), getParent(), nfixed,
|
||||||
|
lastProperty()->getObjectFlags()));
|
||||||
|
if (!shape)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < obj->layout().properties().length(); i++) {
|
||||||
|
const UnboxedLayout::Property &property = obj->layout().properties()[i];
|
||||||
|
|
||||||
|
if (!values.append(obj->getValue(property)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
StackShape unrootedChild(shape->base()->unowned(), NameToId(property.name), i,
|
||||||
|
JSPROP_ENUMERATE, 0);
|
||||||
|
RootedGeneric<StackShape*> child(cx, &unrootedChild);
|
||||||
|
shape = cx->compartment()->propertyTree.getChild(cx, shape, *child);
|
||||||
|
if (!shape)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetClassAndProto(cx, obj, &PlainObject::class_, proto))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Any failures after this point will leave the object as a mutant, and we
|
||||||
|
// can't recover.
|
||||||
|
|
||||||
|
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
|
||||||
|
if (!nobj->setLastProperty(cx, nobj, shape))
|
||||||
|
CrashAtUnhandlableOOM("UnboxedPlainObject::convertToNative");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < values.length(); i++)
|
||||||
|
nobj->initSlot(i, values[i]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
UnboxedPlainObject *
|
||||||
|
UnboxedPlainObject::create(JSContext *cx, HandleTypeObject type, NewObjectKind newKind)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(type->clasp() == &class_);
|
||||||
|
gc::AllocKind allocKind = type->unboxedLayout().getAllocKind();
|
||||||
|
|
||||||
|
UnboxedPlainObject *res = NewObjectWithType<UnboxedPlainObject>(cx, type, cx->global(),
|
||||||
|
allocKind, newKind);
|
||||||
|
if (!res)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Initialize reference fields of the object. All fields in the object will
|
||||||
|
// be overwritten shortly, but references need to be safe for the GC.
|
||||||
|
const int32_t *list = res->layout().traceList();
|
||||||
|
if (list) {
|
||||||
|
uint8_t *data = res->data();
|
||||||
|
while (*list != -1) {
|
||||||
|
HeapPtrString *heap = reinterpret_cast<HeapPtrString *>(data + *list);
|
||||||
|
heap->init(cx->names().empty);
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
while (*list != -1) {
|
||||||
|
HeapPtrObject *heap = reinterpret_cast<HeapPtrObject *>(data + *list);
|
||||||
|
heap->init(nullptr);
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
// Unboxed objects don't have Values to initialize.
|
||||||
|
MOZ_ASSERT(*(list + 1) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_lookupGeneric(JSContext *cx, HandleObject obj,
|
||||||
|
HandleId id, MutableHandleObject objp,
|
||||||
|
MutableHandleShape propp)
|
||||||
|
{
|
||||||
|
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
|
||||||
|
MarkNonNativePropertyFound(propp);
|
||||||
|
objp.set(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedObject proto(cx, obj->getProto());
|
||||||
|
if (!proto) {
|
||||||
|
objp.set(nullptr);
|
||||||
|
propp.set(nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LookupProperty(cx, proto, id, objp, propp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj,
|
||||||
|
HandlePropertyName name,
|
||||||
|
MutableHandleObject objp,
|
||||||
|
MutableHandleShape propp)
|
||||||
|
{
|
||||||
|
RootedId id(cx, NameToId(name));
|
||||||
|
return obj_lookupGeneric(cx, obj, id, objp, propp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_lookupElement(JSContext *cx, HandleObject obj,
|
||||||
|
uint32_t index, MutableHandleObject objp,
|
||||||
|
MutableHandleShape propp)
|
||||||
|
{
|
||||||
|
RootedId id(cx);
|
||||||
|
if (!IndexToId(cx, index, &id))
|
||||||
|
return false;
|
||||||
|
return obj_lookupGeneric(cx, obj, id, objp, propp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
|
||||||
|
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||||
|
{
|
||||||
|
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return DefineProperty(cx, obj, id, v, getter, setter, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_defineProperty(JSContext *cx, HandleObject obj,
|
||||||
|
HandlePropertyName name, HandleValue v,
|
||||||
|
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||||
|
{
|
||||||
|
Rooted<jsid> id(cx, NameToId(name));
|
||||||
|
return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
|
||||||
|
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||||
|
{
|
||||||
|
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
|
||||||
|
RootedId id(cx);
|
||||||
|
if (!IndexToId(cx, index, &id))
|
||||||
|
return false;
|
||||||
|
return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||||
|
HandleId id, MutableHandleValue vp)
|
||||||
|
{
|
||||||
|
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||||
|
|
||||||
|
if (const UnboxedLayout::Property *property = layout.lookup(id)) {
|
||||||
|
vp.set(obj->as<UnboxedPlainObject>().getValue(*property));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedObject proto(cx, obj->getProto());
|
||||||
|
if (!proto) {
|
||||||
|
vp.setUndefined();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetProperty(cx, proto, receiver, id, vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||||
|
HandlePropertyName name, MutableHandleValue vp)
|
||||||
|
{
|
||||||
|
RootedId id(cx, NameToId(name));
|
||||||
|
return obj_getGeneric(cx, obj, receiver, id, vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||||
|
uint32_t index, MutableHandleValue vp)
|
||||||
|
{
|
||||||
|
RootedId id(cx);
|
||||||
|
if (!IndexToId(cx, index, &id))
|
||||||
|
return false;
|
||||||
|
return obj_getGeneric(cx, obj, receiver, id, vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||||
|
MutableHandleValue vp, bool strict)
|
||||||
|
{
|
||||||
|
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||||
|
|
||||||
|
if (const UnboxedLayout::Property *property = layout.lookup(id)) {
|
||||||
|
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, vp))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
return SetProperty(cx, obj, obj, id, vp, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedObject proto(cx, obj->getProto());
|
||||||
|
if (!proto) {
|
||||||
|
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
return SetProperty(cx, obj, obj, id, vp, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SetProperty(cx, proto, obj, id, vp, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||||
|
MutableHandleValue vp, bool strict)
|
||||||
|
{
|
||||||
|
RootedId id(cx, NameToId(name));
|
||||||
|
return obj_setGeneric(cx, obj, id, vp, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||||
|
MutableHandleValue vp, bool strict)
|
||||||
|
{
|
||||||
|
RootedId id(cx);
|
||||||
|
if (!IndexToId(cx, index, &id))
|
||||||
|
return false;
|
||||||
|
return obj_setGeneric(cx, obj, id, vp, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
|
||||||
|
MutableHandle<JSPropertyDescriptor> desc)
|
||||||
|
{
|
||||||
|
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||||
|
|
||||||
|
if (const UnboxedLayout::Property *property = layout.lookup(id)) {
|
||||||
|
desc.value().set(obj->as<UnboxedPlainObject>().getValue(*property));
|
||||||
|
desc.setAttributes(JSPROP_ENUMERATE);
|
||||||
|
desc.object().set(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc.object().set(nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
|
||||||
|
HandleId id, unsigned *attrsp)
|
||||||
|
{
|
||||||
|
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
return SetPropertyAttributes(cx, obj, id, attrsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
|
||||||
|
{
|
||||||
|
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
return DeleteProperty(cx, obj, id, succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable)
|
||||||
|
{
|
||||||
|
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||||
|
return false;
|
||||||
|
return WatchProperty(cx, obj, id, callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
UnboxedPlainObject::obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties)
|
||||||
|
{
|
||||||
|
const UnboxedLayout::PropertyVector &unboxed = obj->as<UnboxedPlainObject>().layout().properties();
|
||||||
|
for (size_t i = 0; i < unboxed.length(); i++) {
|
||||||
|
if (!properties.append(NameToId(unboxed[i].name)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Class UnboxedPlainObject::class_ = {
|
||||||
|
"Object",
|
||||||
|
Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS,
|
||||||
|
nullptr, /* addProperty */
|
||||||
|
nullptr, /* delProperty */
|
||||||
|
nullptr, /* getProperty */
|
||||||
|
nullptr, /* setProperty */
|
||||||
|
nullptr, /* enumerate */
|
||||||
|
nullptr, /* resolve */
|
||||||
|
nullptr, /* convert */
|
||||||
|
nullptr, /* finalize */
|
||||||
|
nullptr, /* call */
|
||||||
|
nullptr, /* hasInstance */
|
||||||
|
nullptr, /* construct */
|
||||||
|
UnboxedPlainObject::trace,
|
||||||
|
JS_NULL_CLASS_SPEC,
|
||||||
|
JS_NULL_CLASS_EXT,
|
||||||
|
{
|
||||||
|
UnboxedPlainObject::obj_lookupGeneric,
|
||||||
|
UnboxedPlainObject::obj_lookupProperty,
|
||||||
|
UnboxedPlainObject::obj_lookupElement,
|
||||||
|
UnboxedPlainObject::obj_defineGeneric,
|
||||||
|
UnboxedPlainObject::obj_defineProperty,
|
||||||
|
UnboxedPlainObject::obj_defineElement,
|
||||||
|
UnboxedPlainObject::obj_getGeneric,
|
||||||
|
UnboxedPlainObject::obj_getProperty,
|
||||||
|
UnboxedPlainObject::obj_getElement,
|
||||||
|
UnboxedPlainObject::obj_setGeneric,
|
||||||
|
UnboxedPlainObject::obj_setProperty,
|
||||||
|
UnboxedPlainObject::obj_setElement,
|
||||||
|
UnboxedPlainObject::obj_getOwnPropertyDescriptor,
|
||||||
|
UnboxedPlainObject::obj_setGenericAttributes,
|
||||||
|
UnboxedPlainObject::obj_deleteGeneric,
|
||||||
|
UnboxedPlainObject::obj_watch,
|
||||||
|
nullptr, /* No unwatch needed, as watch() converts the object to native */
|
||||||
|
nullptr, /* getElements */
|
||||||
|
UnboxedPlainObject::obj_enumerate,
|
||||||
|
nullptr, /* thisObject */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static bool
|
||||||
|
UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype)
|
||||||
|
{
|
||||||
|
if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32)
|
||||||
|
return true;
|
||||||
|
if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
|
||||||
|
types::TypeObject *type, types::PreliminaryObjectArray *objects)
|
||||||
|
{
|
||||||
|
if (!cx->runtime()->options().unboxedObjects())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (templateShape->slotSpan() == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
UnboxedLayout::PropertyVector properties;
|
||||||
|
if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t objectCount = 0;
|
||||||
|
for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) {
|
||||||
|
JSObject *obj = objects->get(i);
|
||||||
|
if (!obj)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
objectCount++;
|
||||||
|
|
||||||
|
// All preliminary objects must have been created with the largest
|
||||||
|
// allocation kind possible, which will allow their unboxed data to be
|
||||||
|
// filled in inline.
|
||||||
|
MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) ==
|
||||||
|
NativeObject::MAX_FIXED_SLOTS);
|
||||||
|
|
||||||
|
if (obj->as<PlainObject>().lastProperty() != templateShape ||
|
||||||
|
obj->as<PlainObject>().hasDynamicElements())
|
||||||
|
{
|
||||||
|
// Only use an unboxed representation if all created objects match
|
||||||
|
// the template shape exactly.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < templateShape->slotSpan(); i++) {
|
||||||
|
Value val = obj->as<PlainObject>().getSlot(i);
|
||||||
|
|
||||||
|
JSValueType &existing = properties[i].type;
|
||||||
|
JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType();
|
||||||
|
|
||||||
|
if (existing == JSVAL_TYPE_MAGIC || existing == type || UnboxedTypeIncludes(type, existing))
|
||||||
|
existing = type;
|
||||||
|
else if (!UnboxedTypeIncludes(existing, type))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectCount <= 1) {
|
||||||
|
// If only one of the objects has been created, it is more likely to
|
||||||
|
// have new properties added later.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < templateShape->slotSpan(); i++) {
|
||||||
|
// We can't use an unboxed representation if e.g. all the objects have
|
||||||
|
// a null value for one of the properties, as we can't decide what type
|
||||||
|
// it is supposed to have.
|
||||||
|
if (UnboxedTypeSize(properties[i].type) == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the names for all the object's properties.
|
||||||
|
for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
|
||||||
|
size_t slot = r.front().slot();
|
||||||
|
MOZ_ASSERT(!properties[slot].name);
|
||||||
|
properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in all the unboxed object's property offsets, ordering fields from the
|
||||||
|
// largest down to avoid alignment issues.
|
||||||
|
uint32_t offset = 0;
|
||||||
|
|
||||||
|
static const size_t typeSizes[] = { 8, 4, 1 };
|
||||||
|
|
||||||
|
Vector<int32_t, 8, SystemAllocPolicy> objectOffsets, stringOffsets;
|
||||||
|
|
||||||
|
DebugOnly<size_t> addedProperties = 0;
|
||||||
|
for (size_t i = 0; i < ArrayLength(typeSizes); i++) {
|
||||||
|
size_t size = typeSizes[i];
|
||||||
|
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
|
||||||
|
JSValueType type = properties[j].type;
|
||||||
|
if (UnboxedTypeSize(type) == size) {
|
||||||
|
if (type == JSVAL_TYPE_OBJECT) {
|
||||||
|
if (!objectOffsets.append(offset))
|
||||||
|
return false;
|
||||||
|
} else if (type == JSVAL_TYPE_STRING) {
|
||||||
|
if (!stringOffsets.append(offset))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
addedProperties++;
|
||||||
|
properties[j].offset = offset;
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(addedProperties == templateShape->slotSpan());
|
||||||
|
|
||||||
|
// The entire object must be allocatable inline.
|
||||||
|
if (sizeof(JSObject) + offset > JSObject::MAX_BYTE_SIZE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
UnboxedLayout *layout = type->zone()->new_<UnboxedLayout>(properties, offset);
|
||||||
|
if (!layout)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Construct the layout's trace list.
|
||||||
|
if (!objectOffsets.empty() || !stringOffsets.empty()) {
|
||||||
|
Vector<int32_t, 8, SystemAllocPolicy> entries;
|
||||||
|
if (!entries.appendAll(stringOffsets) ||
|
||||||
|
!entries.append(-1) ||
|
||||||
|
!entries.appendAll(objectOffsets) ||
|
||||||
|
!entries.append(-1) ||
|
||||||
|
!entries.append(-1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int32_t *traceList = type->zone()->pod_malloc<int32_t>(entries.length());
|
||||||
|
if (!traceList)
|
||||||
|
return false;
|
||||||
|
PodCopy(traceList, entries.begin(), entries.length());
|
||||||
|
layout->setTraceList(traceList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've determined that all the preliminary objects can use the new layout
|
||||||
|
// just constructed, so convert the existing type to be an
|
||||||
|
// UnboxedPlainObject rather than a PlainObject, and update the preliminary
|
||||||
|
// objects to use the new layout. Do the fallible stuff first before
|
||||||
|
// modifying any objects.
|
||||||
|
|
||||||
|
// Get an empty shape which we can use for the preliminary objects.
|
||||||
|
Shape *newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_,
|
||||||
|
type->proto(),
|
||||||
|
templateShape->getObjectMetadata(),
|
||||||
|
templateShape->getObjectParent(),
|
||||||
|
templateShape->getObjectFlags());
|
||||||
|
if (!newShape) {
|
||||||
|
cx->clearPendingException();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate a list of all the properties in each preliminary object, and
|
||||||
|
// update their shapes.
|
||||||
|
Vector<Value, 0, SystemAllocPolicy> values;
|
||||||
|
if (!values.reserve(objectCount * templateShape->slotSpan()))
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) {
|
||||||
|
if (!objects->get(i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
RootedNativeObject obj(cx, &objects->get(i)->as<NativeObject>());
|
||||||
|
for (size_t j = 0; j < templateShape->slotSpan(); j++)
|
||||||
|
values.infallibleAppend(obj->getSlot(j));
|
||||||
|
|
||||||
|
// Clear the object to remove any dynamically allocated information.
|
||||||
|
NativeObject::clear(cx, obj);
|
||||||
|
|
||||||
|
obj->setLastPropertyMakeNonNative(newShape);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types::TypeNewScript *newScript = type->newScript())
|
||||||
|
layout->setNewScript(newScript);
|
||||||
|
|
||||||
|
type->setClasp(&UnboxedPlainObject::class_);
|
||||||
|
type->setUnboxedLayout(layout);
|
||||||
|
|
||||||
|
size_t valueCursor = 0;
|
||||||
|
for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) {
|
||||||
|
if (!objects->get(i))
|
||||||
|
continue;
|
||||||
|
UnboxedPlainObject *obj = &objects->get(i)->as<UnboxedPlainObject>();
|
||||||
|
memset(obj->data(), 0, layout->size());
|
||||||
|
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
|
||||||
|
Value v = values[valueCursor++];
|
||||||
|
JS_ALWAYS_TRUE(obj->setValue(cx, properties[j], v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(valueCursor == values.length());
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef vm_UnboxedObject_h
|
||||||
|
#define vm_UnboxedObject_h
|
||||||
|
|
||||||
|
#include "jsgc.h"
|
||||||
|
#include "jsinfer.h"
|
||||||
|
#include "jsobj.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
// Memory required for an unboxed value of a given type. Returns zero for types
|
||||||
|
// which can't be used for unboxed objects.
|
||||||
|
static inline size_t
|
||||||
|
UnboxedTypeSize(JSValueType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case JSVAL_TYPE_BOOLEAN: return 1;
|
||||||
|
case JSVAL_TYPE_INT32: return 4;
|
||||||
|
case JSVAL_TYPE_DOUBLE: return 8;
|
||||||
|
case JSVAL_TYPE_STRING: return sizeof(void *);
|
||||||
|
case JSVAL_TYPE_OBJECT: return sizeof(void *);
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class describing the layout of an UnboxedPlainObject.
|
||||||
|
class UnboxedLayout
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Property {
|
||||||
|
PropertyName *name;
|
||||||
|
uint32_t offset;
|
||||||
|
JSValueType type;
|
||||||
|
|
||||||
|
Property()
|
||||||
|
: name(nullptr), offset(0), type(JSVAL_TYPE_MAGIC)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// All properties on objects with this layout, in enumeration order.
|
||||||
|
PropertyVector properties_;
|
||||||
|
|
||||||
|
// Byte size of the data for objects with this layout.
|
||||||
|
size_t size_;
|
||||||
|
|
||||||
|
// Any 'new' script information associated with this layout.
|
||||||
|
types::TypeNewScript *newScript_;
|
||||||
|
|
||||||
|
// List for use in tracing objects with this layout. This has the same
|
||||||
|
// structure as the trace list on a TypeDescr.
|
||||||
|
int32_t *traceList_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UnboxedLayout(const PropertyVector &properties, size_t size)
|
||||||
|
: size_(size), newScript_(nullptr), traceList_(nullptr)
|
||||||
|
{
|
||||||
|
properties_.appendAll(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
~UnboxedLayout() {
|
||||||
|
js_delete(newScript_);
|
||||||
|
js_free(traceList_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PropertyVector &properties() const {
|
||||||
|
return properties_;
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeNewScript *newScript() const {
|
||||||
|
return newScript_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNewScript(types::TypeNewScript *newScript, bool writeBarrier = true);
|
||||||
|
|
||||||
|
const int32_t *traceList() const {
|
||||||
|
return traceList_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTraceList(int32_t *traceList) {
|
||||||
|
traceList_ = traceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Property *lookup(JSAtom *atom) const {
|
||||||
|
for (size_t i = 0; i < properties_.length(); i++) {
|
||||||
|
if (properties_[i].name == atom)
|
||||||
|
return &properties_[i];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Property *lookup(jsid id) const {
|
||||||
|
if (JSID_IS_STRING(id))
|
||||||
|
return lookup(JSID_TO_ATOM(id));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline gc::AllocKind getAllocKind() const;
|
||||||
|
|
||||||
|
void trace(JSTracer *trc);
|
||||||
|
|
||||||
|
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class for a plain object using an unboxed representation. The physical
|
||||||
|
// layout of these objects is identical to that of an InlineTypedObject, though
|
||||||
|
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
|
||||||
|
// how their properties are stored.
|
||||||
|
class UnboxedPlainObject : public JSObject
|
||||||
|
{
|
||||||
|
// Start of the inline data, which immediately follows the shape and type.
|
||||||
|
uint8_t data_[1];
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const Class class_;
|
||||||
|
|
||||||
|
static bool obj_lookupGeneric(JSContext *cx, HandleObject obj,
|
||||||
|
HandleId id, MutableHandleObject objp,
|
||||||
|
MutableHandleShape propp);
|
||||||
|
|
||||||
|
static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
|
||||||
|
HandlePropertyName name,
|
||||||
|
MutableHandleObject objp,
|
||||||
|
MutableHandleShape propp);
|
||||||
|
|
||||||
|
static bool obj_lookupElement(JSContext *cx, HandleObject obj,
|
||||||
|
uint32_t index, MutableHandleObject objp,
|
||||||
|
MutableHandleShape propp);
|
||||||
|
|
||||||
|
static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
|
||||||
|
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
|
||||||
|
|
||||||
|
static bool obj_defineProperty(JSContext *cx, HandleObject obj,
|
||||||
|
HandlePropertyName name, HandleValue v,
|
||||||
|
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
|
||||||
|
|
||||||
|
static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
|
||||||
|
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
|
||||||
|
|
||||||
|
static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||||
|
HandleId id, MutableHandleValue vp);
|
||||||
|
|
||||||
|
static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||||
|
HandlePropertyName name, MutableHandleValue vp);
|
||||||
|
|
||||||
|
static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||||
|
uint32_t index, MutableHandleValue vp);
|
||||||
|
|
||||||
|
static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||||
|
MutableHandleValue vp, bool strict);
|
||||||
|
static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||||
|
MutableHandleValue vp, bool strict);
|
||||||
|
static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||||
|
MutableHandleValue vp, bool strict);
|
||||||
|
|
||||||
|
static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
|
||||||
|
MutableHandle<JSPropertyDescriptor> desc);
|
||||||
|
|
||||||
|
static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
|
||||||
|
HandleId id, unsigned *attrsp);
|
||||||
|
|
||||||
|
static bool obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
|
||||||
|
|
||||||
|
static bool obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties);
|
||||||
|
static bool obj_watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable);
|
||||||
|
|
||||||
|
const UnboxedLayout &layout() const {
|
||||||
|
return type()->unboxedLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *data() {
|
||||||
|
return &data_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v);
|
||||||
|
Value getValue(const UnboxedLayout::Property &property);
|
||||||
|
|
||||||
|
bool convertToNative(JSContext *cx);
|
||||||
|
|
||||||
|
static UnboxedPlainObject *create(JSContext *cx, HandleTypeObject type, NewObjectKind newKind);
|
||||||
|
|
||||||
|
static void trace(JSTracer *trc, JSObject *object);
|
||||||
|
|
||||||
|
static size_t offsetOfData() {
|
||||||
|
return offsetof(UnboxedPlainObject, data_[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to construct an UnboxedLayout for each of the preliminary objects,
|
||||||
|
// provided they all match the template shape. If successful, converts the
|
||||||
|
// preliminary objects and their type to the new unboxed representation.
|
||||||
|
bool
|
||||||
|
TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
|
||||||
|
types::TypeObject *type, types::PreliminaryObjectArray *objects);
|
||||||
|
|
||||||
|
inline gc::AllocKind
|
||||||
|
UnboxedLayout::getAllocKind() const
|
||||||
|
{
|
||||||
|
return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
|
#endif /* vm_UnboxedObject_h */
|
Загрузка…
Ссылка в новой задаче