diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 66284a9e90a2..332f8eae87e1 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -9353,24 +9353,28 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb // as a constructor, for later use during Ion compilation. RootedObject templateObject(cx); if (constructing) { - JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject); - if (!thisObject) - return false; + // If we are calling a constructor for which the new script + // properties analysis has not been performed yet, don't attach a + // stub. After the analysis is performed, CreateThisForFunction may + // start returning objects with a different type, and the Ion + // compiler will get confused. - if (thisObject->is() || thisObject->is()) { - templateObject = thisObject; + // Only attach a stub if the function already has a prototype and + // we can look it up without causing side effects. + RootedValue protov(cx); + if (!GetPropertyPure(cx, fun, NameToId(cx->names().prototype), protov.address())) { + JitSpew(JitSpew_BaselineIC, " Can't purely lookup function prototype"); + return true; + } - // If we are calling a constructor for which the new script - // properties analysis has not been performed yet, don't attach a - // stub. After the analysis is performed, CreateThisForFunction may - // start returning objects with a different type, and the Ion - // compiler might get confused. - TypeNewScript *newScript = templateObject->group()->newScript(); - if (newScript && !newScript->analyzed()) { - // Clear the object just created from the preliminary objects - // on the TypeNewScript, as it will not be used or filled in by - // running code. - newScript->unregisterNewObject(&templateObject->as()); + if (protov.isObject()) { + TaggedProto proto(&protov.toObject()); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, nullptr, proto, fun); + if (!group) + return false; + + if (group->newScript() && !group->newScript()->analyzed()) { + JitSpew(JitSpew_BaselineIC, " Function newScript has not been analyzed"); // This is temporary until the analysis is perfomed, so // don't treat this as unoptimizable. @@ -9378,6 +9382,13 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb return true; } } + + JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject); + if (!thisObject) + return false; + + if (thisObject->is() || thisObject->is()) + templateObject = thisObject; } JitSpew(JitSpew_BaselineIC, diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 92d87db1b573..ac8fcf0687ff 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1299,13 +1299,13 @@ ClassProtoKeyOrAnonymousOrNull(const js::Class *clasp) } static inline bool -NativeGetPureInline(NativeObject *pobj, Shape *shape, MutableHandleValue vp) +NativeGetPureInline(NativeObject *pobj, Shape *shape, Value *vp) { if (shape->hasSlot()) { - vp.set(pobj->getSlot(shape->slot())); - MOZ_ASSERT(!vp.isMagic()); + *vp = pobj->getSlot(shape->slot()); + MOZ_ASSERT(!vp->isMagic()); } else { - vp.setUndefined(); + vp->setUndefined(); } /* Fail if we have a custom getter. */ @@ -1344,7 +1344,7 @@ FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class return false; } else { Shape *shape = nctor->lookup(cx, cx->names().prototype); - if (!shape || !NativeGetPureInline(nctor, shape, &v)) + if (!shape || !NativeGetPureInline(nctor, shape, v.address())) return false; } if (v.isObject()) @@ -1478,6 +1478,7 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec parent == group->proto().toObject()->getParent() && newKind == GenericObject && group->clasp()->isNative() && + (!group->newScript() || group->newScript()->analyzed()) && !cx->compartment()->hasObjectMetadataCallback()) { if (cache.lookupGroup(group, allocKind, &entry)) { @@ -2136,7 +2137,7 @@ js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj) AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as().numFixedSlots())); MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind()); - JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx); + RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); if (!proto) return nullptr; RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, @@ -2144,8 +2145,17 @@ js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj) if (!group) return nullptr; - RootedShape shape(cx, srcObj->lastProperty()); - return NewReshapedObject(cx, group, parent, kind, shape); + RootedPlainObject res(cx, NewObjectWithGroup(cx, group, parent, kind, + MaybeSingletonObject)); + if (!res) + return nullptr; + + RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->lastProperty(), + TaggedProto(proto), parent, kind)); + if (!newShape || !NativeObject::setLastProperty(cx, res, newShape)) + return nullptr; + + return res; } RootedArrayObject srcArray(cx, &srcObj->as()); @@ -3000,6 +3010,16 @@ js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject ** return true; } +bool +js::GetPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, Value *vp) +{ + JSObject *pobj; + Shape *shape; + if (!LookupPropertyPure(cx, obj, id, &pobj, &shape)) + return false; + return pobj->isNative() && NativeGetPureInline(&pobj->as(), shape, vp); +} + bool JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 2cce9d57ccf8..c78aa41fbd77 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1199,6 +1199,9 @@ bool LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp, Shape **propp); +bool +GetPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, Value *vp); + bool GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandle desc); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 35f32dfe4a7e..2e79e3c6f542 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -643,11 +643,6 @@ NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent, return NewObjectWithGroup(cx, group, parent, allocKind, newKind); } -JSObject * -NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject parent, - gc::AllocKind allocKind, HandleShape shape, - NewObjectKind newKind = GenericObject); - /* * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of * the object, zero if the final size is unknown. This should only be used for diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 50df838d67f2..2655b1b36a6e 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -601,21 +601,17 @@ NativeObject::addPropertyInternal(ExclusiveContext *cx, return nullptr; } -JSObject * -js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject parent, - gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind) +Shape * +js::ReshapeForParentAndAllocKind(JSContext *cx, Shape *shape, TaggedProto proto, JSObject *parent, + gc::AllocKind allocKind) { - RootedPlainObject res(cx, NewObjectWithGroup(cx, group, parent, allocKind, newKind)); - if (!res) - return nullptr; + // Compute the number of fixed slots with the new allocation kind. + size_t nfixed = gc::GetGCKindSlots(allocKind, shape->getObjectClass()); - if (shape->isEmptyShape()) - return res; - - /* Get all the ids in the object, in order. */ + // Get all the ids in the shape, in order. js::AutoIdVector ids(cx); { - for (unsigned i = 0; i <= shape->slot(); i++) { + for (unsigned i = 0; i < shape->slotSpan(); i++) { if (!ids.append(JSID_VOID)) return nullptr; } @@ -626,17 +622,13 @@ js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject paren } } - /* Construct the new shape, without updating type information. */ + // Construct the new shape, without updating type information. RootedId id(cx); - RootedShape newShape(cx, EmptyShape::getInitialShape(cx, res->getClass(), - res->getTaggedProto(), - res->getParent(), - res->getMetadata(), - res->numFixedSlots(), - shape->getObjectFlags())); + RootedShape newShape(cx, EmptyShape::getInitialShape(cx, shape->getObjectClass(), + proto, parent, shape->getObjectMetadata(), + nfixed, shape->getObjectFlags())); for (unsigned i = 0; i < ids.length(); i++) { id = ids[i]; - MOZ_ASSERT(!res->contains(cx, id)); uint32_t index; bool indexed = js_IdIsIndex(id, &index); @@ -654,11 +646,9 @@ js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject paren newShape = cx->compartment()->propertyTree.getChild(cx, newShape, child); if (!newShape) return nullptr; - if (!NativeObject::setLastProperty(cx, res, newShape)) - return nullptr; } - return res; + return newShape; } /* diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index f93fcdde5204..2d3ca3f7b421 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -1553,6 +1553,10 @@ IsImplicitDenseOrTypedArrayElement(Shape *prop) return prop == reinterpret_cast(1); } +Shape * +ReshapeForParentAndAllocKind(JSContext *cx, Shape *shape, TaggedProto proto, JSObject *parent, + gc::AllocKind allocKind); + } // namespace js #ifdef _MSC_VER diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 2e01495cde5d..6496caadf09a 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3152,19 +3152,6 @@ PreliminaryObjectArray::registerNewObject(JSObject *res) MOZ_CRASH("There should be room for registering the new object"); } -void -PreliminaryObjectArray::unregisterNewObject(JSObject *res) -{ - for (size_t i = 0; i < COUNT; i++) { - if (objects[i] == res) { - objects[i] = nullptr; - return; - } - } - - MOZ_CRASH("The object should be one of the preliminary objects"); -} - bool PreliminaryObjectArray::full() const { @@ -3240,13 +3227,6 @@ TypeNewScript::registerNewObject(PlainObject *res) preliminaryObjects->registerNewObject(res); } -void -TypeNewScript::unregisterNewObject(PlainObject *res) -{ - MOZ_ASSERT(!analyzed()); - preliminaryObjects->unregisterNewObject(res); -} - // Return whether shape consists entirely of plain data properties. static bool OnlyHasDataProperties(Shape *shape) @@ -3294,15 +3274,13 @@ ChangeObjectFixedSlotCount(JSContext *cx, PlainObject *obj, gc::AllocKind allocK { MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty())); - // Make a clone of the object, with the new allocation kind. - RootedShape oldShape(cx, obj->lastProperty()); - RootedObjectGroup group(cx, obj->group()); - RootedObject parent(cx, obj->getParent()); - JSObject *clone = NewReshapedObject(cx, group, parent, allocKind, oldShape); - if (!clone) + Shape *newShape = ReshapeForParentAndAllocKind(cx, obj->lastProperty(), + obj->getTaggedProto(), obj->getParent(), + allocKind); + if (!newShape) return false; - obj->setLastPropertyShrinkFixedSlots(clone->lastProperty()); + obj->setLastPropertyShrinkFixedSlots(newShape); return true; } @@ -3490,10 +3468,15 @@ TypeNewScript::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, MOZ_ASSERT(group->unboxedLayout().newScript() == this); destroyNewScript.group = nullptr; - // Clear out the template object. This is not used for TypeNewScripts - // with an unboxed layout, and additionally this template is now a - // mutant object with a non-native class and native shape, and must be - // collected by the next GC. + // 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. + ObjectGroup *plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, + group->proto()); + if (!plainGroup) + CrashAtUnhandlableOOM("TypeNewScript::maybeAnalyze"); + templateObject_->setGroup(plainGroup); templateObject_ = nullptr; return true; diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index ac618d05ce04..96d5ee7a9516 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -753,7 +753,6 @@ class PreliminaryObjectArray } void registerNewObject(JSObject *res); - void unregisterNewObject(JSObject *res); JSObject *get(size_t i) const { MOZ_ASSERT(i < COUNT); @@ -889,7 +888,6 @@ class TypeNewScript void sweep(); void registerNewObject(PlainObject *res); - void unregisterNewObject(PlainObject *res); bool maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, bool force = false); bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group);