diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index 915daa9d928..0a896afca0b 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -479,8 +479,20 @@ BaselineInspector::templateCallObject() return &res->as(); } +static Shape *GlobalShapeForGetPropFunction(ICStub *stub) +{ + if (stub->isGetProp_CallNativePrototype()) { + ICGetProp_CallNativePrototype *nstub = + stub->toGetProp_CallNativePrototype(); + if (nstub->receiverShape()->getObjectClass()->flags & JSCLASS_IS_GLOBAL) + return nstub->receiverShape(); + } + return nullptr; +} + JSObject * -BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter) +BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter, + Shape **globalShape) { if (!hasBaselineScript()) return nullptr; @@ -489,6 +501,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J JSObject* holder = nullptr; Shape *holderShape = nullptr; JSFunction *getter = nullptr; + Shape *global = nullptr; for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isGetProp_CallScripted() || stub->isGetProp_CallNative() || @@ -499,7 +512,10 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J holder = nstub->holder(); holderShape = nstub->holderShape(); getter = nstub->getter(); - } else if (nstub->holderShape() != holderShape) { + global = GlobalShapeForGetPropFunction(nstub); + } else if (nstub->holderShape() != holderShape || + GlobalShapeForGetPropFunction(nstub) != global) + { return nullptr; } else { MOZ_ASSERT(getter == nstub->getter()); @@ -513,6 +529,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J } *lastProperty = holderShape; *commonGetter = getter; + *globalShape = global; return holder; } diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 9520c5be5b2..40f83042782 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -115,7 +115,8 @@ class BaselineInspector DeclEnvObject *templateDeclEnvObject(); CallObject *templateCallObject(); - JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter); + JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter, + Shape **globalShape); JSObject *commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter); }; diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index e105864a2b0..985509f9ee4 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -8770,7 +8770,7 @@ IonBuilder::jsop_not() bool IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, - bool isGetter, JSObject *foundProto) + bool isGetter, JSObject *foundProto, bool *guardGlobal) { // With foundProto a prototype with a getter or setter for name, return // whether looking up name on any object in |types| will go through @@ -8780,6 +8780,7 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN // No sense looking if we don't know what's going on. if (!types || types->unknownObject()) return false; + *guardGlobal = false; for (unsigned i = 0; i < types->getObjectCount(); i++) { if (types->getSingleObject(i) == foundProto) @@ -8794,8 +8795,14 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN return false; const Class *clasp = type->clasp(); - if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name)) + if (!ClassHasEffectlessLookup(clasp, name)) return false; + JSObject *singleton = type->singleton(); + if (ClassHasResolveHook(compartment, clasp, name)) { + if (!singleton || !singleton->is()) + return false; + *guardGlobal = true; + } // Look for a getter/setter on the class itself which may need // to be called. Ignore the getGeneric hook for typed arrays, it @@ -8813,9 +8820,11 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN if (!types->empty() || types->nonDataProperty()) return false; } - if (JSObject *obj = type->singleton()) { - if (types::CanHaveEmptyPropertyTypesForOwnProperty(obj)) - return false; + if (singleton) { + if (types::CanHaveEmptyPropertyTypesForOwnProperty(singleton)) { + MOZ_ASSERT(singleton->is()); + *guardGlobal = true; + } } if (!type->hasTenuredProto()) @@ -8837,7 +8846,8 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN void IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, - JSObject *foundProto) + JSObject *foundProto, + bool allowEmptyTypesforGlobal/* = false*/) { for (unsigned i = 0; i < types->getObjectCount(); i++) { // If we found a Singleton object's own-property, there's nothing to @@ -8851,7 +8861,7 @@ IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, P while (true) { types::HeapTypeSetKey property = type->property(NameToId(name)); - JS_ALWAYS_TRUE(!property.isOwnProperty(constraints())); + JS_ALWAYS_TRUE(!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal)); // Don't mark the proto. It will be held down by the shape // guard. This allows us to use properties found on prototypes @@ -8865,21 +8875,34 @@ IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, P inline MDefinition * IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name, - bool isGetter, JSObject *foundProto, Shape *lastProperty) + bool isGetter, JSObject *foundProto, Shape *lastProperty, + Shape *globalShape/* = nullptr*/) { + bool guardGlobal; + // Check if all objects being accessed will lookup the name through foundProto. - if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto)) + if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto, &guardGlobal) || + (guardGlobal && !globalShape)) + { return nullptr; + } // We can optimize the getter/setter, so freeze all involved properties to // ensure there isn't a lower shadowing getter or setter installed in the // future. - freezePropertiesForCommonPrototype(types, name, foundProto); + freezePropertiesForCommonPrototype(types, name, foundProto, guardGlobal); // Add a shape guard on the prototype we found the property on. The rest of - // the prototype chain is guarded by TI freezes. Note that a shape guard is - // good enough here, even in the proxy case, because we have ensured there - // are no lookup hooks for this property. + // the prototype chain is guarded by TI freezes, except when name is a global + // name. In this case, we also have to guard on the globals shape to be able + // to optimize. Note that a shape guard is good enough here, even in the proxy + // case, because we have ensured there are no lookup hooks for this property. + if (guardGlobal) { + JSObject *obj = &script()->global(); + MDefinition *globalObj = constant(ObjectValue(*obj)); + addShapeGuard(globalObj, globalShape, Bailout_ShapeGuard); + } + MInstruction *wrapper = constant(ObjectValue(*foundProto)); return addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard); } @@ -9407,13 +9430,14 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName Shape *lastProperty = nullptr; JSFunction *commonGetter = nullptr; - JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter); + Shape *globalShape = nullptr; + JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter, &globalShape); if (!foundProto) return true; types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); MDefinition *guard = testCommonGetterSetter(objTypes, name, /* isGetter = */ true, - foundProto, lastProperty); + foundProto, lastProperty, globalShape); if (!guard) return true; diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index c1b4b84b9b0..3b66ebd7f6b 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -807,11 +807,12 @@ class IonBuilder MBasicBlock *bottom); bool objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, - bool isGetter, JSObject *foundProto); + bool isGetter, JSObject *foundProto, bool *guardGlobal); void freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, - JSObject *foundProto); + JSObject *foundProto, bool allowEmptyTypesForGlobal = false); MDefinition *testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name, - bool isGetter, JSObject *foundProto, Shape *lastProperty); + bool isGetter, JSObject *foundProto, Shape *lastProperty, + Shape *globalShape = nullptr); bool testShouldDOMCall(types::TypeSet *inTypes, JSFunction *func, JSJitInfo::OpType opType); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 39ed1d04503..8ef72b53d31 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -1446,11 +1446,14 @@ HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints) } bool -HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints) +HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints, + bool allowEmptyTypesForGlobal/* = false*/) { if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty())) return true; - if (JSObject *obj = object()->singleton()) { + JSObject *obj = object()->singleton(); + MOZ_ASSERT_IF(obj, CanHaveEmptyPropertyTypesForOwnProperty(obj) == obj->is()); + if (obj && !allowEmptyTypesForGlobal) { if (CanHaveEmptyPropertyTypesForOwnProperty(obj)) return true; } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index dc18e236d06..a0286be2066 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -1566,7 +1566,7 @@ class HeapTypeSetKey jit::MIRType knownMIRType(CompilerConstraintList *constraints); bool nonData(CompilerConstraintList *constraints); bool nonWritable(CompilerConstraintList *constraints); - bool isOwnProperty(CompilerConstraintList *constraints); + bool isOwnProperty(CompilerConstraintList *constraints, bool allowEmptyTypesForGlobal = false); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); JSObject *singleton(CompilerConstraintList *constraints); bool needsBarrier(CompilerConstraintList *constraints);