tc39 / proposal-shadowrealm

ECMAScript Proposal, specs, and reference implementation for Realms
https://tc39.es/proposal-shadowrealm/
1.41k stars 67 forks source link

Enable writing code directly to be evaluated synchronously #402

Open ByteEater-pl opened 3 months ago

ByteEater-pl commented 3 months ago

ShadowRealm.prototype.evaluate as currently specified takes a string. This is sub-optimal.

If the ECMAScript code to be evaluated is contained in a string literal (likely to be much more common than any other case), occurrences of its delimiters (", ' or backtick) in it need escaping.

Editors and other tools are unable to detect in all cases that a string literal is intended to be passed to ShadowRealm.prototype.evaluate and provide proper support (for editors it would be syntax highlighting, completion suggestions, etc.).

It'd be therefore desirable to have a better syntactic alternative.

Module expressions come to mind, giving:

myRealm.evaluate(module {
    // some code
})

But modules are evaluated asynchronously. It'd probably be too confusing to carve out an exception for ShadowRealm.prototype.evaluate.

If only source code could be extracted from a module object! Not necessarily all of them. And possibly at some phase, not yet present in ECMA-262, or without sufficient API yet. A phase that module expressions feed into. If undesirable for normal modules, perhaps a subclass could be created for module expressions which provides a property or method wrapping what's inside the brackets as string.

ByteEater-pl commented 3 months ago

A possibility (new idiom?):

myRealm.evaluate((() => {
    // some code
}).toString().slice(7, -1))

Much better than a string, although tools would still confuse scopes and e.g. consider identifiers in some code to refer to outer variables.

ljharb commented 3 months ago

That won’t work, because the function could close over objects from the outer realm, including the global. A string, or a host-specific specifier like in Workers, is the only option.

mhofman commented 3 months ago

I wouldn't say never on module loading being async. Technically a module expression would have its module graph already resolved, so we could imagine in the future a way to synchronously evaluate them in another realm. This is definitely for future proposals to figure out.

ByteEater-pl commented 3 months ago

@ljharb, what do you mean that won't work? Yes, the function closes over whatever's referenced from outer scopes, but toString doesn't care, it only gets the expression used to define the function as string. The variable names are then interpreted in a different way inside the ShadowRealm.

ByteEater-pl commented 3 months ago

Shorter alternative:

myRealm.evaluate(`(${() => {
    // some code
}})()`)

It nests the anonymous function scope, but in many cases that's probably OK. Additionally, it works just as well when // some code is just an expression and the brackets are omitted, whereas the former would require a change to e.g. slice(6).

ljharb commented 3 months ago

@ByteEater-pl i'm confused, if it's converting the function to a string and evaling it, how is that any better than just accepting a string directly?

ByteEater-pl commented 3 months ago

@ljharb, from my initial comment:

If the ECMAScript code to be evaluated is contained in a string literal (likely to be much more common than any other case), occurrences of its delimiters (", ' or backtick) in it need escaping.

Editors and other tools are unable to detect in all cases that a string literal is intended to be passed to ShadowRealm.prototype.evaluate and provide proper support (for editors it would be syntax highlighting, completion suggestions, etc.).

ljharb commented 3 months ago

ah. that seems like https://github.com/hax/proposal-raw-string-literals, which was unable to achieve stage 1 - meaning, the committee didn't seem to think that problem was worth solving.

ByteEater-pl commented 3 months ago

There is overlap. But here it's just about ECMAScript code, treated as such. Actually, obviating the need for escaping is a nice bonus for me, however my main motivation is to have tools, especially editors, handle the code as code (with coloring etc.), not as string literal.

If "Raw string literals" is ever revived, though, it might be worth generalizing, so that a hint for tools can be provided, indicating the language, with a predefined hint (say es) for ECMAScript and the rest implementation-defined. To wit:


const
    someECMAScript = LanguageHint.es@``requestLeave(2) // I would like to request `2 leave days`.``,
    someXML = LanguageHint.xml@``<p>In ECMAScript, <code>`</code> can be used to delimit multi-line strings.</p>``