зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1355109 - Add IC for property reads on xrays, r=jandem,bz.
--HG-- extra : rebase_source : bc62ebf7f9c6fc006e95ede93087370ad7120303
This commit is contained in:
Родитель
2ee7157f0e
Коммит
a2b14f72a0
|
@ -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<AutoScratchRegister> 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()
|
||||
{
|
||||
|
|
|
@ -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<NativeObject>();
|
||||
v = holder->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot);
|
||||
if (v.isObject()) {
|
||||
RootedNativeObject expando(cx, &UncheckedUnwrap(&v.toObject())->as<NativeObject>());
|
||||
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<PropertyDescriptor> 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<JSFunction>() || !getter->as<JSFunction>().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<JSFunction>());
|
||||
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<NativeObject>().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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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<jsid>(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<AutoScratchRegister> 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()
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1355109
|
||||
-->
|
||||
<window title="Mozilla Bug 1355109"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=758415"
|
||||
target="_blank">Mozilla Bug 758415</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
const Cu = Components.utils;
|
||||
|
||||
// Import our test JSM. We first strip the filename off
|
||||
// the chrome url, then append the jsm filename.
|
||||
var base = /.*\//.exec(window.location.href)[0];
|
||||
Cu.import(base + "file_expandosharing.jsm");
|
||||
|
||||
// Wait for all child frames to load.
|
||||
var gLoadCount = 0;
|
||||
function frameLoaded() {
|
||||
if (++gLoadCount == window.frames.length)
|
||||
go();
|
||||
}
|
||||
|
||||
function go() {
|
||||
testSandbox(1);
|
||||
testSandbox(100);
|
||||
testSandbox(1000);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function testSandbox(iterations) {
|
||||
var sandbox = new Cu.Sandbox(window);
|
||||
sandbox.iframes = document.getElementsByTagName('iframe');
|
||||
Cu.evalInSandbox(testClassName.toSource(), sandbox);
|
||||
Cu.evalInSandbox(testIC.toSource(), sandbox);
|
||||
is(Cu.evalInSandbox("testIC(" + iterations + ");", sandbox), true, "sandbox test");
|
||||
}
|
||||
|
||||
// This is in a separate function to provide a common source location for ICs.
|
||||
function testClassName(obj, expected) {
|
||||
var className = obj.className;
|
||||
if (className != expected)
|
||||
throw new Error("Got " + className + ", expected " + expected);
|
||||
}
|
||||
|
||||
function testIC(iterations) {
|
||||
for (var i = 0; i < this.iframes.length; i++) {
|
||||
var win = this.iframes[i].contentWindow;
|
||||
var spans = win.document.getElementsByTagName('span');
|
||||
for (var j = 0; j < spans.length; j++) {
|
||||
var span = spans[j];
|
||||
for (var k = 0; k < iterations; k++)
|
||||
testClassName(span, "iamaspan");
|
||||
Object.defineProperty(span, "className", { value: "what" });
|
||||
testClassName(span, "what");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
]]>
|
||||
</script>
|
||||
<iframe id="inlineFrame1" onload="frameLoaded();" type="content" src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_xrayic.html" />
|
||||
<iframe id="inlineFrame2" onload="frameLoaded();" type="content" src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_xrayic.html" />
|
||||
</window>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript">
|
||||
function setup() {
|
||||
// Set up targets for sandbox expandos.
|
||||
window.targetDOM = [document.getElementById("hello"), document.getElementById("there")];
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="setup();">
|
||||
<span id="hello" class="iamaspan">Hello</span>
|
||||
<span id="there" class="iamaspan">There</span>
|
||||
</body>
|
||||
</html>
|
|
@ -34,6 +34,7 @@ support-files =
|
|||
file_matches.html
|
||||
file_nodelists.html
|
||||
file_wrappers-2.html
|
||||
file_xrayic.html
|
||||
inner.html
|
||||
test1_bug629331.html
|
||||
test2_bug629331.html
|
||||
|
|
|
@ -1054,6 +1054,23 @@ GetXrayTraits(JSObject* obj)
|
|||
* them. They are private to the origin that placed them.
|
||||
*/
|
||||
|
||||
// Certain globals do not share expandos with other globals. Xrays in these
|
||||
// globals cache expandos on the wrapper's holder, as there is only one such
|
||||
// wrapper which can create or access the expando. This allows for faster
|
||||
// access to the expando, including through JIT inline caches.
|
||||
static inline bool
|
||||
GlobalHasExclusiveExpandos(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(JS_IsGlobalObject(obj));
|
||||
return !strcmp(js::GetObjectJSClass(obj)->name, "Sandbox");
|
||||
}
|
||||
|
||||
static inline JSObject*
|
||||
GetCachedXrayExpando(JSObject* wrapper);
|
||||
|
||||
static inline void
|
||||
SetCachedXrayExpando(JSObject* holder, JSObject* expandoWrapper);
|
||||
|
||||
static nsIPrincipal*
|
||||
ObjectPrincipal(JSObject* obj)
|
||||
{
|
||||
|
@ -1084,8 +1101,7 @@ const JSClassOps XrayExpandoObjectClassOps = {
|
|||
bool
|
||||
XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
|
||||
HandleObject expandoObject,
|
||||
nsIPrincipal* consumerOrigin,
|
||||
HandleObject exclusiveGlobal)
|
||||
nsIPrincipal* consumerOrigin)
|
||||
{
|
||||
MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx));
|
||||
|
||||
|
@ -1101,39 +1117,50 @@ XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
|
|||
if (!consumerOrigin->Equals(o))
|
||||
return false;
|
||||
|
||||
// Sandboxes want exclusive expando objects.
|
||||
// Certain globals exclusively own the associated expandos, in which case
|
||||
// the caller should have used the cached expando on the wrapper instead.
|
||||
JSObject* owner = JS_GetReservedSlot(expandoObject,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER)
|
||||
.toObjectOrNull();
|
||||
if (!owner && !exclusiveGlobal)
|
||||
return true;
|
||||
|
||||
// The exclusive global should always be wrapped in the target's compartment.
|
||||
MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
|
||||
MOZ_ASSERT(!owner || js::IsObjectInContextCompartment(owner, cx));
|
||||
return owner == exclusiveGlobal;
|
||||
return owner == nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
XrayTraits::getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
|
||||
HandleObject exclusiveWrapper,
|
||||
nsIPrincipal* origin,
|
||||
JSObject* exclusiveGlobalArg,
|
||||
MutableHandleObject expandoObject)
|
||||
{
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
expandoObject.set(nullptr);
|
||||
|
||||
// Use the cached expando if this wrapper has exclusive access to it.
|
||||
if (exclusiveWrapper) {
|
||||
JSObject* expandoWrapper = GetCachedXrayExpando(exclusiveWrapper);
|
||||
expandoObject.set(expandoWrapper ? UncheckedUnwrap(expandoWrapper) : nullptr);
|
||||
#ifdef DEBUG
|
||||
// Make sure the expando we found is on the target's chain. While we
|
||||
// don't use this chain to look up expandos for the wrapper,
|
||||
// the expando still needs to be on the chain to keep the wrapper and
|
||||
// expando alive.
|
||||
if (expandoObject) {
|
||||
JSObject* head = expandoChain;
|
||||
while (head && head != expandoObject)
|
||||
head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
||||
MOZ_ASSERT(head == expandoObject);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// The expando object lives in the compartment of the target, so all our
|
||||
// work needs to happen there.
|
||||
RootedObject exclusiveGlobal(cx, exclusiveGlobalArg);
|
||||
RootedObject head(cx, expandoChain);
|
||||
JSAutoCompartment ac(cx, head);
|
||||
if (!JS_WrapObject(cx, &exclusiveGlobal))
|
||||
return false;
|
||||
|
||||
// Iterate through the chain, looking for a same-origin object.
|
||||
while (head) {
|
||||
if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) {
|
||||
if (expandoObjectMatchesConsumer(cx, head, origin)) {
|
||||
expandoObject.set(head);
|
||||
return true;
|
||||
}
|
||||
|
@ -1155,19 +1182,35 @@ XrayTraits::getExpandoObject(JSContext* cx, HandleObject target, HandleObject co
|
|||
return true;
|
||||
|
||||
JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer);
|
||||
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
||||
return getExpandoObjectInternal(cx, chain, ObjectPrincipal(consumer),
|
||||
isSandbox ? consumerGlobal : nullptr,
|
||||
expandoObject);
|
||||
bool isExclusive = GlobalHasExclusiveExpandos(consumerGlobal);
|
||||
return getExpandoObjectInternal(cx, chain, isExclusive ? consumer : nullptr,
|
||||
ObjectPrincipal(consumer), expandoObject);
|
||||
}
|
||||
|
||||
// Wrappers which have exclusive access to the expando on their target object
|
||||
// need to be kept alive as long as the target object exists. This is done by
|
||||
// keeping the expando in the expando chain on the target (even though it will
|
||||
// not be used while looking up the expando for the wrapper), and keeping a
|
||||
// strong reference from that expando to the wrapper itself, via the
|
||||
// JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER reserved slot. This slot does not
|
||||
// point to the wrapper itself, because it is a cross compartment edge and we
|
||||
// can't create a wrapper for a wrapper. Instead, the slot points to an
|
||||
// instance of the holder class below in the wrapper's compartment, and the
|
||||
// wrapper is held via this holder object's reserved slot.
|
||||
static const JSClass gWrapperHolderClass = {
|
||||
"XrayExpandoWrapperHolder",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1)
|
||||
};
|
||||
static const size_t JSSLOT_WRAPPER_HOLDER_CONTENTS = 0;
|
||||
|
||||
JSObject*
|
||||
XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
||||
nsIPrincipal* origin, HandleObject exclusiveGlobal)
|
||||
HandleObject exclusiveWrapper,
|
||||
nsIPrincipal* origin)
|
||||
{
|
||||
// Make sure the compartments are sane.
|
||||
MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
|
||||
MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
|
||||
MOZ_ASSERT_IF(exclusiveWrapper, !js::IsObjectInContextCompartment(exclusiveWrapper, cx));
|
||||
|
||||
// No duplicates allowed.
|
||||
#ifdef DEBUG
|
||||
|
@ -1175,7 +1218,7 @@ XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
|||
JSObject* chain = getExpandoChain(target);
|
||||
if (chain) {
|
||||
RootedObject existingExpandoObject(cx);
|
||||
if (getExpandoObjectInternal(cx, chain, origin, exclusiveGlobal, &existingExpandoObject))
|
||||
if (getExpandoObjectInternal(cx, chain, exclusiveWrapper, origin, &existingExpandoObject))
|
||||
MOZ_ASSERT(!existingExpandoObject);
|
||||
else
|
||||
JS_ClearPendingException(cx);
|
||||
|
@ -1195,9 +1238,31 @@ XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
|||
NS_ADDREF(origin);
|
||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin));
|
||||
|
||||
// Note the exclusive global, if any.
|
||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
||||
ObjectOrNullValue(exclusiveGlobal));
|
||||
// Note the exclusive wrapper, if there is one.
|
||||
RootedObject wrapperHolder(cx);
|
||||
if (exclusiveWrapper) {
|
||||
JSAutoCompartment ac(cx, exclusiveWrapper);
|
||||
wrapperHolder = JS_NewObjectWithGivenProto(cx, &gWrapperHolderClass, nullptr);
|
||||
if (!wrapperHolder)
|
||||
return nullptr;
|
||||
JS_SetReservedSlot(wrapperHolder, JSSLOT_WRAPPER_HOLDER_CONTENTS, ObjectValue(*exclusiveWrapper));
|
||||
}
|
||||
if (!JS_WrapObject(cx, &wrapperHolder))
|
||||
return nullptr;
|
||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER,
|
||||
ObjectOrNullValue(wrapperHolder));
|
||||
|
||||
// Store it on the exclusive wrapper, if there is one.
|
||||
if (exclusiveWrapper) {
|
||||
RootedObject cachedExpandoObject(cx, expandoObject);
|
||||
JSAutoCompartment ac(cx, exclusiveWrapper);
|
||||
if (!JS_WrapObject(cx, &cachedExpandoObject))
|
||||
return nullptr;
|
||||
JSObject* holder = ensureHolder(cx, exclusiveWrapper);
|
||||
if (!holder)
|
||||
return nullptr;
|
||||
SetCachedXrayExpando(holder, cachedExpandoObject);
|
||||
}
|
||||
|
||||
// If this is our first expando object, take the opportunity to preserve
|
||||
// the wrapper. This keeps our expandos alive even if the Xray wrapper gets
|
||||
|
@ -1223,17 +1288,10 @@ XrayTraits::ensureExpandoObject(JSContext* cx, HandleObject wrapper,
|
|||
if (!getExpandoObject(cx, target, wrapper, &expandoObject))
|
||||
return nullptr;
|
||||
if (!expandoObject) {
|
||||
// If the object is a sandbox, we don't want it to share expandos with
|
||||
// anyone else, so we tag it with the sandbox global.
|
||||
//
|
||||
// NB: We first need to check the class, _then_ wrap for the target's
|
||||
// compartment.
|
||||
RootedObject consumerGlobal(cx, js::GetGlobalForObjectCrossCompartment(wrapper));
|
||||
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
||||
if (!JS_WrapObject(cx, &consumerGlobal))
|
||||
return nullptr;
|
||||
expandoObject = attachExpandoObject(cx, target, ObjectPrincipal(wrapper),
|
||||
isSandbox ? (HandleObject)consumerGlobal : nullptr);
|
||||
JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(wrapper);
|
||||
bool isExclusive = GlobalHasExclusiveExpandos(consumerGlobal);
|
||||
expandoObject = attachExpandoObject(cx, target, isExclusive ? wrapper : nullptr,
|
||||
ObjectPrincipal(wrapper));
|
||||
}
|
||||
return expandoObject;
|
||||
}
|
||||
|
@ -1264,13 +1322,21 @@ XrayTraits::cloneExpandoChain(JSContext* cx, HandleObject dst, HandleObject src)
|
|||
#endif
|
||||
|
||||
while (oldHead) {
|
||||
RootedObject exclusive(cx, JS_GetReservedSlot(oldHead,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
||||
.toObjectOrNull());
|
||||
if (!JS_WrapObject(cx, &exclusive))
|
||||
return false;
|
||||
RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead),
|
||||
exclusive));
|
||||
RootedObject exclusiveWrapper(cx);
|
||||
RootedObject wrapperHolder(cx, JS_GetReservedSlot(oldHead,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER)
|
||||
.toObjectOrNull());
|
||||
if (wrapperHolder) {
|
||||
// The global containing this wrapper holder has an xray for |src|
|
||||
// with expandos. Create an xray in the global for |dst| which
|
||||
// will be associated with a clone of |src|'s expando object.
|
||||
JSAutoCompartment ac(cx, UncheckedUnwrap(wrapperHolder));
|
||||
exclusiveWrapper = dst;
|
||||
if (!JS_WrapObject(cx, &exclusiveWrapper))
|
||||
return false;
|
||||
}
|
||||
RootedObject newHead(cx, attachExpandoObject(cx, dst, exclusiveWrapper,
|
||||
GetExpandoObjectPrincipal(oldHead)));
|
||||
if (!JS_CopyPropertiesFrom(cx, newHead, oldHead))
|
||||
return false;
|
||||
oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
||||
|
@ -1286,6 +1352,8 @@ ClearXrayExpandoSlots(JSObject* target, size_t slotIndex)
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(slotIndex != JSSLOT_EXPANDO_NEXT);
|
||||
MOZ_ASSERT(slotIndex != JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER);
|
||||
MOZ_ASSERT(GetXrayTraits(target) == &DOMXrayTraits::singleton);
|
||||
RootingContext* rootingCx = RootingCx();
|
||||
RootedObject rootedTarget(rootingCx, target);
|
||||
|
@ -1324,17 +1392,19 @@ bool CloneExpandoChain(JSContext* cx, JSObject* dstArg, JSObject* srcArg)
|
|||
}
|
||||
} // namespace XrayUtils
|
||||
|
||||
static const size_t JSSLOT_XRAY_HOLDER = 0;
|
||||
|
||||
static JSObject*
|
||||
GetHolder(JSObject* obj)
|
||||
{
|
||||
return &js::GetProxyReservedSlot(obj, 0).toObject();
|
||||
return &js::GetProxyReservedSlot(obj, JSSLOT_XRAY_HOLDER).toObject();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
/* static */ JSObject*
|
||||
XrayTraits::getHolder(JSObject* wrapper)
|
||||
{
|
||||
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
|
||||
js::Value v = js::GetProxyReservedSlot(wrapper, 0);
|
||||
js::Value v = js::GetProxyReservedSlot(wrapper, JSSLOT_XRAY_HOLDER);
|
||||
return v.isObject() ? &v.toObject() : nullptr;
|
||||
}
|
||||
|
||||
|
@ -1346,10 +1416,28 @@ XrayTraits::ensureHolder(JSContext* cx, HandleObject wrapper)
|
|||
return holder;
|
||||
holder = createHolder(cx, wrapper); // virtual trap.
|
||||
if (holder)
|
||||
js::SetProxyReservedSlot(wrapper, 0, ObjectValue(*holder));
|
||||
js::SetProxyReservedSlot(wrapper, JSSLOT_XRAY_HOLDER, ObjectValue(*holder));
|
||||
return holder;
|
||||
}
|
||||
|
||||
static inline JSObject*
|
||||
GetCachedXrayExpando(JSObject* wrapper)
|
||||
{
|
||||
JSObject* holder = XrayTraits::getHolder(wrapper);
|
||||
if (!holder)
|
||||
return nullptr;
|
||||
Value v = JS_GetReservedSlot(holder, XrayTraits::HOLDER_SLOT_EXPANDO);
|
||||
return v.isObject() ? &v.toObject() : nullptr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetCachedXrayExpando(JSObject* holder, JSObject* expandoWrapper)
|
||||
{
|
||||
MOZ_ASSERT(js::GetObjectCompartment(holder) ==
|
||||
js::GetObjectCompartment(expandoWrapper));
|
||||
JS_SetReservedSlot(holder, XrayTraits::HOLDER_SLOT_EXPANDO, ObjectValue(*expandoWrapper));
|
||||
}
|
||||
|
||||
namespace XrayUtils {
|
||||
|
||||
bool
|
||||
|
@ -2546,4 +2634,22 @@ template class SecurityXrayDOM;
|
|||
template class PermissiveXrayJS;
|
||||
template class PermissiveXrayOpaque;
|
||||
|
||||
/*
|
||||
* This callback is used by the JS engine to test if a proxy handler is for a
|
||||
* cross compartment xray with no security requirements.
|
||||
*/
|
||||
static bool
|
||||
IsCrossCompartmentXrayCallback(const js::BaseProxyHandler* handler)
|
||||
{
|
||||
return handler == &PermissiveXrayDOM::singleton;
|
||||
}
|
||||
|
||||
js::XrayJitInfo gXrayJitInfo = {
|
||||
IsCrossCompartmentXrayCallback,
|
||||
GlobalHasExclusiveExpandos,
|
||||
JSSLOT_XRAY_HOLDER,
|
||||
XrayTraits::HOLDER_SLOT_EXPANDO,
|
||||
JSSLOT_EXPANDO_PROTOTYPE
|
||||
};
|
||||
|
||||
} // namespace xpc
|
||||
|
|
|
@ -105,10 +105,11 @@ public:
|
|||
// Slots for holder objects.
|
||||
enum {
|
||||
HOLDER_SLOT_CACHED_PROTO = 0,
|
||||
HOLDER_SLOT_EXPANDO = 1,
|
||||
HOLDER_SHARED_SLOT_COUNT
|
||||
};
|
||||
|
||||
JSObject* getHolder(JSObject* wrapper);
|
||||
static JSObject* getHolder(JSObject* wrapper);
|
||||
JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
|
||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
|
||||
|
||||
|
@ -125,14 +126,21 @@ protected:
|
|||
|
||||
private:
|
||||
bool expandoObjectMatchesConsumer(JSContext* cx, JS::HandleObject expandoObject,
|
||||
nsIPrincipal* consumerOrigin,
|
||||
JS::HandleObject exclusiveGlobal);
|
||||
nsIPrincipal* consumerOrigin);
|
||||
|
||||
// |expandoChain| is the expando chain in the wrapped object's compartment.
|
||||
// |exclusiveWrapper| is any xray that has exclusive use of the expando.
|
||||
// |cx| may be in any compartment.
|
||||
bool getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
|
||||
nsIPrincipal* origin, JSObject* exclusiveGlobal,
|
||||
JS::MutableHandleObject expandoObject);
|
||||
JSObject* attachExpandoObject(JSContext* cx, JS::HandleObject target,
|
||||
JS::HandleObject exclusiveWrapper,
|
||||
nsIPrincipal* origin,
|
||||
JS::HandleObject exclusiveGlobal);
|
||||
JS::MutableHandleObject expandoObject);
|
||||
|
||||
// |cx| is in the target's compartment, and |exclusiveWrapper| is any xray
|
||||
// that has exclusive use of the expando.
|
||||
JSObject* attachExpandoObject(JSContext* cx, JS::HandleObject target,
|
||||
JS::HandleObject exclusiveWrapper,
|
||||
nsIPrincipal* origin);
|
||||
|
||||
XrayTraits(XrayTraits&) = delete;
|
||||
const XrayTraits& operator=(XrayTraits&) = delete;
|
||||
|
@ -601,7 +609,7 @@ class AutoSetWrapperNotShadowing;
|
|||
enum ExpandoSlots {
|
||||
JSSLOT_EXPANDO_NEXT = 0,
|
||||
JSSLOT_EXPANDO_ORIGIN,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER,
|
||||
JSSLOT_EXPANDO_PROTOTYPE,
|
||||
JSSLOT_EXPANDO_COUNT
|
||||
};
|
||||
|
@ -624,6 +632,9 @@ ClearXrayExpandoSlots(JSObject* target, size_t slotIndex);
|
|||
JSObject*
|
||||
EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper);
|
||||
|
||||
// Information about xrays for use by the JITs.
|
||||
extern js::XrayJitInfo gXrayJitInfo;
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче