diff options
Diffstat (limited to 'js/src/vm/TypeInference.cpp')
-rw-r--r-- | js/src/vm/TypeInference.cpp | 176 |
1 files changed, 165 insertions, 11 deletions
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 2b1fa0e3b..438188a36 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -35,6 +35,7 @@ #include "vm/Opcodes.h" #include "vm/Shape.h" #include "vm/Time.h" +#include "vm/UnboxedObject.h" #include "jsatominlines.h" #include "jsscriptinlines.h" @@ -296,6 +297,9 @@ 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,6 +1948,33 @@ 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 @@ -1968,6 +1999,17 @@ 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) { @@ -2478,6 +2520,8 @@ 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() @@ -2768,6 +2812,15 @@ 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 @@ -2839,6 +2892,12 @@ 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 @@ -2871,6 +2930,23 @@ 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 @@ -2880,7 +2956,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 = this->newScript(); + TypeNewScript* newScript = anyNewScript(); MOZ_ASSERT(newScript); if (newScript->analyzed()) { @@ -2899,7 +2975,10 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement) MOZ_ASSERT(!replacement); } - setAddendum(Addendum_None, nullptr, writeBarrier); + if (this->newScript()) + setAddendum(Addendum_None, nullptr, writeBarrier); + else + unboxedLayout().setNewScript(nullptr, writeBarrier); } void @@ -2910,7 +2989,7 @@ ObjectGroup::maybeClearNewScriptOnOOM() if (!isMarked()) return; - TypeNewScript* newScript = this->newScript(); + TypeNewScript* newScript = anyNewScript(); if (!newScript) return; @@ -2925,7 +3004,7 @@ ObjectGroup::maybeClearNewScriptOnOOM() void ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/) { - TypeNewScript* newScript = this->newScript(); + TypeNewScript* newScript = anyNewScript(); if (!newScript) return; @@ -3332,7 +3411,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun, ///////////////////////////////////////////////////////////////////// void -PreliminaryObjectArray::registerNewObject(PlainObject* res) +PreliminaryObjectArray::registerNewObject(JSObject* res) { // The preliminary object pointers are weak, and won't be swept properly // during nursery collections, so the preliminary objects need to be @@ -3350,7 +3429,7 @@ PreliminaryObjectArray::registerNewObject(PlainObject* res) } void -PreliminaryObjectArray::unregisterObject(PlainObject* obj) +PreliminaryObjectArray::unregisterObject(JSObject* obj) { for (size_t i = 0; i < COUNT; i++) { if (objects[i] == obj) { @@ -3390,6 +3469,22 @@ 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; } } @@ -3489,11 +3584,17 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro } } - // 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()); + 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()); + } } ///////////////////////////////////////////////////////////////////// @@ -3507,6 +3608,7 @@ 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. @@ -3811,9 +3913,34 @@ 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 @@ -3927,6 +4054,12 @@ 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>()); @@ -4120,6 +4253,12 @@ 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; @@ -4203,6 +4342,21 @@ 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(); |