Description
I'm reflecting back on the AST and the overzealous #37 and thinking I could probably narrow it down a bit further. JS has a lot of cases where there's multiple ways to do things and it semantically makes no difference even in the general case:
function foo() { ... }
vsvar foo
+foo = function foo() { ... }
at the top of the enclosing block's scopeexport class Foo { ... }
vsexport {Foo}; class Foo { ... }
and similarimport foo, * as Foo from "mod"
vsimport foo from "mod"; import * as Foo from "mod"
import foo from "mod"
vsimport {default as foo} from "mod"
export {foo, bar}
vsexport {foo}; export {bar}
export {foo}
vsexport {foo as foo}
var foo = ...
vslet foo
at the top of the enclosing function's scope +foo = ...
void expr
vs(expr, void 0)
- In strict mode contexts, when any non-configurable, non-writable global is referenced (like
undefined
,Infinity
,NaN
, and similar), they could be converted to literals - Labeled statements other than block statements, loops, and
eval
expressions can't possibly be broken or continued from. These might as well be nixed, and you could always create synthetic blocks as necessary. {foo: foo}
vs{foo}
- Gzip can remove the redundancy if we let it.{foo: 1}
vs{"foo": 1}
,{1: foo}
vs{"1": foo}
, etc.x => result
vsx => { return result }
- And many others I didn't list.
There's also a few cases where the spec ends up complicating encoding if followed to the letter.
export default 1
in the spec is desugared tovar *default*; *default* = 1; export {*default* as default}
, where*default*
is a spec-internal production. You could get away with replacing this withdefault
and simplify it some -default
is still a reserved keyword.- The list of bindings is really defined per-block, so you could define their names and just use a separate initialization operation, potentially just to a
void 0
. - All imports and exports are hoisted, and local exports already defer missing binding errors to runtime. Hoisting these in the encoding would allow engines to immediately fetch for the module's static imports before processing the rest of the module.
- There's a lot of internal duplication between
import ... from "mod"
andexport ... from "mod"
. - Labels cannot be empty strings. Empty strings could be super convenient to just
So I'm thinking the AST could be reduced to a smaller, significantly more easily parsed and consumed subset so engines can realize parsing gains much quicker.
-
Directives would just be strings. The node type isn't used anywhere as far as I can tell.
-
The only things that can carry labels are block statements and loops. Labeled statements that contain
eval
or ado
expression should just emit a synthetic block. -
Modules would be reduced to this:
- Directives
- Static import dependency list, a list of entries like this:
- Source
- Is anonymous (i.e.
import "mod"
orexport ... from "mod"
) - Namespace import name,
*
forexport * from "mod"
, or empty string if no named import - List of named imports, including default import
- Export name list, a list of entries of one of these variants:
- Export synthetic: export binding + import index + import name index
- Export local: export binding + export name
- Import local list, a list of import index + import name index + export name triples
- Statement list
-
Function bodies would be reduced to this:
- Type: method/arrow, generator, ES5-style
- Parameter names
- Closed-over names, including
this
as applicable - Is async
- Statement list
-
Getter/setter bodies would be reduced to this:
- Closed-over names, including
this
as applicable - Setter parameter
- Getter statement list
- Setter statement list
- Closed-over names, including
-
Statement lists would be reduced to this:
- Contains
eval
- List of declared variable names
- List of statements
- Contains
-
Declared variables would be unified to either
let
orconst
, with novar
s permitted. -
void expr
is represented identically to(expr, undefined)
, but using theundefined
literal production rather than theundefined
value. -
Unlabeled
break
/continue
statements could just have their "label" set to the impossible empty string. -
To simplify/streamline initialization, it'd have its own node type, mirroring assignment functionally. A node can't be assigned more than once.
- Default exports are performed by initializing the empty string variable to the value.
-
And to simplify the spec, statement lists should be generally unified.
- Block statements can just consist of a block + label/empty string (if unlabeled) + statement list
Of course there's ways to further reduce this (reducing block labels to break depth
), but most of that's beyond the scope of this bug. I'm just looking at stuff that still mirrors JS while removing the duplication.
Activity