Skip to content
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: function implementation hiding #1739

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

michaelficarra
Copy link
Member

@michaelficarra michaelficarra commented Oct 17, 2019

This PR implements the currently stage 2 function implementation hiding proposal. I am opening it now to get reviewer feedback and editor sign-off, and to start having a linkable spec text rendering. It is my intention to present this proposal for stage 3 at the June 2020 TC39 meeting.

@michaelficarra michaelficarra added needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 pending stage 4 This proposal has not yet achieved stage 4, but may otherwise be ready to merge. proposal This is related to a specific proposal, and will be closed/merged when the proposal reaches stage 4. labels Oct 17, 2019
@michaelficarra michaelficarra requested a review from a team October 17, 2019 02:18
@michaelficarra
Copy link
Member Author

FYI I would still like feedback on alternative directive names at tc39/proposal-function-implementation-hiding#3. I don't really love the current ones, but haven't heard anything better.

spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@michaelficarra
Copy link
Member Author

Ping @wycats @waldemarhorwat @mikesamuel. You are the stage 2 reviewers for this proposal.

@michaelficarra
Copy link
Member Author

Ping @wycats @waldemarhorwat @mikesamuel once more. Please review before the upcoming meeting. I would like this proposal to advance to stage 3.

@erights
Copy link

erights commented Nov 20, 2019

Hi @michaelficarra , has anything changed since our discussion? IOW, is it essentially the same as what I approved in that discussion?

Can I see a rendered form of this? I do not want to evaluate this by reading ecmarkup source.

@michaelficarra
Copy link
Member Author

@erights The only changes were the ones we talked about in our last call. See the most recently closed issues, particularly tc39/proposal-function-implementation-hiding#32, tc39/proposal-function-implementation-hiding#31, and tc39/proposal-function-implementation-hiding#30.

You can see a rendering of any PRs to ecma262, including this one, by clicking "Details" next to the words "Deploy preview ready!" in the CI checks below.

@erights
Copy link

erights commented Nov 20, 2019

You can see a rendering of any PRs to ecma262, including this one, by clicking "Details" next to the words "Deploy preview ready!" in the CI checks below.

Good to know, thanks!

@waldemarhorwat
Copy link

I'm having trouble reviewing what GetDirectiveContext is doing. It seems to make some unstated assumptions about what happens to be on the execution context stack at the time it is called. I can't tell whether that matches the lexical scope for all of the possible call paths (and note that Scope is passed as a separate parameter and strict mode scoping is done differently). Furthermore, more call paths may be added in the future, so relying on this kind of a hidden side effect seems brittle.

@waldemarhorwat
Copy link

If I understand it correctly, GetDirectiveContext seems broken. Just filed an issue for it.

@michaelficarra
Copy link
Member Author

@waldemarhorwat You're right, I think I had a bug. I've removed GetDirectiveContext in 19687c8 since it wasn't really helpful anymore. Please see if that resolves your issues. If we don't use the execution context stack to track the directive state here, what alternative would you recommend? /cc @allenwb

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I think the pragma contents will still need some bikeshedding, from an editor standpoint, I'm good!

@michaelficarra
Copy link
Member Author

I'm happy to bikeshed names during plenary, though sad to waste committee time on it. Haven't gotten much feedback from other delegates on tc39/proposal-function-implementation-hiding#3, unfortunately, so it would be irresponsible not to at least bring it up.

Copy link
Member

@mikesamuel mikesamuel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. Looks mostly good. Some questions below.

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
@mikesamuel
Copy link
Member

+1. As a reviewer, I sign off on this for stage 3.

@waldemarhorwat
Copy link

@waldemarhorwat You're right, I think I had a bug. I've removed GetDirectiveContext in 19687c8 since it wasn't really helpful anymore. Please see if that resolves your issues. If we don't use the execution context stack to track the directive state here, what alternative would you recommend? /cc @allenwb

As far as I can tell, the bug is fixed. The thing that makes me uneasy is that the use of a running execution context to implement lexical scoping makes it difficult to follow what's happening here — I had to do a deep trace of code throughout the spec to find all possible things that could be on the running execution context stack when OrdinaryFunctionCreate is called. Module and function lexical scopes push onto the execution context, but class lexical scopes don't, which is weird and not apparent without looking at the implementation. I'm with @allenwb here.

@erights
Copy link

erights commented Dec 2, 2019

@waldemarhorwat , I would also like to see lexical concepts avoid needless dependencies on dynamic concepts. How should this be stated?

@michaelficarra michaelficarra force-pushed the function-implementation-hiding branch from ec41948 to 06de41c Compare May 9, 2020 02:14
@michaelficarra
Copy link
Member Author

@waldemarhorwat @erights @allenwb I've update this PR to use static information for deriving [[HasSourceTextAvailable]] and [[PresentInStackTraces]], as opposed to tracking that information at run time via the Execution Context stack. I think this is a nice improvement. Of course, eval still uses the running Execution Context, but I'm pretty sure that's inevitable. Let me know if this satisfies your concerns.

Copy link
Member

@allenwb allenwb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I think this update has move in the right direction. However, eval code is still dependent upon the running execution context rather than being lexically based. As I said in #1739 (comment) "...there should be no significant difference in the handling of these new directives from how the strict mode direct[ive] is currently handled." But that isn't the case in this updated PR.

You have to take into account that eval is really two different things depending upon whether it is a direct eval or an indirect eval. Direct eval and indirect eval code have different lexical contexts (ignoring here for simplicity the additional differences between strict and non-strict eval code) that are statically knowable without reference to the current execution environment (notice that while it is not always knowable that a particular eval-like expression is a direct or indirect eval (or an eval at all). its possible direct and indirect eval lexical contexts are statically knowable so all that needs to be determine at execution time is whether or not the eval is direct as is done in https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation 6.a.iv-vi .

@bakkot
Copy link
Contributor

bakkot commented May 9, 2020

@allenwb

"...there should be no significant difference in the handling of these new directives from how the strict mode direct[ive] is currently handled."

I disagree with this. The strict mode directive is currently handled by defining a new kind of code, in prose, in a way which has caused pain in the past. I do not think the appropriate way to introduce these directives is to add two new kinds of code.

I'm fine with changing this PR to be more static; I think that should be doable by introducing definitions for PresentInStackTraces and HasSourceTextAvailable of CallExpression : CoverCallExpressionAndAsyncArrowHead for use in the function call semantics for direct evals. But I don't think we should try to precisely match how "use strict" works.

@allenwb
Copy link
Member

allenwb commented May 9, 2020

"...there should be no significant difference in the handling of these new directives from how the strict mode direct[ive] is currently handled."

I disagree with this. The strict mode directive is currently handled by defining a new kind of code, in prose, in a way which has caused pain in the past. I do not think the appropriate way to introduce these directives is to add two new kinds of code.

Sorry, I should clarify. I wasn't talking about how you approach specifying the determination and semantics of the different kinds of directives. I was talking about the rules (that a human needs to learn) concerning how the extent of the affect of directive is determined. The extent of affect of a lexically enclosing 'use strict' directive upon an direct or indirect eval should be the same as the extent of affect of a lexically enclosing 'hide source' (etc.) directive on the same eval. In other words, what a human needs to know to determining whether a 'use strict' applies should be the same as what they need to know to determine if a 'hide source'applies. And we know, that for 'use strict' this doesn't require any knowledge of the execution environment (other than determining whether an apparent direct eval really is one).

I don't care strongly about how exactly this is expressed in the specification but I agree that the kinds of code prose may be outliving its usefulness.

@bakkot
Copy link
Contributor

bakkot commented May 9, 2020

In other words, what a human needs to know to determining whether a 'use strict' applies should be the same as what they need to know to determine if a 'hide source' applies. And we know, that for 'use strict' this doesn't require any knowledge of the execution environment (other than determining whether an apparent direct eval really is one).

Oh, I see. That's always been true: in an earlier version of this PR the behavior was specified using knowledge of the execution environment, but that was just for editorial convenience; the actual semantics have always been static and have not changed across revisions of this PR. I had thought your concern was just about the editorial mechanism by which it was specified.

Anyway, sounds like we're on the same page.

@michaelficarra
Copy link
Member Author

@allenwb Thanks for the quick feedback. @bakkot helped me to remove all usage of execution contexts. Can you review again to see if this is acceptable now?

@michaelficarra michaelficarra force-pushed the function-implementation-hiding branch from 4921f31 to caf3fe7 Compare May 13, 2020 22:24
@michaelficarra
Copy link
Member Author

@waldemarhorwat or @erights Can you review the spec text here before the next meeting? Note that I will be asking to split the proposal and only advance the "hide source" directive.

@ljharb ljharb changed the title function implementation hiding Normative: function implementation hiding May 26, 2020
@ljharb ljharb added the normative change Affects behavior required to correctly evaluate some ECMAScript source text label May 26, 2020
@erights
Copy link

erights commented May 26, 2020

I have just reviewed with this split in mind. The portions of this relating just to hiding source text, as well as the resulting suppression of position and source location within a stack frame but no more, LGTM.

This is still a Draft so this comment is adequate, yes?

@michaelficarra
Copy link
Member Author

@erights Yes that is perfect. Thank you!

@erights
Copy link

erights commented May 27, 2020

I see at https://github.com/tc39/ecma262/pull/1739/files#r351043373 that @allenwb also prefers orthogonal "hide source" and "hide stack" directives. The resulting thread asks for a use case.

Here's one: When computing across a membrane, the stack frames of the membrane mechanism itself are often a distraction to the debugging experience. Debugger often do have better ways of handling the suppression of such unwanted detail. But for so-called printf debugging, looking at stacks dumped to logs, this could reduce the verbosity a lot. The Error stacks proposal will give better knobs for this as well.

My point is that I can imagine wanting to suppress stack traces for reasons that would not imply that I also want to suppress source.

Altogether, I am happy to see this go forward with only "hide source";. We should revisit the hiding of stack frames as part of the error stacks proposal.

@michaelficarra
Copy link
Member Author

FYI I've split the proposal into two commits in this PR to illustrate the intended proposal split more clearly.

spec.html Show resolved Hide resolved
<emu-alg>
1. If |CallExpression| occurs within a |ScriptBody|, |ModuleBody|, or |FunctionBody| that has a Directive Prologue that contains a Sensitive Directive, return *false*.
1. If the source text matched by |CallExpression| is eval code resulting from a direct eval, then
1. Return PresentInStackTraces of the |CallExpression| whose evaluation is the direct eval.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I get what you're trying to do here, but magically rematerializing the instance of the ancestor |CallExpression| that resulted in the particular direct eval that led to the instance of this |CallExpression| is harder to think about than I'd like. I wonder if there's a way to write this in a more obvious top-down manner, but the interleaving of static and run-time semantics definitely makes that very challenging.

One option is to have an argument-taking Static Semantics where direct eval can pass in the inherited "is sensitive" or "hide source" state. Not sure if that's a net editorial improvement...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option is to have an argument-taking Static Semantics where direct eval can pass in the inherited "is sensitive" or "hide source" state. Not sure if that's a net editorial improvement...

The problem is that we need the relevant bit at this point, during runtime semantics. Having it at the point that you are doing the eval call doesn't really help unless you propagate that information on some runtime construct like lexical environments.

Copy link
Contributor

@syg syg May 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I see. If we can't come up with more obvious verbiage here, I think that sentence warrants some note that all this is doing is trying to reflect propagate the "lexical" nesting of the directives across (space and?) time, due to direct evals.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a NOTE reading something like

NOTE: Sensitive Directives apply to nested functions, including to code within a direct call to eval.

I'm not in love with that wording but don't have a better one off the top of my head.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first commit LGTM otherwise

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@michaelficarra michaelficarra force-pushed the function-implementation-hiding branch from 223caaa to 9c3c9cc Compare May 28, 2020 18:06
@@ -490,7 +490,7 @@ <h1>The Numeric String Grammar</h1>
<h1>The Syntactic Grammar</h1>
<p>The <em>syntactic grammar</em> for ECMAScript is given in clauses 11, 12, 13, 14, and 15. This grammar has ECMAScript tokens defined by the lexical grammar as its terminal symbols (<emu-xref href="#sec-lexical-and-regexp-grammars"></emu-xref>). It defines a set of productions, starting from two alternative goal symbols |Script| and |Module|, that describe how sequences of tokens form syntactically correct independent components of ECMAScript programs.</p>
<p>When a stream of code points is to be parsed as an ECMAScript |Script| or |Module|, it is first converted to a stream of input elements by repeated application of the lexical grammar; this stream of input elements is then parsed by a single application of the syntactic grammar. The input stream is syntactically in error if the tokens in the stream of input elements cannot be parsed as a single instance of the goal nonterminal (|Script| or |Module|), with no tokens left over.</p>
<p>When a parse is successful, it constructs a <em>parse tree</em>, a rooted tree structure in which each node is a <dfn>Parse Node</dfn>. Each Parse Node is an <em>instance</em> of a symbol in the grammar; it represents a span of the source text that can be derived from that symbol. The root node of the parse tree, representing the whole of the source text, is an instance of the parse's goal symbol. When a Parse Node is an instance of a nonterminal, it is also an instance of some production that has that nonterminal as its left-hand side. Moreover, it has zero or more <em>children</em>, one for each symbol on the production's right-hand side: each child is a Parse Node that is an instance of the corresponding symbol.</p>
<p>When a parse is successful, it constructs a <em>parse tree</em>, a rooted tree structure in which each node is a <dfn>Parse Node</dfn>. Each Parse Node is an <em>instance</em> of a symbol in the grammar; it represents a span of the source text that can be derived from that symbol. The root node of the parse tree, representing the whole of the source text, is an instance of the parse's goal symbol. When a Parse Node is an instance of a nonterminal, it is also an instance of some production that has that nonterminal as its left-hand side. Moreover, it has zero or more <em>children</em>, one for each symbol on the production's right-hand side: each child is a Parse Node that is an instance of the corresponding symbol. A Parse Node _A_ <dfn>occurs within</dfn> another Parse Node _B_ if _A_ is the child of _B_ or if _A_ is the child of some Parse Node _C_ where _C_ occurs within _B_.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 13 existing uses of the phrase "occurs within", none of which match this definition but all of which get linked as a consequence of defining it here (e.g. "Every other finite time value t is defined relative to the greatest preceding time value s that is such a multiple, and represents the instant that occurs within the same UTC day as s but follows it by t − s milliseconds".)

I think it's probably fine not to mark this as a definition.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like our new use of it to be linked though. How do I do that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you could use an explicit <emu-xref> on the occurrences you want to link, or an <emu-not-ref> on the ones you don't want to link, but I think both of those approaches are sub-optimal. It would be better to come up with a different (distinct) phrase for this sense of "occurs within". E.g., "is a descendant of" or "descends from": either is a natural extension of the "child" terminology already established for parse trees, and neither occurs anywhere else in the spec.

spec.html Outdated Show resolved Hide resolved
@michaelficarra michaelficarra force-pushed the function-implementation-hiding branch from 9c3c9cc to 373610b Compare June 1, 2020 21:44
@erights erights removed their request for review July 11, 2020 17:01
@ljharb ljharb force-pushed the master branch 3 times, most recently from 3d0c24c to 7a79833 Compare June 29, 2021 02:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 normative change Affects behavior required to correctly evaluate some ECMAScript source text pending stage 4 This proposal has not yet achieved stage 4, but may otherwise be ready to merge. proposal This is related to a specific proposal, and will be closed/merged when the proposal reaches stage 4.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants