summaryrefslogtreecommitdiffstats
path: root/js/src/vm/TypeInference.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/TypeInference.cpp')
-rw-r--r--js/src/vm/TypeInference.cpp314
1 files changed, 120 insertions, 194 deletions
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp
index 3d09c7464..2b1fa0e3b 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -12,6 +12,8 @@
#include "mozilla/SizePrintfMacros.h"
#include "mozilla/Sprintf.h"
+#include <new>
+
#include "jsapi.h"
#include "jscntxt.h"
#include "jsgc.h"
@@ -33,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"
@@ -295,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)) {
@@ -859,10 +857,8 @@ TypeSet::IsTypeAboutToBeFinalized(TypeSet::Type* v)
}
bool
-TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const
+TypeSet::cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const
{
- MOZ_ASSERT(result->empty());
-
unsigned objectCount = baseObjectCount();
unsigned capacity = (objectCount >= 2) ? TypeHashSet::Capacity(objectCount) : 0;
@@ -874,15 +870,15 @@ TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const
PodCopy(newSet, objectSet, capacity);
}
- new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
+ new (result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
return true;
}
TemporaryTypeSet*
TypeSet::clone(LifoAlloc* alloc) const
{
- TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>();
- if (!res || !clone(alloc, res))
+ TemporaryTypeSet* res = alloc->pod_malloc<TemporaryTypeSet>();
+ if (!res || !cloneIntoUninitialized(alloc, res))
return nullptr;
return res;
}
@@ -1150,10 +1146,9 @@ TypeScript::FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script
TemporaryTypeSet* types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
if (!types)
return false;
- PodZero(types, count);
for (size_t i = 0; i < count; i++) {
- if (!existing[i].clone(alloc, &types[i]))
+ if (!existing[i].cloneIntoUninitialized(alloc, &types[i]))
return false;
}
@@ -1324,7 +1319,8 @@ js::EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id)
AutoEnterAnalysis enter(cx);
if (obj->hasLazyGroup()) {
AutoEnterOOMUnsafeRegion oomUnsafe;
- if (!obj->getGroup(cx)) {
+ RootedObject objRoot(cx, obj);
+ if (!JSObject::getGroup(cx, objRoot)) {
oomUnsafe.crash("Could not allocate ObjectGroup in EnsureTrackPropertyTypes");
return;
}
@@ -1343,9 +1339,12 @@ HeapTypeSetKey::instantiate(JSContext* cx)
{
if (maybeTypes())
return true;
- if (object()->isSingleton() && !object()->singleton()->getGroup(cx)) {
- cx->clearPendingException();
- return false;
+ if (object()->isSingleton()) {
+ RootedObject obj(cx, object()->singleton());
+ if (!JSObject::getGroup(cx, obj)) {
+ cx->clearPendingException();
+ return false;
+ }
}
JSObject* obj = object()->isSingleton() ? object()->singleton() : nullptr;
maybeTypes_ = object()->maybeGroup()->getProperty(cx, obj, id());
@@ -1945,33 +1944,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
@@ -1996,17 +1968,6 @@ TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* co
ConstraintDataFreezeObjectForTypedArrayData(tarray)));
}
-void
-TypeSet::ObjectKey::watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints)
-{
- HeapTypeSetKey objectProperty = property(JSID_EMPTY);
- LifoAlloc* alloc = constraints->alloc();
-
- typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForUnboxedConvertedToNative> T;
- constraints->add(alloc->new_<T>(alloc, objectProperty,
- ConstraintDataFreezeObjectForUnboxedConvertedToNative()));
-}
-
static void
ObjectStateChange(ExclusiveContext* cxArg, ObjectGroup* group, bool markingUnknown)
{
@@ -2517,8 +2478,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
bool
js::ClassCanHaveExtraProperties(const Class* clasp)
{
- if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
- return false;
return clasp->getResolve()
|| clasp->getOpsLookupProperty()
|| clasp->getOpsGetProperty()
@@ -2711,14 +2670,6 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext* cx, JSObject* objArg, jsid
if (shape)
UpdatePropertyType(cx, types, obj, shape, false);
}
-
- if (obj->watched()) {
- /*
- * Mark the property as non-data, to inhibit optimizations on it
- * and avoid bypassing the watchpoint handler.
- */
- types->setNonDataProperty(cx);
- }
}
void
@@ -2817,15 +2768,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
@@ -2897,12 +2839,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
@@ -2935,23 +2871,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*
-ObjectGroup::anyNewScript()
-{
- if (newScript())
- return newScript();
- if (maybeUnboxedLayout())
- return unboxedLayout().newScript();
- return nullptr;
}
void
@@ -2961,7 +2880,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
// analyzed, remove it from the newObjectGroups table so that it will not be
// produced by calling 'new' on the associated function anymore.
// The TypeNewScript is not actually destroyed.
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
MOZ_ASSERT(newScript);
if (newScript->analyzed()) {
@@ -2980,10 +2899,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
@@ -2994,7 +2910,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
if (!isMarked())
return;
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
if (!newScript)
return;
@@ -3009,7 +2925,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
void
ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/)
{
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
if (!newScript)
return;
@@ -3021,7 +2937,8 @@ ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* =
// Mark the constructing function as having its 'new' script cleared, so we
// will not try to construct another one later.
- if (!newScript->function()->setNewScriptCleared(cx))
+ RootedFunction fun(cx, newScript->function());
+ if (!JSObject::setNewScriptCleared(cx, fun))
cx->recoverFromOutOfMemory();
}
@@ -3159,29 +3076,39 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
};
bool
-js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id)
+js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
+ DPAConstraintInfo& constraintInfo,
+ ObjectGroup* group,
+ HandleId id,
+ bool* added)
{
/*
* Ensure that if the properties named here could have a getter, setter or
* a permanent property in any transitive prototype, the definite
* properties get cleared from the group.
*/
+
+ *added = false;
+
RootedObject proto(cx, group->proto().toObjectOrNull());
while (proto) {
- ObjectGroup* protoGroup = proto->getGroup(cx);
+ ObjectGroup* protoGroup = JSObject::getGroup(cx, proto);
if (!protoGroup) {
- cx->recoverFromOutOfMemory();
return false;
}
if (protoGroup->unknownProperties())
- return false;
+ return true;
HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id);
- if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
+ if (!protoTypes)
return false;
- if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group)))
+ if (protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
+ return true;
+ if (!constraintInfo.addProtoConstraint(proto, id))
return false;
proto = proto->staticPrototype();
}
+
+ *added = true;
return true;
}
@@ -3405,7 +3332,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun,
/////////////////////////////////////////////////////////////////////
void
-PreliminaryObjectArray::registerNewObject(JSObject* res)
+PreliminaryObjectArray::registerNewObject(PlainObject* res)
{
// The preliminary object pointers are weak, and won't be swept properly
// during nursery collections, so the preliminary objects need to be
@@ -3423,7 +3350,7 @@ PreliminaryObjectArray::registerNewObject(JSObject* res)
}
void
-PreliminaryObjectArray::unregisterObject(JSObject* obj)
+PreliminaryObjectArray::unregisterObject(PlainObject* obj)
{
for (size_t i = 0; i < COUNT; i++) {
if (objects[i] == obj) {
@@ -3463,22 +3390,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;
}
}
@@ -3578,17 +3489,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro
}
}
- TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects);
- 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());
}
/////////////////////////////////////////////////////////////////////
@@ -3602,7 +3507,10 @@ 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.
+ MOZ_ASSERT(fun->maybeCanonicalFunction() == fun);
if (group->unknownProperties())
return true;
@@ -3706,6 +3614,43 @@ struct DestroyTypeNewScript
} // namespace
+bool DPAConstraintInfo::finishConstraints(JSContext* cx, ObjectGroup* group) {
+ for (const ProtoConstraint& constraint : protoConstraints_) {
+ ObjectGroup* protoGroup = constraint.proto->group();
+
+ // Note: we rely on the group's type information being unchanged since
+ // AddClearDefiniteGetterSetterForPrototypeChain.
+
+ bool unknownProperties = protoGroup->unknownProperties();
+ MOZ_RELEASE_ASSERT(!unknownProperties);
+
+ HeapTypeSet* protoTypes =
+ protoGroup->getProperty(cx, constraint.proto, constraint.id);
+ MOZ_RELEASE_ASSERT(protoTypes);
+
+ MOZ_ASSERT(!protoTypes->nonDataProperty());
+ MOZ_ASSERT(!protoTypes->nonWritableProperty());
+
+ if (!protoTypes->addConstraint(
+ cx,
+ cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
+ group))) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ for (const InliningConstraint& constraint : inliningConstraints_) {
+ if (!AddClearDefiniteFunctionUsesInScript(cx, group, constraint.caller,
+ constraint.callee)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool
TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force)
{
@@ -3809,9 +3754,17 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
return false;
Vector<Initializer> initializerVector(cx);
+
+ DPAConstraintInfo constraintInfo(cx);
RootedPlainObject templateRoot(cx, templateObject());
- if (!jit::AnalyzeNewScriptDefiniteProperties(cx, function(), group, templateRoot, &initializerVector))
+ RootedFunction fun(cx, function());
+ if (!jit::AnalyzeNewScriptDefiniteProperties(cx,
+ constraintInfo,
+ fun,
+ group,
+ templateRoot,
+ &initializerVector))
return false;
if (!group->newScript())
@@ -3858,38 +3811,21 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
}
- // Try to use an unboxed representation for the group.
- if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(), group, preliminaryObjects))
- return false;
-
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
// is needed.
+
+ if (!constraintInfo.finishConstraints(cx, group)) {
+ return false;
+ }
+ if (!group->newScript()) {
+ return true;
+ }
+
group->addDefiniteProperties(cx, templateObject()->lastProperty());
destroyNewScript.group = nullptr;
@@ -3911,6 +3847,16 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
if (!initialGroup)
return false;
+ // Add the constraints. Use the initialGroup as group referenced by the
+ // constraints because that's the group that will have the TypeNewScript
+ // associated with it. See the detachNewScript and setNewScript calls below.
+ if (!constraintInfo.finishConstraints(cx, initialGroup)) {
+ return false;
+ }
+ if (!group->newScript()) {
+ return true;
+ }
+
initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty());
group->addDefiniteProperties(cx, prefixShape);
@@ -3959,8 +3905,15 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
oomUnsafe.crash("rollbackPartiallyInitializedObjects");
}
- if (!iter.isConstructing() || !iter.matchCallee(cx, function))
+ if (!iter.isConstructing()) {
continue;
+ }
+
+ MOZ_ASSERT(iter.calleeTemplate()->maybeCanonicalFunction());
+
+ if (iter.calleeTemplate()->maybeCanonicalFunction() != function) {
+ continue;
+ }
// Derived class constructors initialize their this-binding later and
// we shouldn't run the definite properties analysis on them.
@@ -3974,12 +3927,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>());
@@ -4173,12 +4120,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;
@@ -4262,21 +4203,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();