"values" defined in web console is interfering with lexical declaration
Categories
(DevTools :: Console, defect, P2)
Tracking
(firefox56 wontfix, firefox57 fix-optional, firefox117 affected)
Tracking | Status | |
---|---|---|
firefox56 | --- | wontfix |
firefox57 | --- | fix-optional |
firefox117 | --- | affected |
People
(Reporter: w.doeringer, Assigned: nchevobbe)
References
Details
Attachments
(1 file, 1 obsolete file)
Comment 1•8 years ago
|
||
Comment 2•8 years ago
|
||
Reporter | ||
Comment 3•8 years ago
|
||
Assignee | ||
Comment 4•8 years ago
|
||
Comment 5•7 years ago
|
||
Updated•7 years ago
|
Updated•7 years ago
|
Updated•2 years ago
|
Assignee | ||
Comment 8•2 years ago
|
||
Updated•2 years ago
|
Updated•2 years ago
|
Comment 11•2 years ago
|
||
Comment on attachment 9342208 [details]
Bug 1385198 - [devtools] Don't add webconsole helper bindings if variable exists. r=#devtools-reviewers.
Revision D182717 was moved to bug 1517907. Setting attachment 9342208 [details] to obsolete.
Assignee | ||
Comment 12•2 years ago
|
||
Assignee | ||
Comment 13•2 years ago
|
||
I added a test in D182773 that demonstrates the current issue.
arai, do you know what could be done for avoiding this ?
Updated•2 years ago
|
Comment 14•2 years ago
|
||
Comment 15•2 years ago
|
||
bugherder |
Assignee | ||
Updated•2 years ago
|
Updated•2 years ago
|
Comment 16•2 years ago
|
||
executeInGlobalWithBindings
evaluates the given code in the following environment chain:
global <- global lexical <- with (bindings)
So, it's something like the following, but without the block:
var bindings = { x: "x from bindings" };
with (bindings) {
var x = 1; x;
}
Inside with
, var x = 1
declaration puts the value into the bindings
object,
and x
gets the value from inner-most environment, which is with (bindings)
,
so it returns 1
.
Then, the behavior is different with let
and const
, due to the corner case around with
+ lexical declaration.
In JS syntax, a lexical declaration is not allowed as with
body, and it requires a block:
with ({}) let a = 10; // Syntax Error
with ({}) { let a = 10; } // OK
But given executeInGlobalWithBindings
's behavior is equivalent to the following without the block, it behaves in unexpected way:
var bindings = { y: "y from bindings" };
with (bindings) {
let y = 2; y;
}
If there's no enclosing block, let
and const
have effect on the global lexical environment.
So, let y = 2
adds a global lexical variable,
and y
still gets the value from inner-most environment, which is with (bindings)
.
That results in returning "y from bindings"
.
So, I think we need to define a better/clearer behavior for the top-level let
and const
inside executeInGlobalWithBindings
.
Assignee | ||
Comment 17•2 years ago
|
||
Thanks for the detailed comment arai 👍
Comment 18•2 years ago
|
||
Bug 1842701 has a WIP patch that changes the bindings behavior to always get shadowed by global vars and lexicals.
I want to make sure the assumption below is valid:
- if the conflict happens, users will most likely expect syntactic variables in their code always shadows the extra bindings
The possibly problematic case happens if the variable is accessed before the assignment in the declaration is executed.
Minimal testcase that behaves differently is the following, evaluated in DevTools console:
console.log($); var $ = 10; console.log($);
The behavior difference is the following:
first console.log |
second console.log |
|
---|---|---|
current behavior | the binding function | 10 |
bug 1842701 patch | undefined |
10 |
In the current behavior, $
is written to and read from the with
environemnt for the bindings, and the global variable is untouched.
With bug 1842701 patch, the binding's $
is shadowed by the var $
declaration, and $
is written to and read from the global object, and the bindings is untouched, and the first console.log
reads the default undefined
value.
Another possibly surprising testcase is the following:
console.log($); if (false) { var $ = 10; } console.log($);
first console.log |
second console.log |
|
---|---|---|
current behavior | the binding function | the binding function |
bug 1842701 patch | undefined |
undefined |
Given var
is hoisted to the top-level, even if the declaration is unreachable, the global variable exists and it shadows the bindings, and $
is read from the global object in the patches case.
These behavior is specific to var
(and function
for the 2nd case).
Lexical variables (let
, const
, class`) are unaffected because they don't allow accessing before initialization, and a block creates a new lexical scope.
:nchevobbe, can I have your input here about the above behavior?
do you think the behavior is acceptable? also, is there any other example that needs attention if bindings gets shadowed?
Assignee | ||
Comment 19•2 years ago
|
||
Thanks for working on this arai.
For both example you provided, your patch has the behavior I would expect.
More globally, as a user, I'd expect the evaluation to happen as if there wasn't any binding added by the console itself
One example that could be confusing with the current behavior is :
console.log($); { let $ = 10 }; console.log($)
both logs display binding function, which you might wonder where they come from.
With a "corrected" behavior, I would expect this to throw with a ReferenceError: $ is not defined
But I can see how this could be difficult, so it's not mandatory that we take care of this one.
Comment 20•2 years ago
|
||
Thank you :)
In the WIP patch, I treated the extra bindings like special global variables which is available only during executing the given code.
So, in the let
example, given it's block-scoped local variable, it doesn't affect the global variable, and it will keep the current behavior that shows the bindings' value.
I'll look into hiding the binding when there's any conflicting global or local variable and see if it's light-weight enough that doesn't slow down the compilation.
Updated•2 years ago
|
Description
•