diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 991d7735c681..7a906900d31f 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1700,7 +1700,7 @@ TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, return obj_lookupElement(cx, obj, index, objp, propp); if (JSID_IS_ATOM(id, cx->names().length)) { - MarkNonNativePropertyFound(propp); + MarkNonNativePropertyFound(propp); objp.set(obj); return true; } @@ -1712,7 +1712,7 @@ TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, StructTypeDescr &structDescr = descr->as(); size_t index; if (structDescr.fieldIndex(id, &index)) { - MarkNonNativePropertyFound(propp); + MarkNonNativePropertyFound(propp); objp.set(obj); return true; } @@ -1746,7 +1746,7 @@ TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index, MutableHandleObject objp, MutableHandleShape propp) { MOZ_ASSERT(obj->is()); - MarkNonNativePropertyFound(propp); + MarkNonNativePropertyFound(propp); objp.set(obj); return true; } diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 32505e3463e9..98cfde4bc9d4 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1177,9 +1177,17 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca // of turn. We don't mind doing this even when purity isn't required, because we // only miss out on shape hashification, which is only a temporary perf cost. // The limits were arbitrarily set, anyways. - if (!LookupPropertyPure(cx, obj, NameToId(name), holder.address(), shape.address())) + JSObject *baseHolder = nullptr; + if (!LookupPropertyPure(cx, obj, NameToId(name), &baseHolder, shape.address())) return GetPropertyIC::CanAttachNone; + MOZ_ASSERT(!holder); + if (baseHolder) { + if (!baseHolder->isNative()) + return GetPropertyIC::CanAttachNone; + holder.set(&baseHolder->as()); + } + RootedScript script(cx); jsbytecode *pc; cache.getScriptedLocation(&script, &pc); @@ -2645,7 +2653,7 @@ IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val, static SetPropertyIC::NativeSetPropCacheability CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val, - bool needsTypeBarrier, MutableHandleNativeObject holder, + bool needsTypeBarrier, MutableHandleObject holder, MutableHandleShape shape, bool *checkTypeset) { if (!obj->isNative()) @@ -2723,7 +2731,7 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, } RootedShape shape(cx); - RootedNativeObject holder(cx); + RootedObject holder(cx); bool checkTypeset; canCache = CanAttachNativeSetProp(cx, obj, id, cache.value(), cache.needsTypeBarrier(), &holder, &shape, &checkTypeset); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 47b1ff592ce8..193aa00e1673 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3047,79 +3047,73 @@ js::HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *result) return true; } -static MOZ_ALWAYS_INLINE bool -LookupPropertyPureInline(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp, - Shape **propp) +bool +js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp, + Shape **propp) { - if (!obj->isNative()) - return false; + do { + if (obj->isNative()) { + /* Search for a native dense element, typed array element, or property. */ - NativeObject *current = &obj->as(); - while (true) { - /* Search for a native dense element, typed array element, or property. */ + if (JSID_IS_INT(id) && obj->as().containsDenseElement(JSID_TO_INT(id))) { + *objp = obj; + MarkDenseOrTypedArrayElementFound(propp); + return true; + } - if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) { - *objp = current; - MarkDenseOrTypedArrayElementFound(propp); - return true; - } - - if (IsAnyTypedArray(current)) { - uint64_t index; - if (IsTypedArrayIndex(id, &index)) { - if (index < AnyTypedArrayLength(obj)) { - *objp = current; - MarkDenseOrTypedArrayElementFound(propp); - } else { - *objp = nullptr; - *propp = nullptr; + if (IsAnyTypedArray(obj)) { + uint64_t index; + if (IsTypedArrayIndex(id, &index)) { + if (index < AnyTypedArrayLength(obj)) { + *objp = obj; + MarkDenseOrTypedArrayElementFound(propp); + } else { + *objp = nullptr; + *propp = nullptr; + } + return true; } + } + + if (Shape *shape = obj->as().lookupPure(id)) { + *objp = obj; + *propp = shape; + return true; + } + + // Fail if there's a resolve hook. We allow the JSFunction resolve hook + // if we know it will never add a property with this name or str_resolve + // with a non-integer property. + do { + const Class *clasp = obj->getClass(); + if (!clasp->resolve) + break; + if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id)) + break; + if (clasp->resolve == str_resolve && !JSID_IS_INT(id)) + break; + return false; + } while (0); + } else { + // Search for a property on an unboxed object. Other non-native objects + // are not handled here. + if (!obj->is()) + return false; + if (obj->as().layout().lookup(id)) { + *objp = obj; + MarkNonNativePropertyFound(propp); return true; } } - if (Shape *shape = current->lookupPure(id)) { - *objp = current; - *propp = shape; - return true; - } - - // Fail if there's a resolve hook. We allow the JSFunction resolve hook - // if we know it will never add a property with this name or str_resolve - // with a non-integer property. - do { - const Class *clasp = current->getClass(); - if (!clasp->resolve) - break; - if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id)) - break; - if (clasp->resolve == str_resolve && !JSID_IS_INT(id)) - break; - return false; - } while (0); - - JSObject *proto = current->getProto(); - - if (!proto) - break; - if (!proto->isNative()) - return false; - - current = &proto->as(); - } + obj = obj->getProto(); + } while (obj); *objp = nullptr; *propp = nullptr; return true; } -bool -js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp, - Shape **propp) -{ - return LookupPropertyPureInline(cx, obj, id, objp, propp); -} - bool JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 31563a76638e..20e8d23c578d 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1226,7 +1226,7 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp); namespace js { bool -LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp, +LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp, Shape **propp); bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 3c63d35e2358..d7bba22c1dba 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -559,7 +559,7 @@ js::proxy_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id, return false; if (found) { - MarkNonNativePropertyFound(propp); + MarkNonNativePropertyFound(propp); objp.set(obj); } else { objp.set(nullptr); diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index b2937ce63313..13e5a5535666 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -332,6 +332,7 @@ NativeObject::setLastProperty(ExclusiveContext *cx, HandleNativeObject obj, Hand MOZ_ASSERT(!shape->inDictionary()); MOZ_ASSERT(shape->compartment() == obj->compartment()); MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots()); + MOZ_ASSERT(shape->getObjectClass() == obj->getClass()); size_t oldSpan = obj->lastProperty()->slotSpan(); size_t newSpan = shape->slotSpan(); @@ -355,6 +356,7 @@ NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape) MOZ_ASSERT(!shape->inDictionary()); MOZ_ASSERT(shape->compartment() == compartment()); MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan()); + MOZ_ASSERT(shape->getObjectClass() == getClass()); DebugOnly oldFixed = numFixedSlots(); DebugOnly newFixed = shape->numFixedSlots(); @@ -381,6 +383,30 @@ NativeObject::setLastPropertyMakeNonNative(Shape *shape) shape_ = shape; } +/* static */ void +NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj, + HandleShape shape) +{ + MOZ_ASSERT(obj->getClass()->isNative()); + MOZ_ASSERT(!obj->lastProperty()->isNative()); + MOZ_ASSERT(shape->isNative()); + MOZ_ASSERT(!obj->inDictionaryMode()); + MOZ_ASSERT(!shape->inDictionary()); + MOZ_ASSERT(shape->compartment() == obj->compartment()); + + obj->shape_ = shape; + obj->slots_ = nullptr; + obj->elements_ = emptyObjectElements; + + size_t oldSpan = shape->numFixedSlots(); + size_t newSpan = shape->slotSpan(); + + // A failures at this point will leave the object as a mutant, and we + // can't recover. + if (oldSpan != newSpan && !updateSlotsForSpan(cx, obj, oldSpan, newSpan)) + CrashAtUnhandlableOOM("NativeObject::setLastPropertyMakeNative"); +} + /* static */ bool NativeObject::setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span) { diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 4179e22264bb..6acf9ee0ec4a 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -417,6 +417,12 @@ class NativeObject : public JSObject // 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. + static void setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj, + HandleShape shape); + protected: #ifdef DEBUG void checkShapeConsistency(); diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 2710c0a80251..376b055b718c 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -1495,8 +1495,9 @@ template<> struct RootKind : SpecificRootKind static inline void -MarkNonNativePropertyFound(MutableHandleShape propp) +MarkNonNativePropertyFound(typename MaybeRooted::MutableHandleType propp) { propp.set(reinterpret_cast(1)); } diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index ee6a03751d80..ff276be1c860 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -196,15 +196,11 @@ UnboxedPlainObject::convertToNative(JSContext *cx) if (!SetClassAndProto(cx, obj, &PlainObject::class_, proto)) return false; - // Any failures after this point will leave the object as a mutant, and we - // can't recover. - - RootedPlainObject nobj(cx, &obj->as()); - if (!nobj->setLastProperty(cx, nobj, shape)) - CrashAtUnhandlableOOM("UnboxedPlainObject::convertToNative"); + RootedNativeObject nobj(cx, &obj->as()); + NativeObject::setLastPropertyMakeNative(cx, nobj, shape); for (size_t i = 0; i < values.length(); i++) - nobj->initSlot(i, values[i]); + nobj->initSlotUnchecked(i, values[i]); return true; } @@ -250,7 +246,7 @@ UnboxedPlainObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, MutableHandleShape propp) { if (obj->as().layout().lookup(id)) { - MarkNonNativePropertyFound(propp); + MarkNonNativePropertyFound(propp); objp.set(obj); return true; } diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index ac0778ebe7d5..969e14b66b15 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -28,6 +28,12 @@ UnboxedTypeSize(JSValueType type) } } +static inline bool +UnboxedTypeNeedsPreBarrier(JSValueType type) +{ + return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; +} + // Class describing the layout of an UnboxedPlainObject. class UnboxedLayout {