Bug 1397049 - Fix debugger 'this' in functions with let. r=jorendorff

When the debugger evaluates code in the debuggee frame, the parser may
fail to detect we are in a function context and will compute 'this'
incorrectly as a result. This patch fixes the environment chain
traversal around DebugEnvironmentProxy to more accurately determine the
binding type of 'this' within a function.

MozReview-Commit-ID: GzRDOJLK8fx
This commit is contained in:
Ted Campbell 2017-11-16 00:39:36 -05:00
Родитель f12ca11346
Коммит 20193a2f37
3 изменённых файлов: 57 добавлений и 11 удалений

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

@ -341,22 +341,25 @@ EvalSharedContext::EvalSharedContext(JSContext* cx, JSObject* enclosingEnv,
computeInWith(enclosingScope);
computeThisBinding(enclosingScope);
// Like all things Debugger, Debugger.Frame.eval needs special
// handling. Since the environment chain of such evals are non-syntactic
// (DebuggerEnvironmentProxy is not an EnvironmentObject), computing the
// this binding with respect to enclosingScope is incorrect if the
// Debugger.Frame is a function frame. Recompute the this binding if we
// are such an eval.
// If this eval is in response to Debugger.Frame.eval, we may have been
// passed an incomplete scope chain. In order to better determine the 'this'
// binding type, we traverse the environment chain, looking for a CallObject
// and recompute the binding type based on its body scope.
//
// NOTE: A non-debug eval in a non-syntactic environment will also trigger
// this code. In that case, we should still compute the same binding type.
if (enclosingEnv && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) {
// For Debugger.Frame.eval with bindings, the environment chain may
// have more than the DebugEnvironmentProxy.
JSObject* env = enclosingEnv;
while (env) {
// Look at target of any DebugEnvironmentProxy, but be sure to use
// enclosingEnvironment() of the proxy itself.
JSObject* unwrapped = env;
if (env->is<DebugEnvironmentProxy>())
env = &env->as<DebugEnvironmentProxy>().environment();
unwrapped = &env->as<DebugEnvironmentProxy>().environment();
if (env->is<CallObject>()) {
computeThisBinding(env->as<CallObject>().callee().nonLazyScript()->bodyScope());
if (unwrapped->is<CallObject>()) {
JSFunction* callee = &unwrapped->as<CallObject>().callee();
computeThisBinding(callee->nonLazyScript()->bodyScope());
break;
}

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

@ -0,0 +1,40 @@
// Run debugger in its own global
let g = newGlobal();
g.target = this;
g.evaluate(`
let d = new Debugger;
let gw = d.addDebuggee(target);
d.onDebuggerStatement = function(frame)
{
frame = frame.older;
let res = frame.eval("this");
assertEq(res.return, frame.this);
res = frame.evalWithBindings("this", {x:42});
assertEq(res.return, frame.this);
}
`);
// Debugger statement affects parse so hide in another function
function brk() { debugger; }
function f1() {
var temp = "string";
brk();
}
function f2() {
let temp = "string";
brk();
}
function f3() {
const temp = "string";
brk();
}
f1.call({});
f2.call({});
f3.call({});

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

@ -908,6 +908,9 @@ class DebugEnvironmentProxy : public ProxyObject
static DebugEnvironmentProxy* create(JSContext* cx, EnvironmentObject& env,
HandleObject enclosing);
// NOTE: The environment may be a debug hollow with invalid
// enclosingEnvironment. Always use the enclosingEnvironment accessor on
// the DebugEnvironmentProxy in order to walk the environment chain.
EnvironmentObject& environment() const;
JSObject& enclosingEnvironment() const;