Closed Bug 1841964 Opened 1 year ago Closed 11 months ago

Revisit the behavior of lexical declarations in Debugger.Object.prototype.executeInGlobalWithBindings

Categories

(Core :: JavaScript Engine, task, P2)

task

Tracking

()

RESOLVED DUPLICATE of bug 1842701

People

(Reporter: arai, Unassigned)

References

(Blocks 2 open bugs)

Details

As described in bug 1385198 comment #16, top-level let and const behaves in unexpected way in Debugger.Object.prototype.executeInGlobalWithBindings if the passed bindings object has the same binding.

This comes from the corner case around with environment + lexical declaration without a block, which is disallowed in ECMAScript itself.

Possible options is to add implicit block scope, or enforce the consumer to add explicit block scope.

(In reply to Tooru Fujisawa [:arai] from comment #0)

Possible options is to add implicit block scope, or enforce the consumer to add explicit block scope.

Actually, adding a block won't work as expected if the consumer really wants to declare a global lexical variable by calling executeInGlobalWithBindings.
So, this is very specific to the case where the binding conflicts between the top-level lexical declaration and the passed bindings object.

If there's no conflict, the behavior would be intuitive for both var and let:

var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);

var bindings = {};

// This declares a global variable x with 1,
// and gets the global variable and prints 1.
console.log(gw.executeInGlobalWithBindings(`var x = 1; x;`, bindings).return);

// This still prints 1 given there's global variable x = 1.
g.evaluate(`console.log(x);`);
var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);

var bindings = {};

// This declares a global lexical variable x with 1,
// and gets the global lexical variable and prints 1.
console.log(gw.executeInGlobalWithBindings(`let x = 1; x;`, bindings).return);

// This still prints 1 given there's global lexical variable x = 1.
g.evaluate(`console.log(x);`);

If there's a conflict, the behavior is somewhat strange even with var.

var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);

var bindings = { x: 10 };

// This modifies x in the with env,
// and gets x from the with env and prints 1
console.log(gw.executeInGlobalWithBindings(`var x = 1; x;`, bindings).return);

// This prints undefined, given there's no global variable x.
// This doesn't match with the no-conflict case.
g.evaluate(`console.log(x);`);

And it behaves more strangely with let.

var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);

var bindings = { x: 10 };

// This declares a global lexical variable x with 1,
// and gets x from the with env and prints 10.
// This doesn't match with the no-conflict case.
console.log(gw.executeInGlobalWithBindings(`let x = 1; x;`, bindings).return);

// This prints 1 given there's global lexical variable x = 1.
// This matches to the no-conflict case, but doesn't match with the
// var with conflicting binding case.
g.evaluate(`console.log(x);`);

So, the behavior completely disagree between var and let if there's confllicting binding.

possible intuitive behavior would be to use the passed bindings object as the enclosing environment of the global, so that the passed binding is used as read-only, and always shadowed by any global/local variables:

WithEnvironmentObject(bindings) <- GlobalObject <- global LexicalEnvironmentObject

but it has some problems:

  • it simply require many change to how environment and lookup work
  • other non-syntactic case may expect the var being put into with env, and in that case we'll need to maintain both behavior
Depends on: 1842441

there's difference between an assignment within a declaration and bare assignment.

// bindings = { x: 10, y: 20 };

let x = 11;     // written to GlobalLexicalEnvironmentObject
console.log(x); // 10
y = 21;         // written to WithEnvironmentObject
console.log(y); // 21

so, other possible solution would be to tweak the assignment handling inside declaration.

The performance cliff with the web console (bug 793345) is also related to executeInGlobalWithBindings, so while you're looking at this it might make sense to see if there are other ways to implement these bindings that would fix that issue too.

Depends on: 1842701
Status: NEW → RESOLVED
Closed: 11 months ago
Duplicate of bug: 1842701
Resolution: --- → DUPLICATE
You need to log in before you can comment on or make changes to this bug.