Bug 1467124 - Fix WindowProxy optimizations in the JIT for same-compartment realms. r=bzbarsky

Makes the following changes:

* The WindowProxy optimizations in the ICs and Ion now guard the WindowProxy's
  global is the script's global. Other WindowProxies are harder to optimize
  because of potential security checks based on document.domain.

* IsWindowProxyForScriptGlobal was added as helper function to consolidate the
  logic for this.

* Removes the WindowProxy optimization for CCWs. This becomes more complicated
  in the new world for various reasons and it seems better to focus on
  getting same-compartment realms working to address that use case.

Differential Revision: https://phabricator.services.mozilla.com/D15492

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan de Mooij 2019-01-03 08:52:43 +00:00
Родитель 0bda802a93
Коммит c310b8a7a7
3 изменённых файлов: 54 добавлений и 49 удалений

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

@ -18,6 +18,7 @@
#include "vm/EnvironmentObject-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/TypeInference-inl.h"
#include "vm/UnboxedObject-inl.h"
@ -1111,12 +1112,49 @@ bool GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId,
MOZ_CRASH("Bad NativeGetPropCacheability");
}
bool js::jit::IsWindowProxyForScriptGlobal(JSScript* script, JSObject* obj) {
if (!IsWindowProxy(obj)) {
return false;
}
MOZ_ASSERT(obj->getClass() ==
script->runtimeFromMainThread()->maybeWindowProxyClass());
JSObject* window = ToWindowIfWindowProxy(obj);
// Ion relies on the WindowProxy's group changing (and the group getting
// marked as having unknown properties) on navigation. If we ever stop
// transplanting same-compartment WindowProxies, this assert will fail and we
// need to fix that code.
MOZ_ASSERT(window == &obj->nonCCWGlobal());
// This must be a WindowProxy for a global in this compartment. Else it would
// be a cross-compartment wrapper and IsWindowProxy returns false for
// those.
MOZ_ASSERT(script->compartment() == obj->compartment());
// Only optimize lookups on the WindowProxy for the current global. Other
// WindowProxies in the compartment may require security checks (based on
// mutable document.domain). See bug 1516775.
return window == &script->global();
}
// Guards objId is a WindowProxy for windowObj. Returns the window's operand id.
static ObjOperandId GuardAndLoadWindowProxyWindow(CacheIRWriter& writer,
ObjOperandId objId,
GlobalObject* windowObj) {
writer.guardClass(objId, GuardClassKind::WindowProxy);
ObjOperandId windowObjId = writer.loadWrapperTarget(objId);
writer.guardSpecificObject(windowObjId, windowObj);
return windowObjId;
}
bool GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
ObjOperandId objId, HandleId id) {
// Attach a stub when the receiver is a WindowProxy and we can do the lookup
// on the Window (the global object).
if (!IsWindowProxy(obj)) {
if (!IsWindowProxyForScriptGlobal(script_, obj)) {
return false;
}
@ -1126,14 +1164,8 @@ bool GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
return false;
}
// This must be a WindowProxy for the current Window/global. Else it would
// be a cross-compartment wrapper and IsWindowProxy returns false for
// those.
MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
// Now try to do the lookup on the Window (the current global).
HandleObject windowObj = cx_->global();
Handle<GlobalObject*> windowObj = cx_->global();
RootedShape shape(cx_);
RootedNativeObject holder(cx_);
NativeGetPropCacheability type =
@ -1145,9 +1177,8 @@ bool GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
case CanAttachReadSlot: {
maybeEmitIdGuard(id);
writer.guardClass(objId, GuardClassKind::WindowProxy);
ObjOperandId windowObjId = writer.loadObject(windowObj);
ObjOperandId windowObjId =
GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
EmitReadSlotResult(writer, windowObj, holder, shape, windowObjId);
EmitReadSlotReturn(writer, windowObj, holder, shape);
@ -1177,8 +1208,8 @@ bool GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
// Guard the incoming object is a WindowProxy and inline a getter call
// based on the Window object.
maybeEmitIdGuard(id);
writer.guardClass(objId, GuardClassKind::WindowProxy);
ObjOperandId windowObjId = writer.loadObject(windowObj);
ObjOperandId windowObjId =
GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId,
mode_);
@ -1225,7 +1256,6 @@ bool GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj,
return false;
}
bool isWindowProxy = false;
RootedShape shape(cx_);
RootedNativeObject holder(cx_);
@ -1234,16 +1264,6 @@ bool GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj,
{
AutoRealm ar(cx_, unwrapped);
// The first CCW for iframes is almost always wrapping another WindowProxy
// so we optimize for that case as well.
isWindowProxy = IsWindowProxy(unwrapped);
if (isWindowProxy) {
MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) ==
&unwrapped->nonCCWGlobal());
unwrapped = cx_->global();
MOZ_ASSERT(unwrapped);
}
NativeGetPropCacheability canCache =
CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_,
resultFlags_, isTemporarilyUnoptimizable_);
@ -1285,14 +1305,6 @@ bool GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj,
unwrapped->compartment());
ObjOperandId unwrappedId = wrapperTargetId;
if (isWindowProxy) {
// For the WindowProxy case also unwrap the inner window.
// We avoid loadObject, because storing cross compartment objects in
// stubs / JIT code is tricky.
writer.guardClass(wrapperTargetId, GuardClassKind::WindowProxy);
unwrappedId = writer.loadWrapperTarget(wrapperTargetId);
}
EmitReadSlotResult<SlotReadType::CrossCompartment>(writer, unwrapped, holder,
shape, unwrappedId);
EmitReadSlotReturn(writer, unwrapped, holder, shape, /* wrapResult = */ true);
@ -4426,7 +4438,7 @@ bool SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
// Attach a stub when the receiver is a WindowProxy and we can do the set
// on the Window (the global object).
if (!IsWindowProxy(obj)) {
if (!IsWindowProxyForScriptGlobal(script_, obj)) {
return false;
}
@ -4436,12 +4448,6 @@ bool SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
return false;
}
// This must be a WindowProxy for the current Window/global. Else it would
// be a cross-compartment wrapper and IsWindowProxy returns false for
// those.
MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
// Now try to do the set on the Window (the current global).
Handle<GlobalObject*> windowObj = cx_->global();
@ -4453,9 +4459,8 @@ bool SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
maybeEmitIdGuard(id);
writer.guardClass(objId, GuardClassKind::WindowProxy);
ObjOperandId windowObjId = writer.loadObject(windowObj);
ObjOperandId windowObjId =
GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
writer.guardShape(windowObjId, windowObj->lastProperty());
writer.guardGroupForTypeBarrier(windowObjId, windowObj->group());
typeCheckInfo_.set(windowObj->group(), id);

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

@ -2092,6 +2092,9 @@ inline ReferenceType ReferenceTypeFromSimpleTypeDescrKey(uint32_t key) {
return ReferenceType(key >> 1);
}
// Returns whether obj is a WindowProxy wrapping the script's global.
extern bool IsWindowProxyForScriptGlobal(JSScript* script, JSObject* obj);
} // namespace jit
} // namespace js

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

@ -14,6 +14,7 @@
#include "frontend/SourceNotes.h"
#include "jit/BaselineFrame.h"
#include "jit/BaselineInspector.h"
#include "jit/CacheIR.h"
#include "jit/Ion.h"
#include "jit/IonControlFlow.h"
#include "jit/IonOptimizationLevels.h"
@ -11864,8 +11865,8 @@ MDefinition* IonBuilder::tryInnerizeWindow(MDefinition* obj) {
// Try to optimize accesses on outer window proxies (window.foo, for
// example) to go directly to the inner window, the global.
//
// Callers should be careful not to pass the inner object to getters or
// setters that require outerization.
// Callers should be careful not to pass the global object to getters or
// setters that require the WindowProxy.
if (obj->type() != MIRType::Object) {
return obj;
@ -11881,14 +11882,10 @@ MDefinition* IonBuilder::tryInnerizeWindow(MDefinition* obj) {
return obj;
}
if (!IsWindowProxy(singleton)) {
if (!IsWindowProxyForScriptGlobal(script(), singleton)) {
return obj;
}
// This must be a WindowProxy for the current Window/global. Else it'd be
// a cross-compartment wrapper and IsWindowProxy returns false for those.
MOZ_ASSERT(ToWindowIfWindowProxy(singleton) == &script()->global());
// When we navigate, the WindowProxy is brain transplanted and we'll mark
// its ObjectGroup as having unknown properties. The type constraint we add
// here will invalidate JIT code when this happens.