Remove Unboxed Object code from /vm, Part 3.

This commit is contained in:
wolfbeast 2019-06-25 11:15:11 +00:00 committed by Roy Tam
commit aa89a14709
18 changed files with 13 additions and 1560 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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',

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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