mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 15:28:39 +09:00
Remove Unboxed Object code from /vm, Part 3.
This commit is contained in:
parent
7ec709fb70
commit
aa89a14709
18 changed files with 13 additions and 1560 deletions
|
|
@ -45,7 +45,6 @@
|
|||
#include "vm/Caches-inl.h"
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
|
|
|||
|
|
@ -529,9 +529,6 @@ struct JSCompartment
|
|||
// table manages references from such typed objects to their buffers.
|
||||
js::ObjectWeakMap* lazyArrayBuffers;
|
||||
|
||||
// All unboxed layouts in the compartment.
|
||||
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
|
||||
|
||||
// WebAssembly state for the compartment.
|
||||
js::wasm::Compartment wasm;
|
||||
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls)
|
|||
if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
|
||||
return Proxy::getBuiltinClass(cx, obj, cls);
|
||||
|
||||
if (obj->is<PlainObject>() || obj->is<UnboxedPlainObject>())
|
||||
if (obj->is<PlainObject>())
|
||||
*cls = ESClass::Object;
|
||||
else if (obj->is<ArrayObject>())
|
||||
*cls = ESClass::Array;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@
|
|||
#include "vm/RegExpStaticsObject.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/TypedArrayCommon.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsboolinlines.h"
|
||||
|
|
|
|||
|
|
@ -32,19 +32,6 @@
|
|||
#include "vm/ShapedObject-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// This is needed here for ensureShape() below.
|
||||
inline bool
|
||||
MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj)
|
||||
{
|
||||
if (obj->is<UnboxedPlainObject>())
|
||||
return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
inline js::Shape*
|
||||
JSObject::maybeShape() const
|
||||
{
|
||||
|
|
@ -57,8 +44,6 @@ JSObject::maybeShape() const
|
|||
inline js::Shape*
|
||||
JSObject::ensureShape(js::ExclusiveContext* cx)
|
||||
{
|
||||
if (!js::MaybeConvertUnboxedObjectToNative(cx, this))
|
||||
return nullptr;
|
||||
js::Shape* shape = maybeShape();
|
||||
MOZ_ASSERT(shape);
|
||||
return shape;
|
||||
|
|
|
|||
|
|
@ -355,7 +355,6 @@ UNIFIED_SOURCES += [
|
|||
'vm/UbiNode.cpp',
|
||||
'vm/UbiNodeCensus.cpp',
|
||||
'vm/UbiNodeShortestPaths.cpp',
|
||||
'vm/UnboxedObject.cpp',
|
||||
'vm/Unicode.cpp',
|
||||
'vm/Value.cpp',
|
||||
'vm/WeakMapPtr.cpp',
|
||||
|
|
|
|||
|
|
@ -4970,11 +4970,6 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject)
|
|||
|
||||
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
|
||||
|
||||
if (templateObject->group()->maybeUnboxedLayout()) {
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
return UnboxedPlainObject::create(cx, group, newKind);
|
||||
}
|
||||
|
||||
JSObject* obj = CopyInitializerObject(cx, templateObject.as<PlainObject>(), newKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -390,33 +390,6 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape)
|
|||
shape_ = shape;
|
||||
}
|
||||
|
||||
void
|
||||
NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape)
|
||||
{
|
||||
MOZ_ASSERT(getClass()->isNative());
|
||||
MOZ_ASSERT(shape->getObjectClass()->isNative());
|
||||
MOZ_ASSERT(!shape->inDictionary());
|
||||
|
||||
// This method is used to convert unboxed objects into native objects. In
|
||||
// this case, the shape_ field was previously used to store other data and
|
||||
// this should be treated as an initialization.
|
||||
shape_.init(shape);
|
||||
|
||||
slots_ = nullptr;
|
||||
elements_ = emptyObjectElements;
|
||||
|
||||
size_t oldSpan = shape->numFixedSlots();
|
||||
size_t newSpan = shape->slotSpan();
|
||||
|
||||
initializeSlotRange(0, oldSpan);
|
||||
|
||||
// A failure at this point will leave the object as a mutant, and we
|
||||
// can't recover.
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan))
|
||||
oomUnsafe.crash("NativeObject::setLastPropertyMakeNative");
|
||||
}
|
||||
|
||||
bool
|
||||
NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -470,11 +470,6 @@ class NativeObject : public ShapedObject
|
|||
// that are (temporarily) inconsistent.
|
||||
void setLastPropertyMakeNonNative(Shape* shape);
|
||||
|
||||
// As for setLastProperty(), but changes the class associated with the
|
||||
// object to a native one. The object's type has already been changed, and
|
||||
// this brings the shape into sync with it.
|
||||
void setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape);
|
||||
|
||||
// Newly-created TypedArrays that map a SharedArrayBuffer are
|
||||
// marked as shared by giving them an ObjectElements that has the
|
||||
// ObjectElements::SHARED_MEMORY flag set.
|
||||
|
|
|
|||
|
|
@ -108,20 +108,6 @@ ObjectGroup::maybePreliminaryObjects()
|
|||
return maybePreliminaryObjectsDontCheckGeneration();
|
||||
}
|
||||
|
||||
inline UnboxedLayout*
|
||||
ObjectGroup::maybeUnboxedLayout()
|
||||
{
|
||||
maybeSweep(nullptr);
|
||||
return maybeUnboxedLayoutDontCheckGeneration();
|
||||
}
|
||||
|
||||
inline UnboxedLayout&
|
||||
ObjectGroup::unboxedLayout()
|
||||
{
|
||||
maybeSweep(nullptr);
|
||||
return unboxedLayoutDontCheckGeneration();
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* vm_ObjectGroup_inl_h */
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@
|
|||
#include "vm/ArrayObject.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/TaggedProto.h"
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
|
|
@ -56,7 +55,6 @@ ObjectGroup::finalize(FreeOp* fop)
|
|||
if (newScriptDontCheckGeneration())
|
||||
newScriptDontCheckGeneration()->clear();
|
||||
fop->delete_(newScriptDontCheckGeneration());
|
||||
fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
|
||||
if (maybePreliminaryObjectsDontCheckGeneration())
|
||||
maybePreliminaryObjectsDontCheckGeneration()->clear();
|
||||
fop->delete_(maybePreliminaryObjectsDontCheckGeneration());
|
||||
|
|
@ -83,8 +81,6 @@ ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|||
size_t n = 0;
|
||||
if (TypeNewScript* newScript = newScriptDontCheckGeneration())
|
||||
n += newScript->sizeOfIncludingThis(mallocSizeOf);
|
||||
if (UnboxedLayout* layout = maybeUnboxedLayoutDontCheckGeneration())
|
||||
n += layout->sizeOfIncludingThis(mallocSizeOf);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
@ -530,8 +526,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
|
|||
if (p) {
|
||||
ObjectGroup* group = p->group;
|
||||
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
|
||||
MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
|
||||
group->clasp() == &UnboxedPlainObject::class_);
|
||||
MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_);
|
||||
MOZ_ASSERT(group->proto() == proto);
|
||||
return group;
|
||||
}
|
||||
|
|
@ -971,46 +966,6 @@ js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (newObj->is<UnboxedPlainObject>()) {
|
||||
const UnboxedLayout& layout = newObj->as<UnboxedPlainObject>().layout();
|
||||
const int32_t* traceList = layout.traceList();
|
||||
if (!traceList)
|
||||
return true;
|
||||
|
||||
uint8_t* newData = newObj->as<UnboxedPlainObject>().data();
|
||||
uint8_t* oldData = oldObj->as<UnboxedPlainObject>().data();
|
||||
|
||||
for (; *traceList != -1; traceList++) {}
|
||||
traceList++;
|
||||
for (; *traceList != -1; traceList++) {
|
||||
JSObject* newInnerObj = *reinterpret_cast<JSObject**>(newData + *traceList);
|
||||
JSObject* oldInnerObj = *reinterpret_cast<JSObject**>(oldData + *traceList);
|
||||
|
||||
if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj))
|
||||
continue;
|
||||
|
||||
if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
|
||||
return false;
|
||||
|
||||
if (SameGroup(oldInnerObj, newInnerObj))
|
||||
continue;
|
||||
|
||||
if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
|
||||
return false;
|
||||
|
||||
if (SameGroup(oldInnerObj, newInnerObj)) {
|
||||
for (size_t i = 1; i < ncompare; i++) {
|
||||
if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
|
||||
uint8_t* otherData = compare[i].toObject().as<UnboxedPlainObject>().data();
|
||||
JSObject* otherInnerObj = *reinterpret_cast<JSObject**>(otherData + *traceList);
|
||||
if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) {
|
||||
if (!GiveObjectGroup(cx, otherInnerObj, newInnerObj))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -1234,12 +1189,6 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_
|
|||
|
||||
RootedObjectGroup group(cx, p->value().group);
|
||||
|
||||
// Watch for existing groups which now use an unboxed layout.
|
||||
if (group->maybeUnboxedLayout()) {
|
||||
MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
|
||||
return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
|
||||
}
|
||||
|
||||
// Update property types according to the properties we are about to add.
|
||||
// Do this before we do anything which can GC, which might move or remove
|
||||
// this table entry.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
namespace js {
|
||||
|
||||
class TypeDescr;
|
||||
class UnboxedLayout;
|
||||
|
||||
class PreliminaryObjectArrayWithTemplate;
|
||||
class TypeNewScript;
|
||||
|
|
@ -154,11 +153,6 @@ class ObjectGroup : public gc::TenuredCell
|
|||
// For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate.
|
||||
Addendum_PreliminaryObjects,
|
||||
|
||||
// When objects in this group have an unboxed representation, the
|
||||
// addendum stores an UnboxedLayout (which might have a TypeNewScript
|
||||
// as well, if the group is also constructed using 'new').
|
||||
Addendum_UnboxedLayout,
|
||||
|
||||
// If this group is used by objects that have been converted from an
|
||||
// unboxed representation and/or have the same allocation kind as such
|
||||
// objects, the addendum points to that unboxed group.
|
||||
|
|
@ -225,24 +219,6 @@ class ObjectGroup : public gc::TenuredCell
|
|||
maybePreliminaryObjectsDontCheckGeneration();
|
||||
}
|
||||
|
||||
inline UnboxedLayout* maybeUnboxedLayout();
|
||||
inline UnboxedLayout& unboxedLayout();
|
||||
|
||||
UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const {
|
||||
if (addendumKind() == Addendum_UnboxedLayout)
|
||||
return reinterpret_cast<UnboxedLayout*>(addendum_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnboxedLayout& unboxedLayoutDontCheckGeneration() const {
|
||||
MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
|
||||
return *maybeUnboxedLayoutDontCheckGeneration();
|
||||
}
|
||||
|
||||
void setUnboxedLayout(UnboxedLayout* layout) {
|
||||
setAddendum(Addendum_UnboxedLayout, layout);
|
||||
}
|
||||
|
||||
ObjectGroup* maybeOriginalUnboxedGroup() const {
|
||||
if (addendumKind() == Addendum_OriginalUnboxedGroup)
|
||||
return reinterpret_cast<ObjectGroup*>(addendum_);
|
||||
|
|
@ -511,8 +487,8 @@ class ObjectGroup : public gc::TenuredCell
|
|||
NewObjectKind newKind,
|
||||
NewArrayKind arrayKind = NewArrayKind::Normal);
|
||||
|
||||
// Create a PlainObject or UnboxedPlainObject with the specified properties
|
||||
// and a group specialized for those properties.
|
||||
// Create a PlainObject with the specified properties and a group specialized
|
||||
// for those properties.
|
||||
static JSObject* newPlainObject(ExclusiveContext* cx,
|
||||
IdValuePair* properties, size_t nproperties,
|
||||
NewObjectKind newKind);
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj)
|
|||
: group(nullptr), shape(nullptr)
|
||||
{
|
||||
if (obj) {
|
||||
if (obj->is<UnboxedPlainObject>()) {
|
||||
group = obj->group();
|
||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
||||
shape = expando->lastProperty();
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
if (obj->is<TypedObject>()) {
|
||||
group = obj->group();
|
||||
} else {
|
||||
shape = obj->maybeShape();
|
||||
|
|
@ -32,9 +28,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
|
|||
{
|
||||
if (group) {
|
||||
const Class* clasp = group->clasp();
|
||||
if (clasp == &UnboxedPlainObject::class_) {
|
||||
// Keep both group and shape.
|
||||
} else if (IsTypedObjectClass(clasp)) {
|
||||
if (IsTypedObjectClass(clasp)) {
|
||||
this->shape = nullptr;
|
||||
} else {
|
||||
this->group = nullptr;
|
||||
|
|
@ -45,10 +39,6 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
|
|||
/* static */ int32_t
|
||||
HeapReceiverGuard::keyBits(JSObject* obj)
|
||||
{
|
||||
if (obj->is<UnboxedPlainObject>()) {
|
||||
// Both the group and shape need to be guarded for unboxed plain objects.
|
||||
return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
|
||||
}
|
||||
if (obj->is<TypedObject>()) {
|
||||
// Only the group needs to be guarded for typed objects.
|
||||
return 2;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#include "vm/SharedArrayObject.h"
|
||||
#include "vm/StringObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
|
|
@ -285,10 +284,6 @@ TypeIdString(jsid id)
|
|||
*/
|
||||
struct AutoEnterAnalysis
|
||||
{
|
||||
// For use when initializing an UnboxedLayout. The UniquePtr's destructor
|
||||
// must run when GC is not suppressed.
|
||||
UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp;
|
||||
|
||||
// Prevent GC activity in the middle of analysis.
|
||||
gc::AutoSuppressGC suppressGC;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
#include "vm/Opcodes.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/Time.h"
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
|
@ -297,9 +296,6 @@ js::ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Val
|
|||
return true;
|
||||
}
|
||||
}
|
||||
JSObject* obj = &value.toObject();
|
||||
if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!types->hasType(type)) {
|
||||
|
|
@ -1944,33 +1940,6 @@ class ConstraintDataFreezeObjectForTypedArrayData
|
|||
}
|
||||
};
|
||||
|
||||
// Constraint which triggers recompilation if an unboxed object in some group
|
||||
// is converted to a native object.
|
||||
class ConstraintDataFreezeObjectForUnboxedConvertedToNative
|
||||
{
|
||||
public:
|
||||
ConstraintDataFreezeObjectForUnboxedConvertedToNative()
|
||||
{}
|
||||
|
||||
const char* kind() { return "freezeObjectForUnboxedConvertedToNative"; }
|
||||
|
||||
bool invalidateOnNewType(TypeSet::Type type) { return false; }
|
||||
bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
|
||||
bool invalidateOnNewObjectState(ObjectGroup* group) {
|
||||
return group->unboxedLayout().nativeGroup() != nullptr;
|
||||
}
|
||||
|
||||
bool constraintHolds(JSContext* cx,
|
||||
const HeapTypeSetKey& property, TemporaryTypeSet* expected)
|
||||
{
|
||||
return !invalidateOnNewObjectState(property.object()->maybeGroup());
|
||||
}
|
||||
|
||||
bool shouldSweep() { return false; }
|
||||
|
||||
JSCompartment* maybeCompartment() { return nullptr; }
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
void
|
||||
|
|
@ -2505,8 +2474,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
|
|||
bool
|
||||
js::ClassCanHaveExtraProperties(const Class* clasp)
|
||||
{
|
||||
if (clasp == &UnboxedPlainObject::class_)
|
||||
return false;
|
||||
return clasp->getResolve()
|
||||
|| clasp->getOpsLookupProperty()
|
||||
|| clasp->getOpsGetProperty()
|
||||
|
|
@ -2805,15 +2772,6 @@ js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, j
|
|||
// from acquiring the fully initialized group.
|
||||
if (group->newScript() && group->newScript()->initializedGroup())
|
||||
AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type);
|
||||
|
||||
// Maintain equivalent type information for unboxed object groups and their
|
||||
// corresponding native group. Since type sets might contain the unboxed
|
||||
// group but not the native group, this ensures optimizations based on the
|
||||
// unboxed group are valid for the native group.
|
||||
if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup())
|
||||
AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type);
|
||||
if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
|
||||
AddTypePropertyId(cx, unboxedGroup, nullptr, id, type);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2885,12 +2843,6 @@ ObjectGroup::setFlags(ExclusiveContext* cx, ObjectGroupFlags flags)
|
|||
// acquired properties analysis.
|
||||
if (newScript() && newScript()->initializedGroup())
|
||||
newScript()->initializedGroup()->setFlags(cx, flags);
|
||||
|
||||
// Propagate flag changes between unboxed and corresponding native groups.
|
||||
if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
|
||||
maybeUnboxedLayout()->nativeGroup()->setFlags(cx, flags);
|
||||
if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
|
||||
unboxedGroup->setFlags(cx, flags);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2923,13 +2875,6 @@ ObjectGroup::markUnknown(ExclusiveContext* cx)
|
|||
prop->types.setNonDataProperty(cx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
|
||||
MarkObjectGroupUnknownProperties(cx, unboxedGroup);
|
||||
if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
|
||||
MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup());
|
||||
if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
|
||||
MarkObjectGroupUnknownProperties(cx, unboxedGroup);
|
||||
}
|
||||
|
||||
TypeNewScript*
|
||||
|
|
@ -2937,8 +2882,6 @@ ObjectGroup::anyNewScript()
|
|||
{
|
||||
if (newScript())
|
||||
return newScript();
|
||||
if (maybeUnboxedLayout())
|
||||
return unboxedLayout().newScript();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -2968,10 +2911,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
|
|||
MOZ_ASSERT(!replacement);
|
||||
}
|
||||
|
||||
if (this->newScript())
|
||||
setAddendum(Addendum_None, nullptr, writeBarrier);
|
||||
else
|
||||
unboxedLayout().setNewScript(nullptr, writeBarrier);
|
||||
setAddendum(Addendum_None, nullptr, writeBarrier);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -3451,22 +3391,6 @@ PreliminaryObjectArray::sweep()
|
|||
for (size_t i = 0; i < COUNT; i++) {
|
||||
JSObject** ptr = &objects[i];
|
||||
if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
|
||||
// Before we clear this reference, change the object's group to the
|
||||
// Object.prototype group. This is done to ensure JSObject::finalize
|
||||
// sees a NativeObject Class even if we change the current group's
|
||||
// Class to one of the unboxed object classes in the meantime. If
|
||||
// the compartment's global is dead, we don't do anything as the
|
||||
// group's Class is not going to change in that case.
|
||||
JSObject* obj = *ptr;
|
||||
GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal();
|
||||
if (global && !obj->isSingleton()) {
|
||||
JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object);
|
||||
obj->setGroup(objectProto->groupRaw());
|
||||
MOZ_ASSERT(obj->is<NativeObject>());
|
||||
MOZ_ASSERT(obj->getClass() == objectProto->getClass());
|
||||
MOZ_ASSERT(!obj->getClass()->hasFinalize());
|
||||
}
|
||||
|
||||
*ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -3566,16 +3490,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro
|
|||
}
|
||||
}
|
||||
|
||||
if (group->maybeUnboxedLayout())
|
||||
return;
|
||||
|
||||
if (shape()) {
|
||||
// We weren't able to use an unboxed layout, but since the preliminary
|
||||
// objects still reflect the template object's properties, and all
|
||||
// objects in the future will be created with those properties, the
|
||||
// properties can be marked as definite for objects in the group.
|
||||
group->addDefiniteProperties(cx, shape());
|
||||
}
|
||||
// Since the preliminary objects still reflect the template object's
|
||||
// properties, and all objects in the future will be created with those
|
||||
// properties, the properties can be marked as definitive for objects in
|
||||
// the group.
|
||||
group->addDefiniteProperties(cx, shape());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -3589,7 +3508,6 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun)
|
|||
{
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
MOZ_ASSERT(!group->newScript());
|
||||
MOZ_ASSERT(!group->maybeUnboxedLayout());
|
||||
|
||||
// rollbackPartiallyInitializedObjects expects function_ to be
|
||||
// canonicalized.
|
||||
|
|
@ -3852,27 +3770,6 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
|
|||
js_delete(preliminaryObjects);
|
||||
preliminaryObjects = nullptr;
|
||||
|
||||
if (group->maybeUnboxedLayout()) {
|
||||
// An unboxed layout was constructed for the group, and this has already
|
||||
// been hooked into it.
|
||||
MOZ_ASSERT(group->unboxedLayout().newScript() == this);
|
||||
destroyNewScript.group = nullptr;
|
||||
|
||||
// Clear out the template object, which is not used for TypeNewScripts
|
||||
// with an unboxed layout. Currently it is a mutant object with a
|
||||
// non-native group and native shape, so make it safe for GC by changing
|
||||
// its group to the default for its prototype.
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
ObjectGroup* plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
|
||||
group->proto());
|
||||
if (!plainGroup)
|
||||
oomUnsafe.crash("TypeNewScript::maybeAnalyze");
|
||||
templateObject_->setGroup(plainGroup);
|
||||
templateObject_ = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
|
||||
// The definite properties analysis found exactly the properties that
|
||||
// are held in common by the preliminary objects. No further analysis
|
||||
|
|
@ -3968,12 +3865,6 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
|
|||
continue;
|
||||
}
|
||||
|
||||
if (thisv.toObject().is<UnboxedPlainObject>()) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
|
||||
oomUnsafe.crash("rollbackPartiallyInitializedObjects");
|
||||
}
|
||||
|
||||
// Found a matching frame.
|
||||
RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
|
||||
|
||||
|
|
@ -4167,12 +4058,6 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
|
|||
// Object sets containing objects with unknown properties might
|
||||
// not be complete. Mark the type set as unknown, which it will
|
||||
// be treated as during Ion compilation.
|
||||
//
|
||||
// Note that we don't have to do this when the type set might
|
||||
// be missing the native group corresponding to an unboxed
|
||||
// object group. In this case, the native group points to the
|
||||
// unboxed object group via its addendum, so as long as objects
|
||||
// with either group exist, neither group will be finalized.
|
||||
flags |= TYPE_FLAG_ANYOBJECT;
|
||||
clearObjects();
|
||||
objectCount = 0;
|
||||
|
|
@ -4256,21 +4141,6 @@ ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom)
|
|||
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
||||
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
||||
|
||||
if (maybeUnboxedLayout()) {
|
||||
// Remove unboxed layouts that are about to be finalized from the
|
||||
// compartment wide list while we are still on the main thread.
|
||||
ObjectGroup* group = this;
|
||||
if (IsAboutToBeFinalizedUnbarriered(&group))
|
||||
unboxedLayout().detachFromCompartment();
|
||||
|
||||
if (unboxedLayout().newScript())
|
||||
unboxedLayout().newScript()->sweep();
|
||||
|
||||
// Discard constructor code to avoid holding onto ExecutablePools.
|
||||
if (zone()->isGCCompacting())
|
||||
unboxedLayout().setConstructorCode(nullptr);
|
||||
}
|
||||
|
||||
if (maybePreliminaryObjects())
|
||||
maybePreliminaryObjects()->sweep();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,177 +0,0 @@
|
|||
/* -*- 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_inl_h
|
||||
#define vm_UnboxedObject_inl_h
|
||||
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
#include "gc/StoreBuffer-inl.h"
|
||||
#include "vm/ArrayObject-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
static inline Value
|
||||
GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized)
|
||||
{
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
return BooleanValue(*p != 0);
|
||||
|
||||
case JSVAL_TYPE_INT32:
|
||||
return Int32Value(*reinterpret_cast<int32_t*>(p));
|
||||
|
||||
case JSVAL_TYPE_DOUBLE: {
|
||||
// During unboxed plain object creation, non-GC thing properties are
|
||||
// left uninitialized. This is normally fine, since the properties will
|
||||
// be filled in shortly, but if they are read before that happens we
|
||||
// need to make sure that doubles are canonical.
|
||||
double d = *reinterpret_cast<double*>(p);
|
||||
if (maybeUninitialized)
|
||||
return DoubleValue(JS::CanonicalizeNaN(d));
|
||||
return DoubleValue(d);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetUnboxedValueNoTypeChange(JSObject* unboxedObject,
|
||||
uint8_t* p, JSValueType type, const Value& v,
|
||||
bool preBarrier)
|
||||
{
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
*p = v.toBoolean();
|
||||
return;
|
||||
|
||||
case JSVAL_TYPE_INT32:
|
||||
*reinterpret_cast<int32_t*>(p) = v.toInt32();
|
||||
return;
|
||||
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
*reinterpret_cast<double*>(p) = v.toNumber();
|
||||
return;
|
||||
|
||||
case JSVAL_TYPE_STRING: {
|
||||
MOZ_ASSERT(!IsInsideNursery(v.toString()));
|
||||
JSString** np = reinterpret_cast<JSString**>(p);
|
||||
if (preBarrier)
|
||||
JSString::writeBarrierPre(*np);
|
||||
*np = v.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
case JSVAL_TYPE_OBJECT: {
|
||||
JSObject** np = reinterpret_cast<JSObject**>(p);
|
||||
|
||||
// Manually trigger post barriers on the whole object. If we treat
|
||||
// the pointer as a HeapPtrObject we will get confused later if the
|
||||
// object is converted to its native representation.
|
||||
JSObject* obj = v.toObjectOrNull();
|
||||
if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject)) {
|
||||
JSRuntime* rt = unboxedObject->runtimeFromMainThread();
|
||||
rt->gc.storeBuffer.putWholeCell(unboxedObject);
|
||||
}
|
||||
|
||||
if (preBarrier)
|
||||
JSObject::writeBarrierPre(*np);
|
||||
*np = obj;
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Invalid type for unboxed value");
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id,
|
||||
uint8_t* p, JSValueType type, const Value& v, bool preBarrier)
|
||||
{
|
||||
switch (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()) {
|
||||
MOZ_ASSERT(!IsInsideNursery(v.toString()));
|
||||
JSString** np = reinterpret_cast<JSString**>(p);
|
||||
if (preBarrier)
|
||||
JSString::writeBarrierPre(*np);
|
||||
*np = v.toString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
if (v.isObjectOrNull()) {
|
||||
JSObject** np = reinterpret_cast<JSObject**>(p);
|
||||
|
||||
// Update property types when writing object properties. Types for
|
||||
// other properties were captured when the unboxed layout was
|
||||
// created.
|
||||
AddTypePropertyId(cx, unboxedObject, id, v);
|
||||
|
||||
// As above, trigger post barriers on the whole object.
|
||||
JSObject* obj = v.toObjectOrNull();
|
||||
if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) {
|
||||
JSRuntime* rt = unboxedObject->runtimeFromMainThread();
|
||||
rt->gc.storeBuffer.putWholeCell(unboxedObject);
|
||||
}
|
||||
|
||||
if (preBarrier)
|
||||
JSObject::writeBarrierPre(*np);
|
||||
*np = obj;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Invalid type for unboxed value");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// UnboxedPlainObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline const UnboxedLayout&
|
||||
UnboxedPlainObject::layout() const
|
||||
{
|
||||
return group()->unboxedLayout();
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // vm_UnboxedObject_inl_h
|
||||
|
|
@ -1,759 +0,0 @@
|
|||
/* -*- 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-inl.h"
|
||||
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
#include "jit/JitCommon.h"
|
||||
#include "jit/Linker.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "gc/Nursery-inl.h"
|
||||
#include "jit/MacroAssembler-inl.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++)
|
||||
TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name");
|
||||
|
||||
if (newScript())
|
||||
newScript()->trace(trc);
|
||||
|
||||
TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
|
||||
TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape");
|
||||
TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript");
|
||||
TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup");
|
||||
TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode");
|
||||
}
|
||||
|
||||
size_t
|
||||
UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
return mallocSizeOf(this)
|
||||
+ properties_.sizeOfExcludingThis(mallocSizeOf)
|
||||
+ (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
|
||||
+ mallocSizeOf(traceList());
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */)
|
||||
{
|
||||
if (newScript_ && writeBarrier)
|
||||
TypeNewScript::writeBarrierPre(newScript_);
|
||||
newScript_ = newScript;
|
||||
}
|
||||
|
||||
// Constructor code returns a 0x1 value to indicate the constructor code should
|
||||
// be cleared.
|
||||
static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
|
||||
|
||||
/* static */ bool
|
||||
UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
|
||||
{
|
||||
// *** STUB ***
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedLayout::detachFromCompartment()
|
||||
{
|
||||
if (isInList())
|
||||
remove();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// UnboxedPlainObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool
|
||||
UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property,
|
||||
const Value& v)
|
||||
{
|
||||
uint8_t* p = &data_[property.offset];
|
||||
return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v,
|
||||
/* preBarrier = */ true);
|
||||
}
|
||||
|
||||
Value
|
||||
UnboxedPlainObject::getValue(const UnboxedLayout::Property& property,
|
||||
bool maybeUninitialized /* = false */)
|
||||
{
|
||||
uint8_t* p = &data_[property.offset];
|
||||
return GetUnboxedValue(p, property.type, maybeUninitialized);
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (obj->as<UnboxedPlainObject>().expando_) {
|
||||
TraceManuallyBarrieredEdge(trc,
|
||||
reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
|
||||
"unboxed_expando");
|
||||
}
|
||||
|
||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
|
||||
const int32_t* list = layout.traceList();
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
uint8_t* data = obj->as<UnboxedPlainObject>().data();
|
||||
while (*list != -1) {
|
||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
|
||||
TraceEdge(trc, heap, "unboxed_string");
|
||||
list++;
|
||||
}
|
||||
list++;
|
||||
while (*list != -1) {
|
||||
GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
|
||||
TraceNullableEdge(trc, heap, "unboxed_object");
|
||||
list++;
|
||||
}
|
||||
|
||||
// Unboxed objects don't have Values to trace.
|
||||
MOZ_ASSERT(*(list + 1) == -1);
|
||||
}
|
||||
|
||||
/* static */ UnboxedExpandoObject*
|
||||
UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj)
|
||||
{
|
||||
if (obj->expando_)
|
||||
return obj->expando_;
|
||||
|
||||
UnboxedExpandoObject* expando =
|
||||
NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4);
|
||||
if (!expando)
|
||||
return nullptr;
|
||||
|
||||
// Don't track property types for expando objects. This allows Baseline
|
||||
// and Ion AddSlot ICs to guard on the unboxed group without guarding on
|
||||
// the expando group.
|
||||
MarkObjectGroupUnknownProperties(cx, expando->group());
|
||||
|
||||
// If the expando is tenured then the original object must also be tenured.
|
||||
// Otherwise barriers triggered on the original object for writes to the
|
||||
// expando (as can happen in the JIT) won't see the tenured->nursery edge.
|
||||
// See WholeCellEdges::mark.
|
||||
MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj));
|
||||
|
||||
// As with setValue(), we need to manually trigger post barriers on the
|
||||
// whole object. If we treat the field as a GCPtrObject and later
|
||||
// convert the object to its native representation, we will end up with a
|
||||
// corrupted store buffer entry.
|
||||
if (IsInsideNursery(expando) && !IsInsideNursery(obj))
|
||||
cx->runtime()->gc.storeBuffer.putWholeCell(obj);
|
||||
|
||||
obj->expando_ = expando;
|
||||
return expando;
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const
|
||||
{
|
||||
if (layout().lookup(id))
|
||||
return true;
|
||||
|
||||
if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup)
|
||||
{
|
||||
HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id);
|
||||
TypeSet::TypeList types;
|
||||
if (!typeProperty->enumerateTypes(&types)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
for (size_t j = 0; j < types.length(); j++)
|
||||
AddTypePropertyId(cx, newGroup, nullptr, id, types[j]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static PlainObject*
|
||||
MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
|
||||
{
|
||||
PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
|
||||
TenuredObject);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
|
||||
return nullptr;
|
||||
MOZ_ASSERT(obj->slotSpan() == i + 1);
|
||||
MOZ_ASSERT(!obj->inDictionaryMode());
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
UnboxedLayout& layout = group->unboxedLayout();
|
||||
Rooted<TaggedProto> proto(cx, group->proto());
|
||||
|
||||
MOZ_ASSERT(!layout.nativeGroup());
|
||||
|
||||
RootedObjectGroup replacementGroup(cx);
|
||||
|
||||
// Immediately clear any new script on the group. This is done by replacing
|
||||
// the existing new script with one for a replacement default new group.
|
||||
// This is done so that the size of the replacment group's objects is the
|
||||
// same as that for the unboxed group, so that we do not see polymorphic
|
||||
// slot accesses later on for sites that see converted objects from this
|
||||
// group and objects that were allocated using the replacement new group.
|
||||
if (layout.newScript()) {
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
||||
if (!replacementGroup)
|
||||
return false;
|
||||
|
||||
PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout);
|
||||
if (!templateObject)
|
||||
return false;
|
||||
|
||||
TypeNewScript* replacementNewScript =
|
||||
TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
|
||||
if (!replacementNewScript)
|
||||
return false;
|
||||
|
||||
replacementGroup->setNewScript(replacementNewScript);
|
||||
gc::TraceTypeNewScript(replacementGroup);
|
||||
|
||||
group->clearNewScript(cx, replacementGroup);
|
||||
}
|
||||
|
||||
// Similarly, if this group is keyed to an allocation site, replace its
|
||||
// entry with a new group that has no unboxed layout.
|
||||
if (layout.allocationScript()) {
|
||||
RootedScript script(cx, layout.allocationScript());
|
||||
jsbytecode* pc = layout.allocationPc();
|
||||
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
||||
if (!replacementGroup)
|
||||
return false;
|
||||
|
||||
PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
|
||||
replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
|
||||
|
||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, JSProto_Object,
|
||||
replacementGroup);
|
||||
|
||||
// Clear any baseline information at this opcode which might use the old group.
|
||||
if (script->hasBaselineScript()) {
|
||||
jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
|
||||
jit::ICFallbackStub* fallback = entry.fallbackStub();
|
||||
for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
|
||||
iter.unlink(cx);
|
||||
if (fallback->isNewObject_Fallback())
|
||||
fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
|
||||
else if (fallback->isNewArray_Fallback())
|
||||
fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
|
||||
}
|
||||
}
|
||||
|
||||
size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind());
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0));
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
// Add shapes for each property, if this is for a plain object.
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
|
||||
Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name),
|
||||
i, JSPROP_ENUMERATE, 0));
|
||||
shape = cx->zone()->propertyTree.getChild(cx, shape, child);
|
||||
if (!shape)
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGroup* nativeGroup =
|
||||
ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto,
|
||||
group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
|
||||
if (!nativeGroup)
|
||||
return false;
|
||||
|
||||
// No sense propagating if we don't know what we started with.
|
||||
if (!group->unknownProperties()) {
|
||||
// Propagate all property types from the old group to the new group.
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
jsid id = NameToId(property.name);
|
||||
if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
|
||||
return false;
|
||||
|
||||
// If we are OOM we may not be able to propagate properties.
|
||||
if (nativeGroup->unknownProperties())
|
||||
break;
|
||||
|
||||
HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
|
||||
if (nativeProperty && nativeProperty->canSetDefinite(i))
|
||||
nativeProperty->setDefinite(i);
|
||||
}
|
||||
} else {
|
||||
// If we skip, though, the new group had better agree.
|
||||
MOZ_ASSERT(nativeGroup->unknownProperties());
|
||||
}
|
||||
|
||||
layout.nativeGroup_ = nativeGroup;
|
||||
layout.nativeShape_ = shape;
|
||||
layout.replacementGroup_ = replacementGroup;
|
||||
|
||||
nativeGroup->setOriginalUnboxedGroup(group);
|
||||
|
||||
group->markStateChange(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
||||
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
|
||||
if (!layout.nativeGroup()) {
|
||||
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
|
||||
return false;
|
||||
|
||||
// makeNativeGroup can reentrantly invoke this method.
|
||||
if (obj->is<PlainObject>())
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoValueVector values(cx);
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
// We might be reading properties off the object which have not been
|
||||
// initialized yet. Make sure any double values we read here are
|
||||
// canonicalized.
|
||||
if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are eliminating the expando edge with the conversion, so trigger a
|
||||
// pre barrier.
|
||||
JSObject::writeBarrierPre(expando);
|
||||
|
||||
// Additionally trigger a post barrier on the expando itself. Whole cell
|
||||
// store buffer entries can be added on the original unboxed object for
|
||||
// writes to the expando (see WholeCellEdges::trace), so after conversion
|
||||
// we need to make sure the expando itself will still be traced.
|
||||
if (expando && !IsInsideNursery(expando))
|
||||
cx->runtime()->gc.storeBuffer.putWholeCell(expando);
|
||||
|
||||
obj->setGroup(layout.nativeGroup());
|
||||
obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
|
||||
|
||||
for (size_t i = 0; i < values.length(); i++)
|
||||
obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
|
||||
|
||||
if (expando) {
|
||||
// Add properties from the expando object to the object, in order.
|
||||
// Suppress GC here, so that callers don't need to worry about this
|
||||
// method collecting. The stuff below can only fail due to OOM, in
|
||||
// which case the object will not have been completely filled back in.
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
|
||||
Vector<jsid> ids(cx);
|
||||
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
|
||||
if (!ids.append(r.front().propid()))
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
|
||||
if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
|
||||
if (!ids.append(INT_TO_JSID(i)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
::Reverse(ids.begin(), ids.end());
|
||||
|
||||
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
|
||||
Rooted<UnboxedExpandoObject*> nexpando(cx, expando);
|
||||
RootedId id(cx);
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
for (size_t i = 0; i < ids.length(); i++) {
|
||||
id = ids[i];
|
||||
if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
|
||||
return false;
|
||||
ObjectOpResult result;
|
||||
if (!DefineProperty(cx, nobj, id, desc, result))
|
||||
return false;
|
||||
MOZ_ASSERT(result.ok());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
UnboxedPlainObject*
|
||||
UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind)
|
||||
{
|
||||
AutoSetNewObjectMetadata metadata(cx);
|
||||
|
||||
MOZ_ASSERT(group->clasp() == &class_);
|
||||
gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
|
||||
|
||||
UnboxedPlainObject* res =
|
||||
NewObjectWithGroup<UnboxedPlainObject>(cx, group, allocKind, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
// Overwrite the dummy shape which was written to the object's expando field.
|
||||
res->initExpando();
|
||||
|
||||
// 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) {
|
||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
|
||||
heap->init(cx->names().empty);
|
||||
list++;
|
||||
}
|
||||
list++;
|
||||
while (*list != -1) {
|
||||
GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
|
||||
heap->init(nullptr);
|
||||
list++;
|
||||
}
|
||||
// Unboxed objects don't have Values to initialize.
|
||||
MOZ_ASSERT(*(list + 1) == -1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* static */ JSObject*
|
||||
UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
NewObjectKind newKind, IdValuePair* properties)
|
||||
{
|
||||
MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
|
||||
|
||||
UnboxedLayout& layout = group->unboxedLayout();
|
||||
|
||||
if (layout.constructorCode()) {
|
||||
MOZ_ASSERT(cx->isJSContext());
|
||||
|
||||
typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind);
|
||||
ConstructorCodeSignature function =
|
||||
reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
|
||||
|
||||
JSObject* obj;
|
||||
{
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
obj = reinterpret_cast<JSObject*>(CALL_GENERATED_2(function, properties, newKind));
|
||||
}
|
||||
if (obj > reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
|
||||
return obj;
|
||||
|
||||
if (obj == reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
|
||||
layout.setConstructorCode(nullptr);
|
||||
}
|
||||
|
||||
UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
|
||||
return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
|
||||
}
|
||||
|
||||
#ifndef JS_CODEGEN_NONE
|
||||
if (cx->isJSContext() &&
|
||||
!group->unknownProperties() &&
|
||||
!layout.constructorCode() &&
|
||||
cx->asJSContext()->runtime()->jitSupportsFloatingPoint &&
|
||||
jit::CanLikelyAllocateMoreExecutableMemory())
|
||||
{
|
||||
if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp)
|
||||
{
|
||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
||||
MarkNonNativePropertyFound<CanGC>(propp);
|
||||
objp.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
objp.set(nullptr);
|
||||
propp.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return LookupProperty(cx, proto, id, objp, propp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
||||
|
||||
if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
||||
if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
|
||||
// This define is equivalent to setting an existing property.
|
||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value()))
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
// Trying to incompatibly redefine an existing property requires the
|
||||
// object to be converted to a native object.
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
|
||||
return DefineProperty(cx, obj, id, desc, result);
|
||||
}
|
||||
|
||||
// Define the property on the expando object.
|
||||
Rooted<UnboxedExpandoObject*> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
|
||||
if (!expando)
|
||||
return false;
|
||||
|
||||
// Update property types on the unboxed object as well.
|
||||
AddTypePropertyId(cx, obj, id, desc.value());
|
||||
|
||||
return DefineProperty(cx, expando, id, desc, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
|
||||
{
|
||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
||||
*foundp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
*foundp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return HasProperty(cx, proto, id, foundp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue 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;
|
||||
}
|
||||
|
||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
if (expando->containsShapeOrElement(cx, id)) {
|
||||
RootedObject nexpando(cx, expando);
|
||||
return GetProperty(cx, nexpando, receiver, id, vp);
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
return GetProperty(cx, proto, receiver, id, vp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result)
|
||||
{
|
||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
||||
|
||||
if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
||||
if (receiver.isObject() && obj == &receiver.toObject()) {
|
||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
|
||||
return result.succeed();
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return SetProperty(cx, obj, id, v, receiver, result);
|
||||
}
|
||||
|
||||
return SetPropertyByDefining(cx, id, v, receiver, result);
|
||||
}
|
||||
|
||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
if (expando->containsShapeOrElement(cx, id)) {
|
||||
// Update property types on the unboxed object as well.
|
||||
AddTypePropertyId(cx, obj, id, v);
|
||||
|
||||
RootedObject nexpando(cx, expando);
|
||||
return SetProperty(cx, nexpando, id, v, receiver, result);
|
||||
}
|
||||
}
|
||||
|
||||
return SetPropertyOnProto(cx, obj, id, v, receiver, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> 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;
|
||||
}
|
||||
|
||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
if (expando->containsShapeOrElement(cx, id)) {
|
||||
RootedObject nexpando(cx, expando);
|
||||
if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc))
|
||||
return false;
|
||||
if (desc.object() == nexpando)
|
||||
desc.object().set(obj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
desc.object().set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return DeleteProperty(cx, obj, id, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
|
||||
{
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return WatchProperty(cx, obj, id, callable);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
||||
bool enumerableOnly)
|
||||
{
|
||||
// Ignore expando properties here, they are special-cased by the property
|
||||
// enumeration code.
|
||||
|
||||
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 UnboxedExpandoObject::class_ = {
|
||||
"UnboxedExpandoObject",
|
||||
0
|
||||
};
|
||||
|
||||
static const ClassOps UnboxedPlainObjectClassOps = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
nullptr, /* finalize */
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
UnboxedPlainObject::trace,
|
||||
};
|
||||
|
||||
static const ObjectOps UnboxedPlainObjectObjectOps = {
|
||||
UnboxedPlainObject::obj_lookupProperty,
|
||||
UnboxedPlainObject::obj_defineProperty,
|
||||
UnboxedPlainObject::obj_hasProperty,
|
||||
UnboxedPlainObject::obj_getProperty,
|
||||
UnboxedPlainObject::obj_setProperty,
|
||||
UnboxedPlainObject::obj_getOwnPropertyDescriptor,
|
||||
UnboxedPlainObject::obj_deleteProperty,
|
||||
UnboxedPlainObject::obj_watch,
|
||||
nullptr, /* No unwatch needed, as watch() converts the object to native */
|
||||
nullptr, /* getElements */
|
||||
UnboxedPlainObject::obj_enumerate,
|
||||
nullptr /* funToString */
|
||||
};
|
||||
|
||||
const Class UnboxedPlainObject::class_ = {
|
||||
js_Object_str,
|
||||
Class::NON_NATIVE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
|
||||
JSCLASS_DELAY_METADATA_BUILDER,
|
||||
&UnboxedPlainObjectClassOps,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
&UnboxedPlainObjectObjectOps
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline Value
|
||||
NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
|
||||
{
|
||||
return values[(*valueCursor)++];
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor)
|
||||
{
|
||||
initExpando();
|
||||
memset(data(), 0, layout().size());
|
||||
for (size_t i = 0; i < layout().properties().length(); i++)
|
||||
JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
|
||||
}
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef vm_UnboxedObject_h
|
||||
#define vm_UnboxedObject_h
|
||||
|
||||
#include "jsgc.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
#include "vm/Runtime.h"
|
||||
#include "vm/TypeInference.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// Memory required for an unboxed value of a given type. Returns zero for types
|
||||
// which can't be used for unboxed objects.
|
||||
static inline size_t
|
||||
UnboxedTypeSize(JSValueType type)
|
||||
{
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_BOOLEAN: return 1;
|
||||
case JSVAL_TYPE_INT32: return 4;
|
||||
case JSVAL_TYPE_DOUBLE: return 8;
|
||||
case JSVAL_TYPE_STRING: return sizeof(void*);
|
||||
case JSVAL_TYPE_OBJECT: return sizeof(void*);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
UnboxedTypeNeedsPreBarrier(JSValueType type)
|
||||
{
|
||||
return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
UnboxedTypeNeedsPostBarrier(JSValueType type)
|
||||
{
|
||||
return type == JSVAL_TYPE_OBJECT;
|
||||
}
|
||||
|
||||
// Class tracking information specific to unboxed objects.
|
||||
class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
{
|
||||
public:
|
||||
struct Property {
|
||||
PropertyName* name;
|
||||
uint32_t offset;
|
||||
JSValueType type;
|
||||
|
||||
Property()
|
||||
: name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
|
||||
|
||||
private:
|
||||
// If objects in this group have ever been converted to native objects,
|
||||
// these store the corresponding native group and initial shape for such
|
||||
// objects. Type information for this object is reflected in nativeGroup.
|
||||
GCPtrObjectGroup nativeGroup_;
|
||||
GCPtrShape nativeShape_;
|
||||
|
||||
// Any script/pc which the associated group is created for.
|
||||
GCPtrScript allocationScript_;
|
||||
jsbytecode* allocationPc_;
|
||||
|
||||
// If nativeGroup is set and this object originally had a TypeNewScript or
|
||||
// was keyed to an allocation site, this points to the group which replaced
|
||||
// this one. This link is only needed to keep the replacement group from
|
||||
// being GC'ed. If it were GC'ed and a new one regenerated later, that new
|
||||
// group might have a different allocation kind from this group.
|
||||
GCPtrObjectGroup replacementGroup_;
|
||||
|
||||
// The following members are only used for unboxed plain objects.
|
||||
|
||||
// All properties on objects with this layout, in enumeration order.
|
||||
PropertyVector properties_;
|
||||
|
||||
// Byte size of the data for objects with this layout.
|
||||
size_t size_;
|
||||
|
||||
// Any 'new' script information associated with this layout.
|
||||
TypeNewScript* newScript_;
|
||||
|
||||
// List for use in tracing objects with this layout. This has the same
|
||||
// structure as the trace list on a TypeDescr.
|
||||
int32_t* traceList_;
|
||||
|
||||
// If this layout has been used to construct script or JSON constant
|
||||
// objects, this code might be filled in to more quickly fill in objects
|
||||
// from an array of values.
|
||||
GCPtrJitCode constructorCode_;
|
||||
|
||||
public:
|
||||
UnboxedLayout()
|
||||
: nativeGroup_(nullptr), nativeShape_(nullptr),
|
||||
allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
|
||||
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr)
|
||||
{}
|
||||
|
||||
bool initProperties(const PropertyVector& properties, size_t size) {
|
||||
size_ = size;
|
||||
return properties_.appendAll(properties);
|
||||
}
|
||||
|
||||
~UnboxedLayout() {
|
||||
if (newScript_)
|
||||
newScript_->clear();
|
||||
js_delete(newScript_);
|
||||
js_free(traceList_);
|
||||
|
||||
nativeGroup_.init(nullptr);
|
||||
nativeShape_.init(nullptr);
|
||||
replacementGroup_.init(nullptr);
|
||||
constructorCode_.init(nullptr);
|
||||
}
|
||||
|
||||
void detachFromCompartment();
|
||||
|
||||
const PropertyVector& properties() const {
|
||||
return properties_;
|
||||
}
|
||||
|
||||
TypeNewScript* newScript() const {
|
||||
return newScript_;
|
||||
}
|
||||
|
||||
void setNewScript(TypeNewScript* newScript, bool writeBarrier = true);
|
||||
|
||||
JSScript* allocationScript() const {
|
||||
return allocationScript_;
|
||||
}
|
||||
|
||||
jsbytecode* allocationPc() const {
|
||||
return allocationPc_;
|
||||
}
|
||||
|
||||
void setAllocationSite(JSScript* script, jsbytecode* pc) {
|
||||
allocationScript_ = script;
|
||||
allocationPc_ = pc;
|
||||
}
|
||||
|
||||
const int32_t* traceList() const {
|
||||
return traceList_;
|
||||
}
|
||||
|
||||
void setTraceList(int32_t* traceList) {
|
||||
traceList_ = traceList;
|
||||
}
|
||||
|
||||
const Property* lookup(JSAtom* atom) const {
|
||||
for (size_t i = 0; i < properties_.length(); i++) {
|
||||
if (properties_[i].name == atom)
|
||||
return &properties_[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Property* lookup(jsid id) const {
|
||||
if (JSID_IS_STRING(id))
|
||||
return lookup(JSID_TO_ATOM(id));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
ObjectGroup* nativeGroup() const {
|
||||
return nativeGroup_;
|
||||
}
|
||||
|
||||
Shape* nativeShape() const {
|
||||
return nativeShape_;
|
||||
}
|
||||
|
||||
jit::JitCode* constructorCode() const {
|
||||
return constructorCode_;
|
||||
}
|
||||
|
||||
void setConstructorCode(jit::JitCode* code) {
|
||||
constructorCode_ = code;
|
||||
}
|
||||
|
||||
inline gc::AllocKind getAllocKind() const;
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
static bool makeNativeGroup(JSContext* cx, ObjectGroup* group);
|
||||
static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group);
|
||||
};
|
||||
|
||||
// Class for expando objects holding extra properties given to an unboxed plain
|
||||
// object. These objects behave identically to normal native plain objects, and
|
||||
// have a separate Class to distinguish them for memory usage reporting.
|
||||
class UnboxedExpandoObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
// Class for a plain object using an unboxed representation. The physical
|
||||
// layout of these objects is identical to that of an InlineTypedObject, though
|
||||
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
|
||||
// how their properties are stored.
|
||||
class UnboxedPlainObject : public JSObject
|
||||
{
|
||||
// Optional object which stores extra properties on this object. This is
|
||||
// not automatically barriered to avoid problems if the object is converted
|
||||
// to a native. See ensureExpando().
|
||||
UnboxedExpandoObject* expando_;
|
||||
|
||||
// Start of the inline data, which immediately follows the group and extra properties.
|
||||
uint8_t data_[1];
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp);
|
||||
|
||||
static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result);
|
||||
|
||||
static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
|
||||
|
||||
static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp);
|
||||
|
||||
static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result);
|
||||
|
||||
static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc);
|
||||
|
||||
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result);
|
||||
|
||||
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
||||
bool enumerableOnly);
|
||||
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
|
||||
|
||||
inline const UnboxedLayout& layout() const;
|
||||
|
||||
const UnboxedLayout& layoutDontCheckGeneration() const {
|
||||
return group()->unboxedLayoutDontCheckGeneration();
|
||||
}
|
||||
|
||||
uint8_t* data() {
|
||||
return &data_[0];
|
||||
}
|
||||
|
||||
UnboxedExpandoObject* maybeExpando() const {
|
||||
return expando_;
|
||||
}
|
||||
|
||||
void initExpando() {
|
||||
expando_ = nullptr;
|
||||
}
|
||||
|
||||
// For use during GC.
|
||||
JSObject** addressOfExpando() {
|
||||
return reinterpret_cast<JSObject**>(&expando_);
|
||||
}
|
||||
|
||||
bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const;
|
||||
|
||||
static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj);
|
||||
|
||||
bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v);
|
||||
Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false);
|
||||
|
||||
static bool convertToNative(JSContext* cx, JSObject* obj);
|
||||
static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
NewObjectKind newKind);
|
||||
static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
NewObjectKind newKind, IdValuePair* properties);
|
||||
|
||||
void fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* object);
|
||||
|
||||
static size_t offsetOfExpando() {
|
||||
return offsetof(UnboxedPlainObject, expando_);
|
||||
}
|
||||
|
||||
static size_t offsetOfData() {
|
||||
return offsetof(UnboxedPlainObject, data_[0]);
|
||||
}
|
||||
};
|
||||
|
||||
inline gc::AllocKind
|
||||
UnboxedLayout::getAllocKind() const
|
||||
{
|
||||
MOZ_ASSERT(size());
|
||||
return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
template <>
|
||||
struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout>
|
||||
{};
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
#endif /* vm_UnboxedObject_h */
|
||||
Loading…
Add table
Add a link
Reference in a new issue