From 2263216f3e526b6f00529145908b41e548b12d65 Mon Sep 17 00:00:00 2001 From: hhugo Date: Sun, 26 Nov 2023 16:12:30 +0100 Subject: [PATCH] Compiler: support for es6 import/export (#1529) --- compiler/bin-jsoo_minify/jsoo_minify.ml | 3 +- compiler/lib/driver.ml | 2 +- compiler/lib/javascript.ml | 40 +++++++ compiler/lib/javascript.mli | 40 +++++++ compiler/lib/js_output.ml | 153 ++++++++++++++++++++++-- compiler/lib/js_parser.mly | 133 ++++++++++++++++++++ compiler/lib/js_simpl.ml | 2 + compiler/lib/js_token.ml | 3 + compiler/lib/js_token.mli | 1 + compiler/lib/js_traverse.ml | 135 +++++++++++++++++++-- compiler/lib/js_traverse.mli | 11 +- compiler/lib/parse_js.ml | 20 +++- compiler/lib/stdlib.ml | 4 + compiler/lib/wasm/wa_link.ml | 2 +- 14 files changed, 526 insertions(+), 23 deletions(-) diff --git a/compiler/bin-jsoo_minify/jsoo_minify.ml b/compiler/bin-jsoo_minify/jsoo_minify.ml index 22ffdb132c..f607fa369b 100644 --- a/compiler/bin-jsoo_minify/jsoo_minify.ml +++ b/compiler/bin-jsoo_minify/jsoo_minify.ml @@ -81,7 +81,8 @@ let f { Cmd_arg.common; output_file; use_stdin; files } = let true_ () = true in let open Config in let passes : ((unit -> bool) * (unit -> Js_traverse.mapper)) list = - [ (Flag.shortvar, fun () -> (new Js_traverse.rename_variable :> Js_traverse.mapper)) + [ ( Flag.shortvar + , fun () -> (new Js_traverse.rename_variable ~esm:false :> Js_traverse.mapper) ) ; (true_, fun () -> new Js_traverse.simpl) ; (true_, fun () -> new Js_traverse.clean) ] diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index 7c0ed54ff6..889ef36237 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -560,7 +560,7 @@ if (typeof module === 'object' && module.exports) { if Config.Flag.shortvar () then ( let t5 = Timer.make () in - let js = (new Js_traverse.rename_variable)#program js in + let js = (new Js_traverse.rename_variable ~esm:false)#program js in if times () then Format.eprintf " shortten vars: %a@." Timer.print t5; js) else js diff --git a/compiler/lib/javascript.ml b/compiler/lib/javascript.ml index 756fdefdc5..256f3cc098 100644 --- a/compiler/lib/javascript.ml +++ b/compiler/lib/javascript.ml @@ -346,6 +346,8 @@ and statement = | Throw_statement of expression | Try_statement of block * (formal_parameter option * block) option * block option | Debugger_statement + | Import of import * Parse_info.t + | Export of export * Parse_info.t and ('left, 'right) either = | Left of 'left @@ -422,6 +424,44 @@ and function_body = statement_list and program = statement_list +and export = + | ExportVar of variable_declaration_kind * variable_declaration list + | ExportFun of ident * function_declaration + | ExportClass of ident * class_declaration + | ExportNames of (ident * Utf8_string.t) list + (* default *) + | ExportDefaultFun of ident * function_declaration + | ExportDefaultClass of ident * class_declaration + | ExportDefaultExpression of expression + (* from *) + | ExportFrom of + { kind : export_from_kind + ; from : Utf8_string.t + } + | CoverExportFrom of early_error + +and export_from_kind = + | Export_all of Utf8_string.t option + | Export_names of (Utf8_string.t * Utf8_string.t) list + +and import = + { from : Utf8_string.t + ; kind : import_kind + } + +and import_default = ident + +and import_kind = + | Namespace of import_default option * ident + (* import * as name from "fname" *) + (* import defaultname, * as name from "fname" *) + | Named of import_default option * (Utf8_string.t * ident) list + (* import { 'a' as a, ...} from "fname" *) + (* import defaultname, { 'a' as a, ...} from "fname" *) + | Default of import_default + (* import defaultname from "fname" *) + | SideEffect (* import "fname" *) + and program_with_annots = (statement_list * (Js_token.Annot.t * Parse_info.t) list) list let compare_ident t1 t2 = diff --git a/compiler/lib/javascript.mli b/compiler/lib/javascript.mli index 75cf608a77..69a87bea9d 100644 --- a/compiler/lib/javascript.mli +++ b/compiler/lib/javascript.mli @@ -266,6 +266,8 @@ and statement = | Throw_statement of expression | Try_statement of block * (formal_parameter option * block) option * block option | Debugger_statement + | Import of import * Parse_info.t + | Export of export * Parse_info.t and ('left, 'right) either = | Left of 'left @@ -342,6 +344,44 @@ and function_body = statement_list and program = statement_list +and export = + | ExportVar of variable_declaration_kind * variable_declaration list + | ExportFun of ident * function_declaration + | ExportClass of ident * class_declaration + | ExportNames of (ident * Utf8_string.t) list + (* default *) + | ExportDefaultFun of ident * function_declaration + | ExportDefaultClass of ident * class_declaration + | ExportDefaultExpression of expression + (* from *) + | ExportFrom of + { kind : export_from_kind + ; from : Utf8_string.t + } + | CoverExportFrom of early_error + +and export_from_kind = + | Export_all of Utf8_string.t option + | Export_names of (Utf8_string.t * Utf8_string.t) list + +and import = + { from : Utf8_string.t + ; kind : import_kind + } + +and import_default = ident + +and import_kind = + | Namespace of import_default option * ident + (* import * as name from "fname" *) + (* import defaultname, * as name from "fname" *) + | Named of import_default option * (Utf8_string.t * ident) list + (* import { 'a' as a, ...} from "fname" *) + (* import defaultname, { 'a' as a, ...} from "fname" *) + | Default of import_default + (* import defaultname from "fname" *) + | SideEffect (* import "fname" *) + and program_with_annots = (statement_list * (Js_token.Annot.t * Parse_info.t) list) list val compare_ident : ident -> ident -> int diff --git a/compiler/lib/js_output.ml b/compiler/lib/js_output.ml index 2db393ddc0..1898a8e01d 100644 --- a/compiler/lib/js_output.ml +++ b/compiler/lib/js_output.ml @@ -289,7 +289,9 @@ struct | Try_statement _ | Function_declaration _ | Class_declaration _ - | Debugger_statement -> false + | Debugger_statement + | Import _ + | Export _ -> false let starts_with ~obj ~funct ~let_identifier ~async_identifier l e = let rec traverse l e = @@ -368,6 +370,13 @@ struct Buffer.add_char b quote; PP.string f (Buffer.contents b) + let pp_string_lit f (Stdlib.Utf8_string.Utf8 s) = + let quote = best_string_quote s in + pp_string f ~quote s + + let pp_ident_or_string_lit f (Stdlib.Utf8_string.Utf8 s_lit as s) = + if is_ident s_lit then PP.string f s_lit else pp_string_lit f s + let rec comma_list f f_elt l = match l with | [] -> () @@ -523,9 +532,7 @@ struct then ( PP.string f ")"; PP.end_group f) - | EStr (Utf8 s) -> - let quote = best_string_quote s in - pp_string f ~quote s + | EStr x -> pp_string_lit f x | ETemplate l -> template f l | EBool b -> PP.string f (if b then "true" else "false") | ENum num -> @@ -833,9 +840,7 @@ struct and property_name f n = match n with | PNI (Utf8 s) -> PP.string f s - | PNS (Utf8 s) -> - let quote = best_string_quote s in - pp_string f ~quote s + | PNS s -> pp_string_lit f s | PNN v -> expression Expression f (ENum v) | PComputed e -> PP.string f "["; @@ -1409,6 +1414,140 @@ struct PP.string f "finally"; block f b); PP.end_group f + | Import ({ kind; from }, _loc) -> + PP.start_group f 0; + PP.string f "import"; + (match kind with + | SideEffect -> () + | Default i -> + PP.space f; + ident f i + | Namespace (def, i) -> + Option.iter def ~f:(fun def -> + PP.space f; + ident f def; + PP.string f ","); + PP.space f; + PP.string f "* as "; + ident f i + | Named (def, l) -> + Option.iter def ~f:(fun def -> + PP.space f; + ident f def; + PP.string f ","); + PP.space f; + PP.string f "{"; + PP.space f; + comma_list + f + (fun f (s, i) -> + if match i with + | S { name; _ } when Stdlib.Utf8_string.equal name s -> true + | _ -> false + then ident f i + else ( + pp_ident_or_string_lit f s; + PP.string f " as "; + ident f i)) + l; + PP.space f; + PP.string f "}"); + (match kind with + | SideEffect -> () + | _ -> + PP.space f; + PP.string f "from"); + PP.space f; + pp_string_lit f from; + PP.string f ";"; + PP.end_group f + | Export (e, _loc) -> + PP.start_group f 0; + PP.string f "export"; + (match e with + | ExportNames l -> + PP.space f; + PP.string f "{"; + PP.space f; + comma_list + f + (fun f (i, s) -> + if match i with + | S { name; _ } when Stdlib.Utf8_string.equal name s -> true + | _ -> false + then ident f i + else ( + ident f i; + PP.string f " as "; + pp_ident_or_string_lit f s)) + l; + PP.space f; + PP.string f "};" + | ExportFrom { kind; from } -> + PP.space f; + (match kind with + | Export_all None -> PP.string f "*" + | Export_all (Some s) -> + PP.string f "* as "; + pp_ident_or_string_lit f s + | Export_names l -> + PP.string f "{"; + PP.space f; + comma_list + f + (fun f (a, b) -> + if Stdlib.Utf8_string.equal a b + then pp_ident_or_string_lit f a + else ( + pp_ident_or_string_lit f a; + PP.string f " as "; + pp_ident_or_string_lit f b)) + l; + PP.space f; + PP.string f "}"); + PP.space f; + PP.string f "from"; + PP.space f; + pp_string_lit f from; + PP.string f ";" + | ExportDefaultExpression ((EFun _ | EClass _) as e) -> + PP.space f; + PP.string f "default"; + PP.space f; + expression Expression f e + | ExportDefaultExpression e -> + PP.space f; + PP.string f "default"; + PP.space f; + parenthesized_expression + ~last_semi + ~obj:true + ~funct:true + ~let_identifier:true + Expression + f + e + | ExportDefaultFun (id, decl) -> + PP.space f; + PP.string f "default"; + PP.space f; + statement f (Function_declaration (id, decl), loc) + | ExportDefaultClass (id, decl) -> + PP.space f; + PP.string f "default"; + PP.space f; + statement f (Class_declaration (id, decl), loc) + | ExportFun (id, decl) -> + PP.space f; + statement f (Function_declaration (id, decl), loc) + | ExportClass (id, decl) -> + PP.space f; + statement f (Class_declaration (id, decl), loc) + | ExportVar (k, l) -> + PP.space f; + variable_declaration_list k (not can_omit_semi) f l + | CoverExportFrom e -> early_error e); + PP.end_group f and statement_list f ?skip_last_semi b = match b with diff --git a/compiler/lib/js_parser.mly b/compiler/lib/js_parser.mly index f3a1ccbfd0..3f95883748 100644 --- a/compiler/lib/js_parser.mly +++ b/compiler/lib/js_parser.mly @@ -67,6 +67,10 @@ let vartok pos tok = let utf8_s = Stdlib.Utf8_string.of_string_exn +let name_of_ident = function + | S { name; _} -> name + | V _ -> assert false + %} (*************************************************************************) @@ -120,6 +124,7 @@ T_PACKAGE T_DEBUGGER T_GET T_SET T_FROM +T_AS T_TARGET T_META (*-----------------------------------------*) @@ -249,6 +254,9 @@ program: module_item: | item { $symbolstartpos, $1 } + | import_decl { $symbolstartpos, $1 } + | export_decl { $symbolstartpos, $1 } + (*************************************************************************) (* statement *) @@ -269,6 +277,131 @@ decl: | class_decl { let i,f = $1 in Class_declaration (i,f), p $symbolstartpos } +(*************************************************************************) +(* Namespace *) +(*************************************************************************) +(*----------------------------*) +(* import *) +(*----------------------------*) + +import_decl: + | T_IMPORT kind=import_clause from=from_clause sc + { let pos = $symbolstartpos in + Import ({ from; kind }, pi pos), p pos } + | T_IMPORT from=module_specifier sc + { let pos = $symbolstartpos in + Import ({ from; kind = SideEffect }, pi pos), p pos } + +import_clause: + | import_default { Default $1 } + | import_default "," "*" T_AS id=binding_id { Namespace (Some $1, id) } + | "*" T_AS id=binding_id { Namespace (None, id) } + | import_default "," x=named_imports { Named (Some $1, x) } + | x=named_imports { Named (None, x) } + +import_default: binding_id { $1 } + +named_imports: + | "{" "}" { [] } + | "{" listc(import_specifier) "}" { $2 } + | "{" listc(import_specifier) "," "}" { $2 } + +(* also valid for export *) +from_clause: T_FROM module_specifier {$2 } + +import_specifier: + | binding_id { (name_of_ident $1, $1) } + | string_or_ident T_AS binding_id { + let (_,s,_) = $1 in + (s, $3) } + +%inline string_or_ident: + | T_STRING { `String, fst $1, $symbolstartpos } + | T_DEFAULT { `Ident, Stdlib.Utf8_string.of_string_exn "default", $symbolstartpos } + | id { `Ident, $1, $symbolstartpos } + +module_specifier: + | T_STRING { (fst $1) } + +(*----------------------------*) +(* export *) +(*----------------------------*) + +export_decl: + | T_EXPORT names=export_clause sc { + let exception Invalid of Lexing.position in + let k = + try + let names = + List.map (fun ((k, id,pos), (_,s,_)) -> + match k with + | `Ident -> (var (p pos) id, s) + | `String -> raise (Invalid pos)) + names + in + (ExportNames names) + with Invalid pos -> + CoverExportFrom (early_error (pi pos)) + in + let pos = $symbolstartpos in + Export (k, pi pos), p pos } + | T_EXPORT v=variable_stmt + { + let pos = $symbolstartpos in + let k = match v with + | Variable_statement (k,l) -> ExportVar (k, l) + | _ -> assert false + in + Export (k, pi pos), p pos } + | T_EXPORT d=decl + { let k = match d with + | Variable_statement (k,l),_ -> ExportVar (k,l) + | Function_declaration (id, decl),_ -> ExportFun (id,decl) + | Class_declaration (id, decl),_ -> ExportClass (id,decl) + | _ -> assert false + in + let pos = $symbolstartpos in + Export (k,pi pos), p pos } + (* in theory just func/gen/class, no lexical_decl *) + | T_EXPORT T_DEFAULT e=assignment_expr sc + { + let k = match e with + | EFun (Some id, decl) -> + ExportDefaultFun (id,decl) + | EClass (Some id, decl) -> + ExportDefaultClass (id, decl) + | e -> ExportDefaultExpression e + in + let pos = $symbolstartpos in + Export (k,pi pos), p pos } +| T_EXPORT "*" T_FROM from=module_specifier sc { + let kind = Export_all None in + let pos = $symbolstartpos in + Export (ExportFrom ({from; kind}),pi pos), p pos + } + | T_EXPORT "*" T_AS id=string_or_ident T_FROM from=module_specifier sc { + let (_,id,_) = id in + let kind = Export_all (Some id) in + let pos = $symbolstartpos in + Export (ExportFrom ({from; kind}), pi pos), p pos + } +| T_EXPORT names=export_clause T_FROM from=module_specifier sc { + let names = List.map (fun ((_,a,_), (_,b,_)) -> a, b) names in + let kind = Export_names names in + let pos = $symbolstartpos in + Export (ExportFrom ({from; kind}), pi pos), p pos + } + +export_specifier: + | string_or_ident { ($1, $1) } + | string_or_ident T_AS string_or_ident { ($1, $3) } + +export_clause: + | "{" "}" { [] } + | "{" listc(export_specifier) "}" { $2 } + | "{" listc(export_specifier) "," "}" { $2 } + + (*************************************************************************) (* Variable decl *) (*************************************************************************) diff --git a/compiler/lib/js_simpl.ml b/compiler/lib/js_simpl.ml index 536dc00bbd..f552f52492 100644 --- a/compiler/lib/js_simpl.ml +++ b/compiler/lib/js_simpl.ml @@ -179,6 +179,8 @@ let rec depth = function | Try_statement (b, _, None) -> depth_block b + 1 | Try_statement (b, _, Some b2) -> max (depth_block b) (depth_block b2) + 1 | Debugger_statement -> 1 + | Import _ -> 1 + | Export _ -> 1 and depth_block b = List.fold_left b ~init:0 ~f:(fun acc (s, _) -> max acc (depth s)) diff --git a/compiler/lib/js_token.ml b/compiler/lib/js_token.ml index 4f0e56c0d5..e6a4a7b614 100644 --- a/compiler/lib/js_token.ml +++ b/compiler/lib/js_token.ml @@ -148,6 +148,7 @@ type t = | T_BACKQUOTE | T_DOLLARCURLY | T_ENCAPSED_STRING of string + | T_AS (* Extra tokens *) | T_ERROR of string | T_EOF @@ -303,6 +304,7 @@ let to_string = function | T_BACKQUOTE -> "`" | T_DOLLARCURLY -> "${" | T_ENCAPSED_STRING s -> s + | T_AS -> "as" let to_string_extra x = to_string x @@ -375,4 +377,5 @@ let is_keyword s = | "from" -> Some T_FROM | "target" -> Some T_TARGET | "meta" -> Some T_META + | "as" -> Some T_AS | _ -> None diff --git a/compiler/lib/js_token.mli b/compiler/lib/js_token.mli index 6c6a38e62f..2771555d80 100644 --- a/compiler/lib/js_token.mli +++ b/compiler/lib/js_token.mli @@ -147,6 +147,7 @@ type t = | T_BACKQUOTE | T_DOLLARCURLY | T_ENCAPSED_STRING of string + | T_AS (* Extra tokens *) | T_ERROR of string | T_EOF diff --git a/compiler/lib/js_traverse.ml b/compiler/lib/js_traverse.ml index 6f9c79cdff..e747f3aa73 100644 --- a/compiler/lib/js_traverse.ml +++ b/compiler/lib/js_traverse.ml @@ -71,6 +71,10 @@ class type mapper = object method program : Javascript.program -> Javascript.program method function_body : statement_list -> statement_list + + method import : import -> import + + method export : export -> export end (* generic js ast walk/map *) @@ -187,6 +191,47 @@ class map : mapper = , match final with | None -> None | Some s -> Some (m#block s) ) + | Import (import, loc) -> Import (m#import import, loc) + | Export (export, loc) -> Export (m#export export, loc) + + method import { from; kind } = + let kind = + match kind with + | Namespace (iopt, i) -> Namespace (Option.map ~f:m#ident iopt, m#ident i) + | Named (iopt, l) -> + Named + (Option.map ~f:m#ident iopt, List.map ~f:(fun (s, id) -> s, m#ident id) l) + | Default import_default -> Default (m#ident import_default) + | SideEffect -> SideEffect + in + { from; kind } + + method export e = + match e with + | ExportVar (k, l) -> ( + match m#statement (Variable_statement (k, l)) with + | Variable_statement (k, l) -> ExportVar (k, l) + | _ -> assert false) + | ExportFun (id, f) -> ( + match m#statement (Function_declaration (id, f)) with + | Function_declaration (id, f) -> ExportFun (id, f) + | _ -> assert false) + | ExportClass (id, f) -> ( + match m#statement (Class_declaration (id, f)) with + | Class_declaration (id, f) -> ExportClass (id, f) + | _ -> assert false) + | ExportNames l -> ExportNames (List.map ~f:(fun (id, s) -> m#ident id, s) l) + | ExportDefaultFun (id, decl) -> ( + match m#statement (Function_declaration (id, decl)) with + | Function_declaration (id, decl) -> ExportDefaultFun (id, decl) + | _ -> assert false) + | ExportDefaultClass (id, decl) -> ( + match m#statement (Class_declaration (id, decl)) with + | Class_declaration (id, decl) -> ExportDefaultClass (id, decl) + | _ -> assert false) + | ExportDefaultExpression e -> ExportDefaultExpression (m#expression e) + | ExportFrom l -> ExportFrom l + | CoverExportFrom e -> CoverExportFrom (m#early_error e) method statement_o x = match x with @@ -340,6 +385,10 @@ class type iterator = object method program : Javascript.program -> unit method function_body : Javascript.statement_list -> unit + + method import : import -> unit + + method export : export -> unit end (* generic js ast iterator *) @@ -466,6 +515,31 @@ class iter : iterator = match final with | None -> () | Some s -> m#block s) + | Import (x, _loc) -> m#import x + | Export (x, _loc) -> m#export x + + method import { from = _; kind } = + match kind with + | Namespace (iopt, i) -> + Option.iter ~f:m#ident iopt; + m#ident i + | Named (iopt, l) -> + Option.iter ~f:m#ident iopt; + List.iter ~f:(fun (_, id) -> m#ident id) l + | Default import_default -> m#ident import_default + | SideEffect -> () + + method export e = + match e with + | ExportVar (k, l) -> m#statement (Variable_statement (k, l)) + | ExportFun (id, f) -> m#statement (Function_declaration (id, f)) + | ExportClass (id, f) -> m#statement (Class_declaration (id, f)) + | ExportNames l -> List.iter ~f:(fun (id, _) -> m#ident id) l + | ExportDefaultFun (id, decl) -> m#statement (Function_declaration (id, decl)) + | ExportDefaultClass (id, decl) -> m#statement (Class_declaration (id, decl)) + | ExportDefaultExpression e -> m#expression e + | ExportFrom { from = _; kind = _ } -> () + | CoverExportFrom e -> m#early_error e method statement_o x = match x with @@ -968,6 +1042,17 @@ class free = | Some f -> Some (m#block f) in Try_statement (b, w, f) + | Import ({ from = _; kind }, _) -> + (match kind with + | Namespace (iopt, i) -> + Option.iter ~f:m#def_local iopt; + m#def_local i + | Named (iopt, l) -> + Option.iter ~f:m#def_local iopt; + List.iter ~f:(fun (_, id) -> m#def_local id) l + | Default import_default -> m#def_local import_default + | SideEffect -> ()); + super#statement x | _ -> super#statement x method for_binding k x = @@ -985,10 +1070,11 @@ class free = end type scope = + | Module | Lexical_block | Fun_block of ident option -class rename_variable = +class rename_variable ~esm = let declared scope params body = let declared_names = ref StringSet.empty in let decl_var x = @@ -997,6 +1083,7 @@ class rename_variable = | _ -> () in (match scope with + | Module -> () | Lexical_block -> () | Fun_block None -> () | Fun_block (Some x) -> decl_var x); @@ -1014,13 +1101,14 @@ class rename_variable = method statement x = match scope, x with - | Fun_block _, Function_declaration (id, fd) -> + | (Fun_block _ | Module), Function_declaration (id, fd) -> decl_var id; self#fun_decl fd | Lexical_block, Function_declaration (_, fd) -> self#fun_decl fd - | (Lexical_block | Fun_block _), Class_declaration (id, cl_decl) -> + | (Fun_block _ | Module), Class_declaration (id, cl_decl) -> decl_var id; self#class_decl cl_decl + | Lexical_block, Class_declaration (_, cl_decl) -> self#class_decl cl_decl | _, For_statement (Right (((Const | Let) as k), l), _e1, _e2, (st, _loc)) -> let m = {} in List.iter ~f:(m#variable_declaration k) l; @@ -1038,13 +1126,35 @@ class rename_variable = List.iter l ~f:(fun (_, s) -> m#statements s); Option.iter def ~f:(fun l -> m#statements l); List.iter l' ~f:(fun (_, s) -> m#statements s) - | (Fun_block _ | Lexical_block), _ -> super#statement x + | _, Import ({ kind; from = _ }, _loc) -> ( + match kind with + | Namespace (iopt, i) -> + Option.iter ~f:decl_var iopt; + decl_var i + | Named (iopt, l) -> + Option.iter ~f:decl_var iopt; + List.iter ~f:(fun (_, id) -> decl_var id) l + | Default import_default -> decl_var import_default + | SideEffect -> ()) + | (Fun_block _ | Lexical_block | Module), _ -> super#statement x + + method export e = + match e with + | ExportVar (_k, _l) -> () + | ExportFun (_id, _f) -> () + | ExportClass (_id, _f) -> () + | ExportNames l -> List.iter ~f:(fun (id, _) -> self#ident id) l + | ExportDefaultFun (id, decl) -> self#statement (Function_declaration (id, decl)) + | ExportDefaultClass (id, decl) -> self#statement (Class_declaration (id, decl)) + | ExportDefaultExpression e -> self#expression e + | ExportFrom { from = _; kind = _ } -> () + | CoverExportFrom _ -> () method variable_declaration k l = if match scope, k with - | (Lexical_block | Fun_block _), (Let | Const) -> depth = 0 + | (Lexical_block | Fun_block _ | Module), (Let | Const) -> depth = 0 | Lexical_block, Var -> false - | Fun_block _, Var -> true + | (Fun_block _ | Module), Var -> true then let ids = bound_idents_of_variable_declaration l in List.iter ids ~f:decl_var @@ -1055,9 +1165,9 @@ class rename_variable = method for_binding k p = if match scope, k with - | (Lexical_block | Fun_block _), (Let | Const) -> depth = 0 + | (Lexical_block | Fun_block _ | Module), (Let | Const) -> depth = 0 | Lexical_block, Var -> false - | Fun_block _, Var -> true + | (Fun_block _ | Module), Var -> true then match p with | BindingIdent i -> decl_var i @@ -1105,8 +1215,13 @@ class rename_variable = k, m'#formal_parameter_list params, m'#function_body body, m#loc nid method program p = - let m' = m#update_state Lexical_block [] p in - m'#statements p + if esm + then + let m' = m#update_state Module [] p in + m'#statements p + else + let m' = m#update_state Lexical_block [] p in + m'#statements p method expression e = match e with diff --git a/compiler/lib/js_traverse.mli b/compiler/lib/js_traverse.mli index f931214e16..062402eced 100644 --- a/compiler/lib/js_traverse.mli +++ b/compiler/lib/js_traverse.mli @@ -64,6 +64,10 @@ class type mapper = object method program : program -> program method function_body : statement_list -> statement_list + + method import : import -> import + + method export : export -> export end class type iterator = object @@ -102,6 +106,10 @@ class type iterator = object method program : Javascript.program -> unit method function_body : Javascript.statement_list -> unit + + method import : import -> unit + + method export : export -> unit end class map : mapper @@ -152,10 +160,11 @@ end class free : freevar type scope = + | Module | Lexical_block | Fun_block of ident option -class rename_variable : object ('a) +class rename_variable : esm:bool -> object ('a) inherit mapper method update_state : scope -> Javascript.ident list -> Javascript.statement_list -> 'a diff --git a/compiler/lib/parse_js.ml b/compiler/lib/parse_js.ml index 3a9edff265..5dcbfb3471 100644 --- a/compiler/lib/parse_js.ml +++ b/compiler/lib/parse_js.ml @@ -511,10 +511,26 @@ let parse_aux the_parser (lexbuf : Lexer.t) = raise (Parsing_error (Parse_info.t_of_pos p)) let fail_early = - object - inherit Js_traverse.iter + object (m) + inherit Js_traverse.iter as super method early_error p = raise (Parsing_error p.loc) + + method statement s = + match s with + | Import (_, loc) -> raise (Parsing_error loc) + | Export (_, loc) -> raise (Parsing_error loc) + | _ -> super#statement s + + method program p = + List.iter p ~f:(fun ((p : Javascript.statement), _loc) -> + match p with + | Import _ -> super#statement p + | Export (e, _) -> ( + match e with + | CoverExportFrom e -> m#early_error e + | _ -> super#statement p) + | _ -> super#statement p) end let check_program p = List.iter p ~f:(function _, p -> fail_early#program [ p ]) diff --git a/compiler/lib/stdlib.ml b/compiler/lib/stdlib.ml index d81187b8a1..f68e8cdb53 100644 --- a/compiler/lib/stdlib.ml +++ b/compiler/lib/stdlib.ml @@ -1028,6 +1028,8 @@ module Utf8_string : sig val of_string_exn : string -> t val compare : t -> t -> int + + val equal : t -> t -> bool end = struct type t = Utf8 of string [@@ocaml.unboxed] @@ -1037,6 +1039,8 @@ end = struct else invalid_arg "Utf8_string.of_string: invalid utf8 string" let compare (Utf8 x) (Utf8 y) = String.compare x y + + let equal (Utf8 x) (Utf8 y) = String.equal x y end module Int = struct diff --git a/compiler/lib/wasm/wa_link.ml b/compiler/lib/wasm/wa_link.ml index 0c4efbc9ce..f778f9b564 100644 --- a/compiler/lib/wasm/wa_link.ml +++ b/compiler/lib/wasm/wa_link.ml @@ -315,7 +315,7 @@ let output_js js = | S { name = Utf8 x; _ } -> Var_printer.add_reserved x) free; let js = - if Config.Flag.shortvar () then (new Js_traverse.rename_variable)#program js else js + if Config.Flag.shortvar () then (new Js_traverse.rename_variable ~esm:false)#program js else js in let js = (new Js_traverse.simpl)#program js in let js = (new Js_traverse.clean)#program js in