-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Normative: Eliminate extra environment for eval in parameter initializers #1046
Conversation
A bit hand-wavy because I haven't checked the changes in detail, but I don't think it completely matches JSC (which shouldn't be a requirement). function f0(a = eval("var b"), b) { }
// f0(); // No error
function f1(a = eval("var b = 0"), b) { }
// f1(); // ReferenceError, but no error in JSC
function f2(a = eval("function b(){}"), b) { }
// f2(); // ReferenceError, but SyntaxError in JSC
function f3(a = eval("{ function b(){} }"), b) { }
// f3(); // ReferenceError, but no error in JSC
function f4(b, a = eval("var b = 0")) { print(b) }
// f4(); // Prints "0"
function f5(b, a = eval("function b(){}")) { print(b) }
// f5(); // Prints "function b(){}", but SyntaxError in JSC
function f6(b, a = eval("{ function b(){} }")) { print(b) }
// f6(); // Prints "function b(){}", but "undefined" in JSC |
@anba Thanks for the examples! function f0(a = eval("var b"), b) { }
// f0(); // No error JSC seems to get this wrong in general: it doesn't detect conflicts between var declarations and lexical variables. function f1(a = eval("var b = 0"), b) { }
// f1(); // ReferenceError, but no error in JSC I think the PR actually makes this a SyntaxError during EvalDeclarationInstantiation. That's its intention, anyway; if your reading is different, that's definitely something I want to change. function f2(a = eval("function b(){}"), b) { }
// f2(); // ReferenceError, but SyntaxError in JSC Same as above, I think SyntaxError is correct. function f3(a = eval("{ function b(){} }"), b) { }
// f3(); // ReferenceError, but no error in JSC Why do you think this should be a ReferenceError? I would expect this to result in no error. function f4(b, a = eval("var b = 0")) { print(b) }
// f4(); // Prints "0" Yeah, this is the same JSC bug as f0; I think this should be a SyntaxError. function f5(b, a = eval("function b(){}")) { print(b) }
// f5(); // Prints "function b(){}", but SyntaxError in JSC SyntaxError is what I expect here too. function f6(b, a = eval("{ function b(){} }")) { print(b) }
// f6(); // Prints "function b(){}", but "undefined" in JSC "undefined" seems right to me, too. Are you expecting Annex B to cause the function to get assigned to |
The parameter bindings are installed in the current execution context's VariableEnvironment, so the redeclaration check in EvalDeclarationInstantiation, step 5.d won't report any SyntaxErrors for the variable declaration And because EvalDeclarationInstantiation won't throw any SyntaxErrors, Annex B semantics apply for the other cases with |
@anba Thanks, got it. The quickest fix I can think of would be, in sloppy mode, to introduce a LexicalEnvironment that sits just below the VariableEnvironment of the function, and declare parameters there. Do you think this would cause any additional problems? |
Do you mean the following environment set-up? (I haven't yet checked if this approach is feasible!) Non-strict function with parameter expressions: Description Environment kind Execution context state +----------------------------------+ +----------------+ +-----------------------------------------+ | | | | | | | Var-bindings from eval in params +-----+ FunctionEnv +-----+ VariableEnv during parameter evaluation | | | | | | | +----------------------------------+ +----------------+ +-----------------------------------------+ | | +--------------------------+ +------v---------+ +----------------------------------------+ | | | | | | | Parameter bindings +-----+ DeclarativeEnv +-----+ LexicalEnv during parameter evaluation | | "arguments" binding (?) | | | | | | | +----------------+ +----------------------------------------+ +--------------------------+ | | +--------------------------------+ +------v---------+ +----------------------------------------+ | | | | | | | Var-bindings from body +----+ DeclarativeEnv +-----+ VariableEnv after parameter evaluation | | Var-bindings from eval in body | | | | | | | +----------------+ +----------------------------------------+ +--------------------------------+ | | +--------------------------------+ +------v---------+ +---------------------------------------+ | | | | | | | Lexical bindings from body +----+ DeclarativeEnv +-----+ LexicalEnv after parameter evaluation | | | | | | | +--------------------------------+ +----------------+ +---------------------------------------+ Strict function with parameter expressions (no changes): Description Environment kind Execution context state +---------------------+ +----------------+ +-----------------------------------------+ | | | | | | | Parameter bindings +-----+ FunctionEnv +-----+ VariableEnv during parameter evaluation | | "arguments" binding | | | | LexicalEnv during parameter evaluation | | | +------+---------+ | | +---------------------+ | +-----------------------------------------+ | +----------------------------+ +------v---------+ +----------------------------------------+ | | | | | | | Var-bindings from body +-----+ DeclarativeEnv +-----+ VariableEnv after parameter evaluation | | Lexical bindings from body | | | | LexicalEnv after parameter evaluation | | | +----------------+ | | +----------------------------+ +----------------------------------------+ Non-strict function without parameter expressions (no changes): Description Environment kind Execution context state +----------------------------------+ +----------------+ +----------------------------------------+ | | | | | | | Parameter bindings +-----+ FunctionEnv +-----+ VariableEnv | | "arguments" binding | | | | LexicalEnv during parameter evaluation | | Var-bindings from body | +------+---------+ | | | Var-bindings from eval in body | | +----------------------------------------+ | | | +----------------------------------+ | | +--------------------------------+ +------v---------+ +---------------------------------------+ | | | | | | | Lexical bindings from body +-----+ DeclarativeEnv +-----+ LexicalEnv after parameter evaluation | | | | | | | +--------------------------------+ +----------------+ +---------------------------------------+ Strict function without parameter expressions (no changes): Description Environment kind Execution context state +----------------------------------+ +----------------+ +-----------------------+ | | | | | | | Parameter bindings +-----+ FunctionEnv +-----+ VariableEnv | | "arguments" binding | | | | LexicalEnv | | Var-bindings from body | +----------------+ | | | Lexical bindings from body | +-----------------------+ | | +----------------------------------+ With:
|
Yes, that's what I had in mind. |
@syg That's not my understanding. The eval should have access to the parameters; it's simply that any var bindings created in the eval will be "hoisted" above the parameters (or cause an error in case they conflict with the name of a parameter). |
Ah, right, the direct eval will have its own LexicalEnv which has the parameter environment as the enclosing environment, while its VariablesEnv will be the function environment. That seems okay. |
I've update the PR to match @anba's diagram. Another round of review would be greatly appreciated. I'm also not at all happy with my NOTEs, so any suggestions for how to add more clarity would be good. |
@leobalter @spectranaut @nomadtechie we should review these changes for potential Test262 updating. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, this is a nice simplification. (That is, in addition to implementing the change in question.)
Personally I wouldn't bother with the _strict_
branch, since the difference is unobservable; see the closely related #623. But it doesn't really matter, and could later be changed as part of addressing #623.
If implementation complexity was the rationale for this proposal, why was Option 3 "Make eval in parameters strict" dismissed? In comparison, that option seems the best: it's semantically sane and relatively easy to implement. Admittedly, implicitly switching to strict mode might come as a bit of a surprise, but there are other precedents in the spec where new language features change legacy behavior. IMHO, Option 4 (this PR) is not a real improvement. It reintroduces the undesirable and error-prone behavior that eval var declarations leak into the caller. Of course, the current spec version is similarly flawed but, at least, the variable leaks are more confined. |
@woess It was indeed the concerns you lay out in your second paragraph I was worried about. We'd either have to decide that it was OK to change the strict-ness of the existing eval code, or create a new thing (strict eval with sloppy code inside). I agree that the eval var declaration leakage is not great, but the amount of spec and implementation complexity (and security issues in the wild, in Chrome/V8's case) to "defend" against what I think is a very rare case doesn't seem worthwhile to me. |
@rwaldron did we ever figure out if there were any Test262 concerns? |
Re-pinging this thread for a new year. I continue to believe this is a nice simplification, which if landed and implemented in engines would increase interop while reducing both cognitive and implementation complexity. @LeszekSwirski of V8 might be particularly interested in the "implementation complexity reduction bit" |
@ajklein would you be able to resummarize both a) what current browsers do, and b) what would change as a result of this patch? Thanks :-) |
@ljharb / @ajklein: to jostle my own memory, I wrote up my understanding / recollection of the current and proposed behavior with some tests and their results on current engines. (Edit: the above gist is still accurate as of 2019-11-21.) Basically: the spec currently says that each parameter is evaluated in its own unique var scope which is just inside of the parameter scope. After this change, there would be a var scope just outside of the parameter scope in which all parameters would be evaluated. V8 and SpiderMonkey correctly implement the current spec, Chakra and JSC do their own unique things. |
@erights There would be no observable effects for strict-only code, because strict |
This isn't something I have bandwidth to push on right now, but I still think it's a good idea so would be happy for someone else to take it over. |
I will be taking over this. |
325fb69
to
391182d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rebased this as well.
Tests have been merged |
…zers (tc39#1046) - Remove paramVarEnv from IteratorBindingInitialization of FormalParameters - Create a new DeclarativeEnvironment for the parameters Co-authored-by: Adam Klein <[email protected]> Co-authored-by: Shu-yu Guo <[email protected]>
391182d
to
d68b018
Compare
…nction parameter evaluation Fallout from tc39#1046. When creating a new environment so that sloppy direct eval-introduced vars are in a different environment, the LexicalEnvironment of the execution context was not updated.
This environment was originally added to the spec as the fix for a bug reported by @anba
during the development of ES6. It first appeared in rev33, after a brief discussion
on es-discuss.
Yet as explained in my presentation at the November 2017 meeting,
it has not been uniformly implemented in engines, and its correct implementation in engines
that have attempted it has been a major source of bugs.
This PR implements "Option 4" of the linked slides, which coincides with the implementation
in JavaScriptCore, and thus is believed to be web-compatible.