From a2b14f72a03fdf8d5eb8b6042633e5e91833ae2b Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Fri, 21 Jul 2017 07:49:53 -0700 Subject: [PATCH] Bug 1355109 - Add IC for property reads on xrays, r=jandem,bz. --HG-- extra : rebase_source : bc62ebf7f9c6fc006e95ede93087370ad7120303 --- js/src/jit/BaselineCacheIRCompiler.cpp | 84 ++++++++ js/src/jit/CacheIR.cpp | 150 ++++++++++++- js/src/jit/CacheIR.h | 33 ++- js/src/jit/CacheIRCompiler.cpp | 29 ++- js/src/jit/CacheIRCompiler.h | 2 +- js/src/jit/IonCacheIRCompiler.cpp | 86 ++++++++ js/src/jsfriendapi.cpp | 14 ++ js/src/jsfriendapi.h | 28 +++ js/xpconnect/src/XPCJSRuntime.cpp | 1 + js/xpconnect/tests/chrome/chrome.ini | 2 + js/xpconnect/tests/chrome/test_xrayic.xul | 76 +++++++ js/xpconnect/tests/mochitest/file_xrayic.html | 15 ++ js/xpconnect/tests/mochitest/mochitest.ini | 1 + js/xpconnect/wrappers/XrayWrapper.cpp | 202 +++++++++++++----- js/xpconnect/wrappers/XrayWrapper.h | 27 ++- 15 files changed, 672 insertions(+), 78 deletions(-) create mode 100644 js/xpconnect/tests/chrome/test_xrayic.xul create mode 100644 js/xpconnect/tests/mochitest/file_xrayic.html diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 0422fa2b64ba..867d5ac1283f 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -292,6 +292,42 @@ BaselineCacheIRCompiler::emitGuardCompartment() return true; } +bool +BaselineCacheIRCompiler::emitGuardAnyClass() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + Address testAddr(stubAddress(reader.stubOffset())); + + masm.loadObjGroup(obj, scratch); + masm.loadPtr(Address(scratch, ObjectGroup::offsetOfClasp()), scratch); + masm.branchPtr(Assembler::NotEqual, testAddr, scratch, failure->label()); + return true; +} + +bool +BaselineCacheIRCompiler::emitGuardHasProxyHandler() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + Address testAddr(stubAddress(reader.stubOffset())); + masm.loadPtr(testAddr, scratch); + + Address handlerAddr(obj, ProxyObject::offsetOfHandler()); + masm.branchPtr(Assembler::NotEqual, handlerAddr, scratch, failure->label()); + return true; +} + bool BaselineCacheIRCompiler::emitGuardSpecificObject() { @@ -367,6 +403,54 @@ BaselineCacheIRCompiler::emitGuardSpecificSymbol() return true; } +bool +BaselineCacheIRCompiler::emitGuardXrayExpandoShapeAndDefaultProto() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + bool hasExpando = reader.readBool(); + Address shapeWrapperAddress(stubAddress(reader.stubOffset())); + + AutoScratchRegister scratch(allocator, masm); + Maybe scratch2; + if (hasExpando) + scratch2.emplace(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), scratch); + Address holderAddress(scratch, sizeof(Value) * GetXrayJitInfo()->xrayHolderSlot); + Address expandoAddress(scratch, NativeObject::getFixedSlotOffset(GetXrayJitInfo()->holderExpandoSlot)); + + if (hasExpando) { + masm.branchTestObject(Assembler::NotEqual, holderAddress, failure->label()); + masm.unboxObject(holderAddress, scratch); + masm.branchTestObject(Assembler::NotEqual, expandoAddress, failure->label()); + masm.unboxObject(expandoAddress, scratch); + + // Unwrap the expando before checking its shape. + masm.loadPtr(Address(scratch, ProxyObject::offsetOfReservedSlots()), scratch); + masm.unboxObject(Address(scratch, detail::ProxyReservedSlots::offsetOfPrivateSlot()), scratch); + + masm.loadPtr(shapeWrapperAddress, scratch2.ref()); + LoadShapeWrapperContents(masm, scratch2.ref(), scratch2.ref(), failure->label()); + masm.branchTestObjShape(Assembler::NotEqual, scratch, scratch2.ref(), failure->label()); + + // The reserved slots on the expando should all be in fixed slots. + Address protoAddress(scratch, NativeObject::getFixedSlotOffset(GetXrayJitInfo()->expandoProtoSlot)); + masm.branchTestUndefined(Assembler::NotEqual, protoAddress, failure->label()); + } else { + Label done; + masm.branchTestObject(Assembler::NotEqual, holderAddress, &done); + masm.unboxObject(holderAddress, scratch); + masm.branchTestObject(Assembler::Equal, expandoAddress, failure->label()); + masm.bind(&done); + } + + return true; +} + bool BaselineCacheIRCompiler::emitLoadFixedSlotResult() { diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 620962c93c54..811084a32c27 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -16,6 +16,7 @@ #include "vm/SelfHosting.h" #include "jsobjinlines.h" +#include "jit/MacroAssembler-inl.h" #include "vm/EnvironmentObject-inl.h" #include "vm/UnboxedObject-inl.h" @@ -177,6 +178,8 @@ GetPropIRGenerator::tryAttachStub() return true; if (tryAttachCrossCompartmentWrapper(obj, objId, id)) return true; + if (tryAttachXrayCrossCompartmentWrapper(obj, objId, id)) + return true; if (tryAttachFunction(obj, objId, id)) return true; if (tryAttachProxy(obj, objId, id)) @@ -752,7 +755,7 @@ GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperan maybeEmitIdGuard(id); writer.guardIsProxy(objId); - writer.guardIsCrossCompartmentWrapper(objId); + writer.guardHasProxyHandler(objId, Wrapper::wrapperHandler(obj)); // Load the object wrapped by the CCW ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId); @@ -776,6 +779,114 @@ GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperan return true; } +static bool +GetXrayExpandoShapeWrapper(JSContext* cx, HandleObject xray, MutableHandleObject wrapper) +{ + Value v = GetProxyReservedSlot(xray, GetXrayJitInfo()->xrayHolderSlot); + if (v.isObject()) { + NativeObject* holder = &v.toObject().as(); + v = holder->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot); + if (v.isObject()) { + RootedNativeObject expando(cx, &UncheckedUnwrap(&v.toObject())->as()); + wrapper.set(NewWrapperWithObjectShape(cx, expando)); + return wrapper != nullptr; + } + } + wrapper.set(nullptr); + return true; +} + +bool +GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId, + HandleId id) +{ + if (!IsProxy(obj)) + return false; + + XrayJitInfo* info = GetXrayJitInfo(); + if (!info || !info->isCrossCompartmentXray(GetProxyHandler(obj))) + return false; + + if (!info->globalHasExclusiveExpandos(cx_->global())) + return false; + + RootedObject target(cx_, UncheckedUnwrap(obj)); + + RootedObject expandoShapeWrapper(cx_); + if (!GetXrayExpandoShapeWrapper(cx_, obj, &expandoShapeWrapper)) { + cx_->recoverFromOutOfMemory(); + return false; + } + + // Look for a getter we can call on the xray or its prototype chain. + Rooted desc(cx_); + RootedObject holder(cx_, obj); + AutoObjectVector prototypes(cx_); + AutoObjectVector prototypeExpandoShapeWrappers(cx_); + while (true) { + if (!GetOwnPropertyDescriptor(cx_, holder, id, &desc)) { + cx_->clearPendingException(); + return false; + } + if (desc.object()) + break; + if (!GetPrototype(cx_, holder, &holder)) { + cx_->clearPendingException(); + return false; + } + if (!holder || !IsProxy(holder) || !info->isCrossCompartmentXray(GetProxyHandler(holder))) + return false; + RootedObject prototypeExpandoShapeWrapper(cx_); + if (!GetXrayExpandoShapeWrapper(cx_, holder, &prototypeExpandoShapeWrapper) || + !prototypes.append(holder) || + !prototypeExpandoShapeWrappers.append(prototypeExpandoShapeWrapper)) + { + cx_->recoverFromOutOfMemory(); + return false; + } + } + if (!desc.isAccessorDescriptor()) + return false; + + RootedObject getter(cx_, desc.getterObject()); + if (!getter || !getter->is() || !getter->as().isNative()) + return false; + + maybeEmitIdGuard(id); + writer.guardIsProxy(objId); + writer.guardHasProxyHandler(objId, GetProxyHandler(obj)); + + // Load the object wrapped by the CCW + ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId); + + // Test the wrapped object's class. The properties held by xrays or their + // prototypes will be invariant for objects of a given class, except for + // changes due to xray expandos or xray prototype mutations. + writer.guardAnyClass(wrapperTargetId, target->getClass()); + + // Make sure the expandos on the xray and its prototype chain match up with + // what we expect. The expando shape needs to be consistent, to ensure it + // has not had any shadowing properties added, and the expando cannot have + // any custom prototype (xray prototypes are stable otherwise). + // + // We can only do this for xrays with exclusive access to their expandos + // (as we checked earlier), which store a pointer to their expando + // directly. Xrays in other compartments may share their expandos with each + // other and a VM call is needed just to find the expando. + writer.guardXrayExpandoShapeAndDefaultProto(objId, expandoShapeWrapper); + for (size_t i = 0; i < prototypes.length(); i++) { + JSObject* proto = prototypes[i]; + ObjOperandId protoId = writer.loadObject(proto); + writer.guardXrayExpandoShapeAndDefaultProto(protoId, prototypeExpandoShapeWrappers[i]); + } + + writer.callNativeGetterResult(objId, &getter->as()); + writer.typeMonitorResult(); + + trackAttached("XrayGetter"); + return true; +} + bool GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id, bool handleDOMProxies) @@ -3877,3 +3988,40 @@ CompareIRGenerator::trackNotAttached() } #endif } + +// Class which holds a shape pointer for use when caches might reference data in other zones. +static const Class shapeContainerClass = { + "ShapeContainer", + JSCLASS_HAS_RESERVED_SLOTS(1) +}; + +static const size_t SHAPE_CONTAINER_SLOT = 0; + +JSObject* +jit::NewWrapperWithObjectShape(JSContext* cx, HandleNativeObject obj) +{ + MOZ_ASSERT(cx->compartment() != obj->compartment()); + + RootedObject wrapper(cx); + { + AutoCompartment ac(cx, obj); + wrapper = NewObjectWithClassProto(cx, &shapeContainerClass, nullptr); + if (!obj) + return nullptr; + wrapper->as().setSlot(SHAPE_CONTAINER_SLOT, PrivateGCThingValue(obj->lastProperty())); + } + if (!JS_WrapObject(cx, &wrapper)) + return nullptr; + MOZ_ASSERT(IsWrapper(wrapper)); + return wrapper; +} + +void +jit::LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst, Label* failure) +{ + masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), dst); + Address privateAddr(dst, detail::ProxyReservedSlots::offsetOfPrivateSlot()); + masm.branchTestObject(Assembler::NotEqual, privateAddr, failure); + masm.unboxObject(privateAddr, dst); + masm.unboxNonDouble(Address(dst, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT)), dst); +} diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 29cde9abbb70..754bd4442a5f 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -168,11 +168,12 @@ extern const char* CacheKindNames[]; _(GuardShape) \ _(GuardGroup) \ _(GuardProto) \ - _(GuardClass) \ + _(GuardClass) /* Guard an object class, per GuardClassKind */ \ + _(GuardAnyClass) /* Guard an arbitrary class for an object */ \ _(GuardCompartment) \ _(GuardIsNativeFunction) \ _(GuardIsProxy) \ - _(GuardIsCrossCompartmentWrapper) \ + _(GuardHasProxyHandler) \ _(GuardNotDOMProxy) \ _(GuardSpecificObject) \ _(GuardSpecificAtom) \ @@ -188,6 +189,8 @@ extern const char* CacheKindNames[]; _(GuardAndGetIterator) \ _(GuardHasGetterSetter) \ _(GuardGroupHasUnanalyzedNewScript) \ + _(GuardIndexIsNonNegative) \ + _(GuardXrayExpandoShapeAndDefaultProto) \ _(LoadStackValue) \ _(LoadObject) \ _(LoadProto) \ @@ -349,6 +352,14 @@ enum class GuardClassKind : uint8_t JSFunction, }; +// Some ops refer to shapes that might be in other zones. Instead of putting +// cross-zone pointers in the caches themselves (which would complicate tracing +// enormously), these ops instead contain wrappers for objects in the target +// zone, which refer to the actual shape via a reserved slot. +JSObject* NewWrapperWithObjectShape(JSContext* cx, HandleNativeObject obj); + +void LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst, Label* failure); + // Class to record CacheIR + some additional metadata for code generation. class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { @@ -517,6 +528,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOpWithOperandId(CacheOp::GuardShape, obj); addStubField(uintptr_t(shape), StubField::Type::Shape); } + void guardXrayExpandoShapeAndDefaultProto(ObjOperandId obj, JSObject* shapeWrapper) { + writeOpWithOperandId(CacheOp::GuardXrayExpandoShapeAndDefaultProto, obj); + buffer_.writeByte(uint32_t(!!shapeWrapper)); addStubField(uintptr_t(shapeWrapper), StubField::Type::JSObject); + } void guardGroup(ObjOperandId obj, ObjectGroup* group) { writeOpWithOperandId(CacheOp::GuardGroup, obj); addStubField(uintptr_t(group), StubField::Type::ObjectGroup); @@ -531,6 +546,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOpWithOperandId(CacheOp::GuardClass, obj); buffer_.writeByte(uint32_t(kind)); } + void guardAnyClass(ObjOperandId obj, const Class* clasp) { + writeOpWithOperandId(CacheOp::GuardAnyClass, obj); + addStubField(uintptr_t(clasp), StubField::Type::RawWord); + } void guardIsNativeFunction(ObjOperandId obj, JSNative nativeFunc) { writeOpWithOperandId(CacheOp::GuardIsNativeFunction, obj); writePointer(JS_FUNC_TO_DATA_PTR(void*, nativeFunc)); @@ -538,8 +557,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter void guardIsProxy(ObjOperandId obj) { writeOpWithOperandId(CacheOp::GuardIsProxy, obj); } - void guardIsCrossCompartmentWrapper(ObjOperandId obj) { - writeOpWithOperandId(CacheOp::GuardIsCrossCompartmentWrapper, obj); + void guardHasProxyHandler(ObjOperandId obj, const void* handler) { + writeOpWithOperandId(CacheOp::GuardHasProxyHandler, obj); + addStubField(uintptr_t(handler), StubField::Type::RawWord); } void guardNotDOMProxy(ObjOperandId obj) { writeOpWithOperandId(CacheOp::GuardNotDOMProxy, obj); @@ -605,6 +625,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter addStubField(uintptr_t(group), StubField::Type::ObjectGroup); } + void guardIndexIsNonNegative(Int32OperandId index) { + writeOpWithOperandId(CacheOp::GuardIndexIsNonNegative, index); + } + void loadFrameCalleeResult() { writeOp(CacheOp::LoadFrameCalleeResult); } @@ -1137,6 +1161,7 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator bool tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId, HandleId id); + bool tryAttachXrayCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id, diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 7c8cd711eb1a..6f22d7a311df 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -1435,22 +1435,6 @@ CacheIRCompiler::emitGuardIsProxy() return true; } -bool -CacheIRCompiler::emitGuardIsCrossCompartmentWrapper() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - AutoScratchRegister scratch(allocator, masm); - - FailurePath* failure; - if (!addFailurePath(&failure)) - return false; - - Address handlerAddr(obj, ProxyObject::offsetOfHandler()); - masm.branchPtr(Assembler::NotEqual, handlerAddr, ImmPtr(&CrossCompartmentWrapper::singleton), - failure->label()); - return true; -} - bool CacheIRCompiler::emitGuardNotDOMProxy() { @@ -1927,6 +1911,19 @@ CacheIRCompiler::emitLoadDenseElementResult() return true; } +bool +CacheIRCompiler::emitGuardIndexIsNonNegative() +{ + Register index = allocator.useRegister(masm, reader.int32OperandId()); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.branch32(Assembler::LessThan, index, Imm32(0), failure->label()); + return true; +} + bool CacheIRCompiler::emitLoadDenseElementHoleResult() { diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index 94f55c7cff27..a1f6881b859b 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -24,7 +24,6 @@ namespace jit { _(GuardClass) \ _(GuardIsNativeFunction) \ _(GuardIsProxy) \ - _(GuardIsCrossCompartmentWrapper) \ _(GuardNotDOMProxy) \ _(GuardSpecificInt32Immediate) \ _(GuardMagicValue) \ @@ -33,6 +32,7 @@ namespace jit { _(GuardNoDetachedTypedObjects) \ _(GuardNoDenseElements) \ _(GuardAndGetIndexFromString) \ + _(GuardIndexIsNonNegative) \ _(LoadProto) \ _(LoadEnclosingEnvironment) \ _(LoadWrapperTarget) \ diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index ded559faeaa4..b05f5283d26d 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -106,6 +106,12 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler JSCompartment* compartmentStubField(uint32_t offset) { return (JSCompartment*)readStubWord(offset, StubField::Type::RawWord); } + const Class* classStubField(uintptr_t offset) { + return (const Class*)readStubWord(offset, StubField::Type::RawWord); + } + const void* proxyHandlerStubField(uintptr_t offset) { + return (const void*)readStubWord(offset, StubField::Type::RawWord); + } jsid idStubField(uint32_t offset) { return mozilla::BitwiseCast(readStubWord(offset, StubField::Type::Id)); } @@ -647,6 +653,37 @@ IonCacheIRCompiler::emitGuardCompartment() return true; } +bool +IonCacheIRCompiler::emitGuardAnyClass() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + AutoScratchRegister scratch(allocator, masm); + + const Class* clasp = classStubField(reader.stubOffset()); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, clasp, failure->label()); + return true; +} + +bool +IonCacheIRCompiler::emitGuardHasProxyHandler() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + const void* handler = proxyHandlerStubField(reader.stubOffset()); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + Address handlerAddr(obj, ProxyObject::offsetOfHandler()); + masm.branchPtr(Assembler::NotEqual, handlerAddr, ImmPtr(handler), failure->label()); + return true; +} + bool IonCacheIRCompiler::emitGuardSpecificObject() { @@ -720,6 +757,55 @@ IonCacheIRCompiler::emitGuardSpecificSymbol() return true; } +bool +IonCacheIRCompiler::emitGuardXrayExpandoShapeAndDefaultProto() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + bool hasExpando = reader.readBool(); + JSObject* shapeWrapper = objectStubField(reader.stubOffset()); + MOZ_ASSERT(hasExpando == !!shapeWrapper); + + AutoScratchRegister scratch(allocator, masm); + Maybe scratch2; + if (hasExpando) + scratch2.emplace(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), scratch); + Address holderAddress(scratch, sizeof(Value) * GetXrayJitInfo()->xrayHolderSlot); + Address expandoAddress(scratch, NativeObject::getFixedSlotOffset(GetXrayJitInfo()->holderExpandoSlot)); + + if (hasExpando) { + masm.branchTestObject(Assembler::NotEqual, holderAddress, failure->label()); + masm.unboxObject(holderAddress, scratch); + masm.branchTestObject(Assembler::NotEqual, expandoAddress, failure->label()); + masm.unboxObject(expandoAddress, scratch); + + // Unwrap the expando before checking its shape. + masm.loadPtr(Address(scratch, ProxyObject::offsetOfReservedSlots()), scratch); + masm.unboxObject(Address(scratch, detail::ProxyReservedSlots::offsetOfPrivateSlot()), scratch); + + masm.movePtr(ImmGCPtr(shapeWrapper), scratch2.ref()); + LoadShapeWrapperContents(masm, scratch2.ref(), scratch2.ref(), failure->label()); + masm.branchTestObjShape(Assembler::NotEqual, scratch, scratch2.ref(), failure->label()); + + // The reserved slots on the expando should all be in fixed slots. + Address protoAddress(scratch, NativeObject::getFixedSlotOffset(GetXrayJitInfo()->expandoProtoSlot)); + masm.branchTestUndefined(Assembler::NotEqual, protoAddress, failure->label()); + } else { + Label done; + masm.branchTestObject(Assembler::NotEqual, holderAddress, &done); + masm.unboxObject(holderAddress, scratch); + masm.branchTestObject(Assembler::Equal, expandoAddress, failure->label()); + masm.bind(&done); + } + + return true; +} + bool IonCacheIRCompiler::emitLoadFixedSlotResult() { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index beee64879487..d0ff5100fe67 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1305,6 +1305,20 @@ js::GetDOMProxyShadowsCheck() return gDOMProxyShadowsCheck; } +static XrayJitInfo* gXrayJitInfo = nullptr; + +JS_FRIEND_API(void) +js::SetXrayJitInfo(XrayJitInfo* info) +{ + gXrayJitInfo = info; +} + +XrayJitInfo* +js::GetXrayJitInfo() +{ + return gXrayJitInfo; +} + bool js::detail::IdMatchesAtom(jsid id, JSAtom* atom) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index e37d1ca6ca5b..fc490b1c79ce 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1319,6 +1319,34 @@ inline bool DOMProxyIsShadowing(DOMProxyShadowsResult result) { result == ShadowsViaIndirectExpando; } +// Callbacks and other information for use by the JITs when optimizing accesses +// on xray wrappers. +struct XrayJitInfo { + // Test whether a proxy handler is a cross compartment xray with no + // security checks. + bool (*isCrossCompartmentXray)(const BaseProxyHandler* handler); + + // Test whether xrays with a global object's compartment have expandos of + // their own, instead of sharing them with Xrays from other compartments. + bool (*globalHasExclusiveExpandos)(JSObject* obj); + + // Proxy reserved slot used by xrays in sandboxes to store their holder + // object. + size_t xrayHolderSlot; + + // Reserved slot used by xray holders to store the xray's expando object. + size_t holderExpandoSlot; + + // Reserved slot used by xray expandos to store a custom prototype. + size_t expandoProtoSlot; +}; + +JS_FRIEND_API(void) +SetXrayJitInfo(XrayJitInfo* info); + +XrayJitInfo* +GetXrayJitInfo(); + /* Implemented in jsdate.cpp. */ /** Detect whether the internal date value is NaN. */ diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index fbcd2af35e23..f16c4bc6829b 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2847,6 +2847,7 @@ XPCJSRuntime::Initialize(JSContext* cx) js::SetPreserveWrapperCallback(cx, PreserveWrapper); JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback); js::SetWindowProxyClass(cx, &OuterWindowProxyClass); + js::SetXrayJitInfo(&gXrayJitInfo); JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback); // The JS engine needs to keep the source code around in order to implement diff --git a/js/xpconnect/tests/chrome/chrome.ini b/js/xpconnect/tests/chrome/chrome.ini index 3a5ca2723e25..9396af5a9499 100644 --- a/js/xpconnect/tests/chrome/chrome.ini +++ b/js/xpconnect/tests/chrome/chrome.ini @@ -32,6 +32,7 @@ support-files = !/js/xpconnect/tests/mochitest/file_expandosharing.html !/js/xpconnect/tests/mochitest/file_nodelists.html !/js/xpconnect/tests/mochitest/file_evalInSandbox.html + !/js/xpconnect/tests/mochitest/file_xrayic.html [test_APIExposer.xul] [test_bug361111.xul] @@ -115,5 +116,6 @@ skip-if = os == 'win' || os == 'mac' || (os == 'linux' && !debug) # bug 1131110, [test_weakref.xul] [test_windowProxyDeadWrapper.html] [test_wrappers.xul] +[test_xrayic.xul] [test_xrayToJS.xul] [test_asyncIteration.xul] diff --git a/js/xpconnect/tests/chrome/test_xrayic.xul b/js/xpconnect/tests/chrome/test_xrayic.xul new file mode 100644 index 000000000000..d86a87f38836 --- /dev/null +++ b/js/xpconnect/tests/chrome/test_xrayic.xul @@ -0,0 +1,76 @@ + + + + + + +