зеркало из 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;
|
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
|
bool
|
||||||
BaselineCacheIRCompiler::emitGuardSpecificObject()
|
BaselineCacheIRCompiler::emitGuardSpecificObject()
|
||||||
{
|
{
|
||||||
|
@ -367,6 +403,54 @@ BaselineCacheIRCompiler::emitGuardSpecificSymbol()
|
||||||
return true;
|
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
|
bool
|
||||||
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
|
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "vm/SelfHosting.h"
|
#include "vm/SelfHosting.h"
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
|
|
||||||
|
#include "jit/MacroAssembler-inl.h"
|
||||||
#include "vm/EnvironmentObject-inl.h"
|
#include "vm/EnvironmentObject-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
#include "vm/UnboxedObject-inl.h"
|
||||||
|
|
||||||
|
@ -177,6 +178,8 @@ GetPropIRGenerator::tryAttachStub()
|
||||||
return true;
|
return true;
|
||||||
if (tryAttachCrossCompartmentWrapper(obj, objId, id))
|
if (tryAttachCrossCompartmentWrapper(obj, objId, id))
|
||||||
return true;
|
return true;
|
||||||
|
if (tryAttachXrayCrossCompartmentWrapper(obj, objId, id))
|
||||||
|
return true;
|
||||||
if (tryAttachFunction(obj, objId, id))
|
if (tryAttachFunction(obj, objId, id))
|
||||||
return true;
|
return true;
|
||||||
if (tryAttachProxy(obj, objId, id))
|
if (tryAttachProxy(obj, objId, id))
|
||||||
|
@ -752,7 +755,7 @@ GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperan
|
||||||
|
|
||||||
maybeEmitIdGuard(id);
|
maybeEmitIdGuard(id);
|
||||||
writer.guardIsProxy(objId);
|
writer.guardIsProxy(objId);
|
||||||
writer.guardIsCrossCompartmentWrapper(objId);
|
writer.guardHasProxyHandler(objId, Wrapper::wrapperHandler(obj));
|
||||||
|
|
||||||
// Load the object wrapped by the CCW
|
// Load the object wrapped by the CCW
|
||||||
ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
|
ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
|
||||||
|
@ -776,6 +779,114 @@ GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperan
|
||||||
return true;
|
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
|
bool
|
||||||
GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
|
GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||||
bool handleDOMProxies)
|
bool handleDOMProxies)
|
||||||
|
@ -3877,3 +3988,40 @@ CompareIRGenerator::trackNotAttached()
|
||||||
}
|
}
|
||||||
#endif
|
#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) \
|
_(GuardShape) \
|
||||||
_(GuardGroup) \
|
_(GuardGroup) \
|
||||||
_(GuardProto) \
|
_(GuardProto) \
|
||||||
_(GuardClass) \
|
_(GuardClass) /* Guard an object class, per GuardClassKind */ \
|
||||||
|
_(GuardAnyClass) /* Guard an arbitrary class for an object */ \
|
||||||
_(GuardCompartment) \
|
_(GuardCompartment) \
|
||||||
_(GuardIsNativeFunction) \
|
_(GuardIsNativeFunction) \
|
||||||
_(GuardIsProxy) \
|
_(GuardIsProxy) \
|
||||||
_(GuardIsCrossCompartmentWrapper) \
|
_(GuardHasProxyHandler) \
|
||||||
_(GuardNotDOMProxy) \
|
_(GuardNotDOMProxy) \
|
||||||
_(GuardSpecificObject) \
|
_(GuardSpecificObject) \
|
||||||
_(GuardSpecificAtom) \
|
_(GuardSpecificAtom) \
|
||||||
|
@ -188,6 +189,8 @@ extern const char* CacheKindNames[];
|
||||||
_(GuardAndGetIterator) \
|
_(GuardAndGetIterator) \
|
||||||
_(GuardHasGetterSetter) \
|
_(GuardHasGetterSetter) \
|
||||||
_(GuardGroupHasUnanalyzedNewScript) \
|
_(GuardGroupHasUnanalyzedNewScript) \
|
||||||
|
_(GuardIndexIsNonNegative) \
|
||||||
|
_(GuardXrayExpandoShapeAndDefaultProto) \
|
||||||
_(LoadStackValue) \
|
_(LoadStackValue) \
|
||||||
_(LoadObject) \
|
_(LoadObject) \
|
||||||
_(LoadProto) \
|
_(LoadProto) \
|
||||||
|
@ -349,6 +352,14 @@ enum class GuardClassKind : uint8_t
|
||||||
JSFunction,
|
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 to record CacheIR + some additional metadata for code generation.
|
||||||
class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
{
|
{
|
||||||
|
@ -517,6 +528,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
writeOpWithOperandId(CacheOp::GuardShape, obj);
|
writeOpWithOperandId(CacheOp::GuardShape, obj);
|
||||||
addStubField(uintptr_t(shape), StubField::Type::Shape);
|
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) {
|
void guardGroup(ObjOperandId obj, ObjectGroup* group) {
|
||||||
writeOpWithOperandId(CacheOp::GuardGroup, obj);
|
writeOpWithOperandId(CacheOp::GuardGroup, obj);
|
||||||
addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
||||||
|
@ -531,6 +546,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
writeOpWithOperandId(CacheOp::GuardClass, obj);
|
writeOpWithOperandId(CacheOp::GuardClass, obj);
|
||||||
buffer_.writeByte(uint32_t(kind));
|
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) {
|
void guardIsNativeFunction(ObjOperandId obj, JSNative nativeFunc) {
|
||||||
writeOpWithOperandId(CacheOp::GuardIsNativeFunction, obj);
|
writeOpWithOperandId(CacheOp::GuardIsNativeFunction, obj);
|
||||||
writePointer(JS_FUNC_TO_DATA_PTR(void*, nativeFunc));
|
writePointer(JS_FUNC_TO_DATA_PTR(void*, nativeFunc));
|
||||||
|
@ -538,8 +557,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
void guardIsProxy(ObjOperandId obj) {
|
void guardIsProxy(ObjOperandId obj) {
|
||||||
writeOpWithOperandId(CacheOp::GuardIsProxy, obj);
|
writeOpWithOperandId(CacheOp::GuardIsProxy, obj);
|
||||||
}
|
}
|
||||||
void guardIsCrossCompartmentWrapper(ObjOperandId obj) {
|
void guardHasProxyHandler(ObjOperandId obj, const void* handler) {
|
||||||
writeOpWithOperandId(CacheOp::GuardIsCrossCompartmentWrapper, obj);
|
writeOpWithOperandId(CacheOp::GuardHasProxyHandler, obj);
|
||||||
|
addStubField(uintptr_t(handler), StubField::Type::RawWord);
|
||||||
}
|
}
|
||||||
void guardNotDOMProxy(ObjOperandId obj) {
|
void guardNotDOMProxy(ObjOperandId obj) {
|
||||||
writeOpWithOperandId(CacheOp::GuardNotDOMProxy, obj);
|
writeOpWithOperandId(CacheOp::GuardNotDOMProxy, obj);
|
||||||
|
@ -605,6 +625,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void guardIndexIsNonNegative(Int32OperandId index) {
|
||||||
|
writeOpWithOperandId(CacheOp::GuardIndexIsNonNegative, index);
|
||||||
|
}
|
||||||
|
|
||||||
void loadFrameCalleeResult() {
|
void loadFrameCalleeResult() {
|
||||||
writeOp(CacheOp::LoadFrameCalleeResult);
|
writeOp(CacheOp::LoadFrameCalleeResult);
|
||||||
}
|
}
|
||||||
|
@ -1137,6 +1161,7 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator
|
||||||
bool tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id);
|
bool tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id);
|
||||||
bool tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id);
|
bool tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id);
|
||||||
bool tryAttachCrossCompartmentWrapper(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 tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id);
|
||||||
|
|
||||||
bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
|
bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||||
|
|
|
@ -1435,22 +1435,6 @@ CacheIRCompiler::emitGuardIsProxy()
|
||||||
return true;
|
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
|
bool
|
||||||
CacheIRCompiler::emitGuardNotDOMProxy()
|
CacheIRCompiler::emitGuardNotDOMProxy()
|
||||||
{
|
{
|
||||||
|
@ -1927,6 +1911,19 @@ CacheIRCompiler::emitLoadDenseElementResult()
|
||||||
return true;
|
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
|
bool
|
||||||
CacheIRCompiler::emitLoadDenseElementHoleResult()
|
CacheIRCompiler::emitLoadDenseElementHoleResult()
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,6 @@ namespace jit {
|
||||||
_(GuardClass) \
|
_(GuardClass) \
|
||||||
_(GuardIsNativeFunction) \
|
_(GuardIsNativeFunction) \
|
||||||
_(GuardIsProxy) \
|
_(GuardIsProxy) \
|
||||||
_(GuardIsCrossCompartmentWrapper) \
|
|
||||||
_(GuardNotDOMProxy) \
|
_(GuardNotDOMProxy) \
|
||||||
_(GuardSpecificInt32Immediate) \
|
_(GuardSpecificInt32Immediate) \
|
||||||
_(GuardMagicValue) \
|
_(GuardMagicValue) \
|
||||||
|
@ -33,6 +32,7 @@ namespace jit {
|
||||||
_(GuardNoDetachedTypedObjects) \
|
_(GuardNoDetachedTypedObjects) \
|
||||||
_(GuardNoDenseElements) \
|
_(GuardNoDenseElements) \
|
||||||
_(GuardAndGetIndexFromString) \
|
_(GuardAndGetIndexFromString) \
|
||||||
|
_(GuardIndexIsNonNegative) \
|
||||||
_(LoadProto) \
|
_(LoadProto) \
|
||||||
_(LoadEnclosingEnvironment) \
|
_(LoadEnclosingEnvironment) \
|
||||||
_(LoadWrapperTarget) \
|
_(LoadWrapperTarget) \
|
||||||
|
|
|
@ -106,6 +106,12 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler
|
||||||
JSCompartment* compartmentStubField(uint32_t offset) {
|
JSCompartment* compartmentStubField(uint32_t offset) {
|
||||||
return (JSCompartment*)readStubWord(offset, StubField::Type::RawWord);
|
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) {
|
jsid idStubField(uint32_t offset) {
|
||||||
return mozilla::BitwiseCast<jsid>(readStubWord(offset, StubField::Type::Id));
|
return mozilla::BitwiseCast<jsid>(readStubWord(offset, StubField::Type::Id));
|
||||||
}
|
}
|
||||||
|
@ -647,6 +653,37 @@ IonCacheIRCompiler::emitGuardCompartment()
|
||||||
return true;
|
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
|
bool
|
||||||
IonCacheIRCompiler::emitGuardSpecificObject()
|
IonCacheIRCompiler::emitGuardSpecificObject()
|
||||||
{
|
{
|
||||||
|
@ -720,6 +757,55 @@ IonCacheIRCompiler::emitGuardSpecificSymbol()
|
||||||
return true;
|
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
|
bool
|
||||||
IonCacheIRCompiler::emitLoadFixedSlotResult()
|
IonCacheIRCompiler::emitLoadFixedSlotResult()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1305,6 +1305,20 @@ js::GetDOMProxyShadowsCheck()
|
||||||
return gDOMProxyShadowsCheck;
|
return gDOMProxyShadowsCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static XrayJitInfo* gXrayJitInfo = nullptr;
|
||||||
|
|
||||||
|
JS_FRIEND_API(void)
|
||||||
|
js::SetXrayJitInfo(XrayJitInfo* info)
|
||||||
|
{
|
||||||
|
gXrayJitInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrayJitInfo*
|
||||||
|
js::GetXrayJitInfo()
|
||||||
|
{
|
||||||
|
return gXrayJitInfo;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::detail::IdMatchesAtom(jsid id, JSAtom* atom)
|
js::detail::IdMatchesAtom(jsid id, JSAtom* atom)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1319,6 +1319,34 @@ inline bool DOMProxyIsShadowing(DOMProxyShadowsResult result) {
|
||||||
result == ShadowsViaIndirectExpando;
|
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. */
|
/* Implemented in jsdate.cpp. */
|
||||||
|
|
||||||
/** Detect whether the internal date value is NaN. */
|
/** Detect whether the internal date value is NaN. */
|
||||||
|
|
|
@ -2847,6 +2847,7 @@ XPCJSRuntime::Initialize(JSContext* cx)
|
||||||
js::SetPreserveWrapperCallback(cx, PreserveWrapper);
|
js::SetPreserveWrapperCallback(cx, PreserveWrapper);
|
||||||
JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
|
JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
|
||||||
js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
|
js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
|
||||||
|
js::SetXrayJitInfo(&gXrayJitInfo);
|
||||||
JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
|
JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
|
||||||
|
|
||||||
// The JS engine needs to keep the source code around in order to implement
|
// 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_expandosharing.html
|
||||||
!/js/xpconnect/tests/mochitest/file_nodelists.html
|
!/js/xpconnect/tests/mochitest/file_nodelists.html
|
||||||
!/js/xpconnect/tests/mochitest/file_evalInSandbox.html
|
!/js/xpconnect/tests/mochitest/file_evalInSandbox.html
|
||||||
|
!/js/xpconnect/tests/mochitest/file_xrayic.html
|
||||||
|
|
||||||
[test_APIExposer.xul]
|
[test_APIExposer.xul]
|
||||||
[test_bug361111.xul]
|
[test_bug361111.xul]
|
||||||
|
@ -115,5 +116,6 @@ skip-if = os == 'win' || os == 'mac' || (os == 'linux' && !debug) # bug 1131110,
|
||||||
[test_weakref.xul]
|
[test_weakref.xul]
|
||||||
[test_windowProxyDeadWrapper.html]
|
[test_windowProxyDeadWrapper.html]
|
||||||
[test_wrappers.xul]
|
[test_wrappers.xul]
|
||||||
|
[test_xrayic.xul]
|
||||||
[test_xrayToJS.xul]
|
[test_xrayToJS.xul]
|
||||||
[test_asyncIteration.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_matches.html
|
||||||
file_nodelists.html
|
file_nodelists.html
|
||||||
file_wrappers-2.html
|
file_wrappers-2.html
|
||||||
|
file_xrayic.html
|
||||||
inner.html
|
inner.html
|
||||||
test1_bug629331.html
|
test1_bug629331.html
|
||||||
test2_bug629331.html
|
test2_bug629331.html
|
||||||
|
|
|
@ -1054,6 +1054,23 @@ GetXrayTraits(JSObject* obj)
|
||||||
* them. They are private to the origin that placed them.
|
* 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*
|
static nsIPrincipal*
|
||||||
ObjectPrincipal(JSObject* obj)
|
ObjectPrincipal(JSObject* obj)
|
||||||
{
|
{
|
||||||
|
@ -1084,8 +1101,7 @@ const JSClassOps XrayExpandoObjectClassOps = {
|
||||||
bool
|
bool
|
||||||
XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
|
XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
|
||||||
HandleObject expandoObject,
|
HandleObject expandoObject,
|
||||||
nsIPrincipal* consumerOrigin,
|
nsIPrincipal* consumerOrigin)
|
||||||
HandleObject exclusiveGlobal)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx));
|
MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx));
|
||||||
|
|
||||||
|
@ -1101,39 +1117,50 @@ XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
|
||||||
if (!consumerOrigin->Equals(o))
|
if (!consumerOrigin->Equals(o))
|
||||||
return false;
|
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,
|
JSObject* owner = JS_GetReservedSlot(expandoObject,
|
||||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER)
|
||||||
.toObjectOrNull();
|
.toObjectOrNull();
|
||||||
if (!owner && !exclusiveGlobal)
|
return owner == nullptr;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XrayTraits::getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
|
XrayTraits::getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
|
||||||
|
HandleObject exclusiveWrapper,
|
||||||
nsIPrincipal* origin,
|
nsIPrincipal* origin,
|
||||||
JSObject* exclusiveGlobalArg,
|
|
||||||
MutableHandleObject expandoObject)
|
MutableHandleObject expandoObject)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||||
expandoObject.set(nullptr);
|
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
|
// The expando object lives in the compartment of the target, so all our
|
||||||
// work needs to happen there.
|
// work needs to happen there.
|
||||||
RootedObject exclusiveGlobal(cx, exclusiveGlobalArg);
|
|
||||||
RootedObject head(cx, expandoChain);
|
RootedObject head(cx, expandoChain);
|
||||||
JSAutoCompartment ac(cx, head);
|
JSAutoCompartment ac(cx, head);
|
||||||
if (!JS_WrapObject(cx, &exclusiveGlobal))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Iterate through the chain, looking for a same-origin object.
|
// Iterate through the chain, looking for a same-origin object.
|
||||||
while (head) {
|
while (head) {
|
||||||
if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) {
|
if (expandoObjectMatchesConsumer(cx, head, origin)) {
|
||||||
expandoObject.set(head);
|
expandoObject.set(head);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1155,19 +1182,35 @@ XrayTraits::getExpandoObject(JSContext* cx, HandleObject target, HandleObject co
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer);
|
JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer);
|
||||||
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
bool isExclusive = GlobalHasExclusiveExpandos(consumerGlobal);
|
||||||
return getExpandoObjectInternal(cx, chain, ObjectPrincipal(consumer),
|
return getExpandoObjectInternal(cx, chain, isExclusive ? consumer : nullptr,
|
||||||
isSandbox ? consumerGlobal : nullptr,
|
ObjectPrincipal(consumer), expandoObject);
|
||||||
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*
|
JSObject*
|
||||||
XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
||||||
nsIPrincipal* origin, HandleObject exclusiveGlobal)
|
HandleObject exclusiveWrapper,
|
||||||
|
nsIPrincipal* origin)
|
||||||
{
|
{
|
||||||
// Make sure the compartments are sane.
|
// Make sure the compartments are sane.
|
||||||
MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
|
MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
|
||||||
MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
|
MOZ_ASSERT_IF(exclusiveWrapper, !js::IsObjectInContextCompartment(exclusiveWrapper, cx));
|
||||||
|
|
||||||
// No duplicates allowed.
|
// No duplicates allowed.
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -1175,7 +1218,7 @@ XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
||||||
JSObject* chain = getExpandoChain(target);
|
JSObject* chain = getExpandoChain(target);
|
||||||
if (chain) {
|
if (chain) {
|
||||||
RootedObject existingExpandoObject(cx);
|
RootedObject existingExpandoObject(cx);
|
||||||
if (getExpandoObjectInternal(cx, chain, origin, exclusiveGlobal, &existingExpandoObject))
|
if (getExpandoObjectInternal(cx, chain, exclusiveWrapper, origin, &existingExpandoObject))
|
||||||
MOZ_ASSERT(!existingExpandoObject);
|
MOZ_ASSERT(!existingExpandoObject);
|
||||||
else
|
else
|
||||||
JS_ClearPendingException(cx);
|
JS_ClearPendingException(cx);
|
||||||
|
@ -1195,9 +1238,31 @@ XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
||||||
NS_ADDREF(origin);
|
NS_ADDREF(origin);
|
||||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin));
|
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin));
|
||||||
|
|
||||||
// Note the exclusive global, if any.
|
// Note the exclusive wrapper, if there is one.
|
||||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
RootedObject wrapperHolder(cx);
|
||||||
ObjectOrNullValue(exclusiveGlobal));
|
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
|
// 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
|
// 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))
|
if (!getExpandoObject(cx, target, wrapper, &expandoObject))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (!expandoObject) {
|
if (!expandoObject) {
|
||||||
// If the object is a sandbox, we don't want it to share expandos with
|
JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(wrapper);
|
||||||
// anyone else, so we tag it with the sandbox global.
|
bool isExclusive = GlobalHasExclusiveExpandos(consumerGlobal);
|
||||||
//
|
expandoObject = attachExpandoObject(cx, target, isExclusive ? wrapper : nullptr,
|
||||||
// NB: We first need to check the class, _then_ wrap for the target's
|
ObjectPrincipal(wrapper));
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
return expandoObject;
|
return expandoObject;
|
||||||
}
|
}
|
||||||
|
@ -1264,13 +1322,21 @@ XrayTraits::cloneExpandoChain(JSContext* cx, HandleObject dst, HandleObject src)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (oldHead) {
|
while (oldHead) {
|
||||||
RootedObject exclusive(cx, JS_GetReservedSlot(oldHead,
|
RootedObject exclusiveWrapper(cx);
|
||||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
RootedObject wrapperHolder(cx, JS_GetReservedSlot(oldHead,
|
||||||
.toObjectOrNull());
|
JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER)
|
||||||
if (!JS_WrapObject(cx, &exclusive))
|
.toObjectOrNull());
|
||||||
return false;
|
if (wrapperHolder) {
|
||||||
RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead),
|
// The global containing this wrapper holder has an xray for |src|
|
||||||
exclusive));
|
// 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))
|
if (!JS_CopyPropertiesFrom(cx, newHead, oldHead))
|
||||||
return false;
|
return false;
|
||||||
oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
||||||
|
@ -1286,6 +1352,8 @@ ClearXrayExpandoSlots(JSObject* target, size_t slotIndex)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(slotIndex != JSSLOT_EXPANDO_NEXT);
|
||||||
|
MOZ_ASSERT(slotIndex != JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER);
|
||||||
MOZ_ASSERT(GetXrayTraits(target) == &DOMXrayTraits::singleton);
|
MOZ_ASSERT(GetXrayTraits(target) == &DOMXrayTraits::singleton);
|
||||||
RootingContext* rootingCx = RootingCx();
|
RootingContext* rootingCx = RootingCx();
|
||||||
RootedObject rootedTarget(rootingCx, target);
|
RootedObject rootedTarget(rootingCx, target);
|
||||||
|
@ -1324,17 +1392,19 @@ bool CloneExpandoChain(JSContext* cx, JSObject* dstArg, JSObject* srcArg)
|
||||||
}
|
}
|
||||||
} // namespace XrayUtils
|
} // namespace XrayUtils
|
||||||
|
|
||||||
|
static const size_t JSSLOT_XRAY_HOLDER = 0;
|
||||||
|
|
||||||
static JSObject*
|
static JSObject*
|
||||||
GetHolder(JSObject* obj)
|
GetHolder(JSObject* obj)
|
||||||
{
|
{
|
||||||
return &js::GetProxyReservedSlot(obj, 0).toObject();
|
return &js::GetProxyReservedSlot(obj, JSSLOT_XRAY_HOLDER).toObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
/* static */ JSObject*
|
||||||
XrayTraits::getHolder(JSObject* wrapper)
|
XrayTraits::getHolder(JSObject* wrapper)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(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;
|
return v.isObject() ? &v.toObject() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,10 +1416,28 @@ XrayTraits::ensureHolder(JSContext* cx, HandleObject wrapper)
|
||||||
return holder;
|
return holder;
|
||||||
holder = createHolder(cx, wrapper); // virtual trap.
|
holder = createHolder(cx, wrapper); // virtual trap.
|
||||||
if (holder)
|
if (holder)
|
||||||
js::SetProxyReservedSlot(wrapper, 0, ObjectValue(*holder));
|
js::SetProxyReservedSlot(wrapper, JSSLOT_XRAY_HOLDER, ObjectValue(*holder));
|
||||||
return 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 {
|
namespace XrayUtils {
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -2546,4 +2634,22 @@ template class SecurityXrayDOM;
|
||||||
template class PermissiveXrayJS;
|
template class PermissiveXrayJS;
|
||||||
template class PermissiveXrayOpaque;
|
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
|
} // namespace xpc
|
||||||
|
|
|
@ -105,10 +105,11 @@ public:
|
||||||
// Slots for holder objects.
|
// Slots for holder objects.
|
||||||
enum {
|
enum {
|
||||||
HOLDER_SLOT_CACHED_PROTO = 0,
|
HOLDER_SLOT_CACHED_PROTO = 0,
|
||||||
|
HOLDER_SLOT_EXPANDO = 1,
|
||||||
HOLDER_SHARED_SLOT_COUNT
|
HOLDER_SHARED_SLOT_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
JSObject* getHolder(JSObject* wrapper);
|
static JSObject* getHolder(JSObject* wrapper);
|
||||||
JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
|
JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
|
||||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
|
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
|
||||||
|
|
||||||
|
@ -125,14 +126,21 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool expandoObjectMatchesConsumer(JSContext* cx, JS::HandleObject expandoObject,
|
bool expandoObjectMatchesConsumer(JSContext* cx, JS::HandleObject expandoObject,
|
||||||
nsIPrincipal* consumerOrigin,
|
nsIPrincipal* consumerOrigin);
|
||||||
JS::HandleObject exclusiveGlobal);
|
|
||||||
|
// |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,
|
bool getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
|
||||||
nsIPrincipal* origin, JSObject* exclusiveGlobal,
|
JS::HandleObject exclusiveWrapper,
|
||||||
JS::MutableHandleObject expandoObject);
|
|
||||||
JSObject* attachExpandoObject(JSContext* cx, JS::HandleObject target,
|
|
||||||
nsIPrincipal* origin,
|
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;
|
XrayTraits(XrayTraits&) = delete;
|
||||||
const XrayTraits& operator=(XrayTraits&) = delete;
|
const XrayTraits& operator=(XrayTraits&) = delete;
|
||||||
|
@ -601,7 +609,7 @@ class AutoSetWrapperNotShadowing;
|
||||||
enum ExpandoSlots {
|
enum ExpandoSlots {
|
||||||
JSSLOT_EXPANDO_NEXT = 0,
|
JSSLOT_EXPANDO_NEXT = 0,
|
||||||
JSSLOT_EXPANDO_ORIGIN,
|
JSSLOT_EXPANDO_ORIGIN,
|
||||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER,
|
||||||
JSSLOT_EXPANDO_PROTOTYPE,
|
JSSLOT_EXPANDO_PROTOTYPE,
|
||||||
JSSLOT_EXPANDO_COUNT
|
JSSLOT_EXPANDO_COUNT
|
||||||
};
|
};
|
||||||
|
@ -624,6 +632,9 @@ ClearXrayExpandoSlots(JSObject* target, size_t slotIndex);
|
||||||
JSObject*
|
JSObject*
|
||||||
EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper);
|
EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper);
|
||||||
|
|
||||||
|
// Information about xrays for use by the JITs.
|
||||||
|
extern js::XrayJitInfo gXrayJitInfo;
|
||||||
|
|
||||||
} // namespace xpc
|
} // namespace xpc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче