Bug 1355109 - Add IC for property reads on xrays, r=jandem,bz.

--HG--
extra : rebase_source : bc62ebf7f9c6fc006e95ede93087370ad7120303
This commit is contained in:
Brian Hackett 2017-07-21 07:49:53 -07:00
Родитель 2ee7157f0e
Коммит a2b14f72a0
15 изменённых файлов: 672 добавлений и 78 удалений

Просмотреть файл

@ -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