Ticket #226 (new defect)

Opened 1 year ago

Last modified 1 month ago

(Resolved) Versioning: how will "eval" handle versions?

Reported by: lth Assigned to: lth
Type: defect Priority: minor
Milestone: Component: Spec
Version: Harmony Keywords:
Cc: brendan, graydon, jeffdyer, chrispi, waldemar

Description (last modified by lth) (diff)

Compatibility issue ES3/ES4, your prompt replies are encouraged.

The versioning discussion has had two solutions: the mime type provides the value for a script, telling the implementation how to parse it; and a global variable or perhaps scoped parameter (the semantics are unclear as of yet) called __ECMASCRIPT_VERSION__ can be tested by the program, eg when it wants to insert new SCRIPT tags.

But how will eval cope? It invokes the compiler but has no provisions for script version, and there are two more wrinkles:

  • ES3 programs (ie programs that originate from scripts whose mime type indicates an ES3 style script) that call eval must get ES3 behavior automatically (let is a variable and all that).
  • ES4 programs will presumably expect ES4 behavior automatically, too.

Three cases.

1) eval(s). Here eval is an operator. The code for the operator can in principle be customized during compilation of the containing script, so it can have version-specific behavior.

2) window.eval(s). The value of window.eval is an object with identity, if it has different behavior based on its caller it must be able to inspect its caller and its caller's environment (eg to find out what its caller's notion of __ECMASCRIPT_VERSION__ is). This is doable. Is it what we want? Or do we want window.eval to choose the lowest version among the scripts loaded into that environment? This will almost always be ES3 because of event handlers, which are not mimetyped and hence have to be handled as ES3.

3) f = eval; f(s). Since the use of eval here means the same as if window.eval were used, this is like case 2; if f is passed to a different frame it will still use the environment in which it's closed, ie, the frame from which it was extracted.

This is really a web-only issue, we could leave it unspecified, but since we're elevating versioning into the spec (also only for the web) we should probably cover it.

Attachments

Change History

Changed 1 year ago by lth

  • description changed from Compatibility issue ES3/ES4, your prompt replies are encouraged. The versioning discussion has had two solutions: the mime type provides the value for a script, telling the implementation how to parse it; and a global variable or perhaps scoped parameter (the semantics are unclear as of yet) called `__ECMASCRIPT_VERSION__` can be tested by the program, eg when it wants to insert new SCRIPT tags. But how will `eval` cope? It invokes the compiler but has no provisions for script version, and there are two more wrinkles: * ES3 programs (ie programs that originate from scripts whose mime type indicates an ES3 style script) that call eval __must__ get ES3 behavior automatically (`let` is a variable and all that). * ES4 programs will presumably expect ES4 behavior automatically, too. Three cases. 1) eval(s). Here eval is an operator. The code for the operator can in principle be customized during compilation of the containing script, so it can have version-specific behavior. 2) window.eval(s). The value of window.eval is an object with identity, if it has different behavior based on its caller it must be able to inspect its caller and its caller's environment (eg to find out what its caller's notion of `__ECMASCRIPT_VERSION__` is). This is doable. Is it what we want? Or do we want window.eval to choose the lowest version among the scripts loaded into that environment? This will almost always be ES3 because of event handlers, which are not mimetyped and hence have to be handled as ES3. 3) f = eval; f(s). Since the use of `eval` here means the same as if `window.eval` were used, this is like case 2; if f is passed to a different frame it will still use the environment in which it's closed, ie, the frame from which it was extracted. This cannot be customized based on script content because one global environment ("window") can have scripts of more than one version; a call to `window.eval` or to `f` that is an alias for the `eval` function However, it __may__ be possible to spec it to use the lowest language version currently loaded into the document (or whatever). This is really a web-only issue, we could leave it unspecified, but since we're elevating versioning into the spec (also only for the web) we should probably cover it. to Compatibility issue ES3/ES4, your prompt replies are encouraged. The versioning discussion has had two solutions: the mime type provides the value for a script, telling the implementation how to parse it; and a global variable or perhaps scoped parameter (the semantics are unclear as of yet) called `__ECMASCRIPT_VERSION__` can be tested by the program, eg when it wants to insert new SCRIPT tags. But how will `eval` cope? It invokes the compiler but has no provisions for script version, and there are two more wrinkles: * ES3 programs (ie programs that originate from scripts whose mime type indicates an ES3 style script) that call eval __must__ get ES3 behavior automatically (`let` is a variable and all that). * ES4 programs will presumably expect ES4 behavior automatically, too. Three cases. 1) eval(s). Here eval is an operator. The code for the operator can in principle be customized during compilation of the containing script, so it can have version-specific behavior. 2) window.eval(s). The value of window.eval is an object with identity, if it has different behavior based on its caller it must be able to inspect its caller and its caller's environment (eg to find out what its caller's notion of `__ECMASCRIPT_VERSION__` is). This is doable. Is it what we want? Or do we want window.eval to choose the lowest version among the scripts loaded into that environment? This will almost always be ES3 because of event handlers, which are not mimetyped and hence have to be handled as ES3. 3) f = eval; f(s). Since the use of `eval` here means the same as if `window.eval` were used, this is like case 2; if f is passed to a different frame it will still use the environment in which it's closed, ie, the frame from which it was extracted. This is really a web-only issue, we could leave it unspecified, but since we're elevating versioning into the spec (also only for the web) we should probably cover it.

Changed 1 year ago by lth

See also #227, on event handlers and URIs.

Changed 1 year ago by brendan

I fear the __ECMASCRIPT_VERSION__ proposal was unclear. This global const is not meant to vary. It should contain a number telling the implementation's maximum version supported.

Something else could reflect the ;version= value from the RFC 4329 MIME type, or equivalent. But no one has proposed such a reflection. On the other hand, at least SpiderMonkey? and I suspect other browser engines do have a runtime member of their "Execution Environment" data structure (to use ES1-3 terms). This member varies according to how the script (scripted function) was compiled, from what script tag with what version indicator in its type= or language= attribute it came.

/be

Changed 1 year ago by brendan

So to finish the tale, the version used when compiling eval's program in SpiderMonkey? is the version of its caller -- thus dynamic scope entails version selection. There's no way to override (nor should there be, IMHO).

/be

Changed 1 year ago by lth

But the problem is that the caller may not have a version number as such. Consider:

<SCRIPT type="text/ecmascript;version=4">
f367 = eval;
</SCRIPT>
<SCRIPT type="text/ecmascript">
f376("let = 10")
</SCRIPT>

What is the version? Eval was picked up in a v4 context and is called from a v3 context. It's the same global object in both scripts. And eval is here a plain old function, not an operator.

Changed 1 year ago by brendan

In that example the caller's version in SpiderMonkey? would be JSVERSION_DEFAULT, which is pretty much ES3 + E4X (minus CDATA literals since those conflict with the old script hiding hack of wrapping script tag contents with <!-- and -->) + syntactic extensions such as getters and setters, + some ugly-named-enough-to-be-safe extensions like __proto__.

So yeah, there is a default version used in the absence of explicit version selection in the script tag or content-type header or meta tag equivalent. This is the default mentioned in #227, which applies to event handlers too.

For the near term (I've been at this game too long ;-), the default version must remain ES3-ish. E.g., SpiderMonkey? won't change its default to its "latest" (which btw we name with an alias for API convenience where the API client has captive content it can force-upgrade and so wants to track the latest: JSVERSION_LATEST).

Down the road, things may change. It will take time and my crystal ball doesn't see that far. Not sure the spec needs to say more here than eval uses caller's version, and cite the default-version specs I mentioned over in #227.

Does this help?

/be

Changed 1 year ago by lth

  • cc changed from brendan,graydon,jeffdyer,chrispi to brendan, graydon, jeffdyer, chrispi, waldemar

My proposal:

  • eval gets a second, optional parameter, which must be a nonnegative integer expressing the edition number whose set of reserved words should be used for parsing the string. Anything less than 4 means 3. Anything greater than the value of __ECMASCRIPT_VERSION__ means to use the value of __ECMASCRIPT_VERSION__.
  • The default value for the second parameter is 3, and it should remain 3 also in future revisions.
  • All variants of eval accept this parameter.

Rationale: explicit is better than implicit. If the implementation can reasonably know what the programmer meant, then that's fine, but in this case it's not clear that either static scope (version on the window) or dynamic scope (version of the caller) is correct. Since almost nobody needs the lexically scoped variety of eval, then users are free to construct abstractions a la

   function ev($$$s)
       eval($$$s, 4)

if passing the parameter bugs them. I expect the change to be uncontroversial.

Changed 1 year ago by jeffdyer

We'd (Lars and Jeff) like to propose additionally that if you ask for a version > 3 you get a new empty variable object for the 'eval' code. This allows the eval code to actually have fixtures like namespaces and classes, which are impossible without it. This is because they would introduce fixtures in the surrounding variable object.

Changed 1 year ago by lth

Note, the present proposal is only about version numbers, see longer comment above the previous one. Whether to attach other meaning to the version number is factored out as proposal #297.

Changed 1 year ago by lth

  • priority changed from major to trivial
  • summary changed from Versioning: how will "eval" handle versions? to (Resolved) Versioning: how will "eval" handle versions?

Approved 27 Nov

Changed 1 month ago by David-Sarah Hopwood

  • priority changed from trivial to minor
  • version changed from 4 to Harmony

Proposal still relevant to ES-Harmony.

Note: See TracTickets for help on using tickets.