Bug 1465002 - Make GetScriptedCallerGlobal work with same-compartment realms. r=luke

This commit is contained in:
Jan de Mooij 2018-05-31 07:18:52 +02:00
Родитель f8fc9044ec
Коммит c5f8610e4f
1 изменённых файлов: 44 добавлений и 20 удалений

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

@ -7544,37 +7544,58 @@ DescribeScriptedCaller(JSContext* cx, AutoFilename* filename, unsigned* lineno,
return true; return true;
} }
// Fast path to get the activation to use for GetScriptedCallerGlobal. If this // Fast path to get the activation and realm to use for GetScriptedCallerGlobal.
// returns false, the fast path didn't work out and the caller has to use the // If this returns false, the fast path didn't work out and the caller has to
// (much slower) NonBuiltinFrameIter path. // use the (much slower) NonBuiltinFrameIter path.
// //
// The optimization here is that we skip Ion-inlined frames and only look at // The optimization here is that we skip Ion-inlined frames and only look at
// 'outer' frames. That's fine: each activation is tied to a single compartment, // 'outer' frames. That's fine because Ion doesn't inline cross-realm calls.
// so if an activation contains at least one non-self-hosted frame, we can use // However, GetScriptedCallerGlobal has to skip self-hosted frames and Ion
// the activation's global for GetScriptedCallerGlobal. If, however, all 'outer' // can inline self-hosted scripts, so we have to be careful:
// frames are self-hosted, it's possible Ion inlined a non-self-hosted script, //
// so we must return false and use the slower path. // * When we see a non-self-hosted outer script, it's possible we inlined
// self-hosted scripts into it but that doesn't matter because these scripts
// all have the same realm/global anyway.
//
// * When we see a self-hosted outer script, it's possible we inlined
// non-self-hosted scripts into it, so we have to give up because in this
// case, whether or not to skip the self-hosted frame (to the possibly
// different-realm caller) requires the slow path to handle inlining. Baseline
// and the interpreter don't inline so this only affects Ion.
static bool static bool
GetScriptedCallerActivationFast(JSContext* cx, Activation** activation) GetScriptedCallerActivationRealmFast(JSContext* cx, Activation** activation, Realm** realm)
{ {
ActivationIterator activationIter(cx); ActivationIterator activationIter(cx);
if (activationIter.done()) { if (activationIter.done()) {
*activation = nullptr; *activation = nullptr;
*realm = nullptr;
return true; return true;
} }
*activation = activationIter.activation();
if (activationIter->isJit()) { if (activationIter->isJit()) {
for (OnlyJSJitFrameIter iter(activationIter); !iter.done(); ++iter) { for (OnlyJSJitFrameIter iter(activationIter); !iter.done(); ++iter) {
if (iter.frame().isScripted() && !iter.frame().script()->selfHosted()) if (!iter.frame().isScripted())
continue;
if (!iter.frame().script()->selfHosted()) {
*activation = activationIter.activation();
*realm = iter.frame().script()->realm();
return true; return true;
}
if (iter.frame().isIonScripted()) {
// Ion might have inlined non-self-hosted scripts in this
// self-hosted script.
return false;
}
} }
} else if (activationIter->isInterpreter()) { } else if (activationIter->isInterpreter()) {
for (InterpreterFrameIterator iter((*activation)->asInterpreter()); !iter.done(); ++iter) { InterpreterActivation* act = activationIter->asInterpreter();
if (!iter.frame()->script()->selfHosted()) for (InterpreterFrameIterator iter(act); !iter.done(); ++iter) {
if (!iter.frame()->script()->selfHosted()) {
*activation = act;
*realm = iter.frame()->script()->realm();
return true; return true;
}
} }
} }
@ -7585,8 +7606,8 @@ JS_PUBLIC_API(JSObject*)
GetScriptedCallerGlobal(JSContext* cx) GetScriptedCallerGlobal(JSContext* cx)
{ {
Activation* activation; Activation* activation;
Realm* realm;
if (GetScriptedCallerActivationFast(cx, &activation)) { if (GetScriptedCallerActivationRealmFast(cx, &activation, &realm)) {
if (!activation) if (!activation)
return nullptr; return nullptr;
} else { } else {
@ -7594,18 +7615,21 @@ GetScriptedCallerGlobal(JSContext* cx)
if (i.done()) if (i.done())
return nullptr; return nullptr;
activation = i.activation(); activation = i.activation();
realm = i.realm();
} }
MOZ_ASSERT(realm->compartment() == activation->compartment());
// If the caller is hidden, the embedding wants us to return null here so // If the caller is hidden, the embedding wants us to return null here so
// that it can check its own stack (see HideScriptedCaller). // that it can check its own stack (see HideScriptedCaller).
if (activation->scriptedCallerIsHidden()) if (activation->scriptedCallerIsHidden())
return nullptr; return nullptr;
GlobalObject* global = JS::GetRealmForCompartment(activation->compartment())->maybeGlobal(); GlobalObject* global = realm->maybeGlobal();
// No one should be running code in the atoms realm or running code in // No one should be running code in the atoms realm or running code in a
// a compartment without any live objects, so there should definitely be a // realm without any live objects, so there should definitely be a live
// live global. // global.
MOZ_ASSERT(global); MOZ_ASSERT(global);
return global; return global;