Skip to content

Commit 6269264

Browse files
committed
dynamic import
1 parent 2fb38ce commit 6269264

22 files changed

+1024
-813
lines changed

jscomp/core/js_packages_info.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ let is_runtime_package (x : t) = x.name = Pkg_runtime
8484

8585
let iter (x : t) cb = Ext_list.iter x.module_systems cb
8686

87+
let map (x : t) cb = Ext_list.map x.module_systems cb
88+
8789
(* let equal (x : t) ({name; module_systems}) =
8890
x.name = name &&
8991
Ext_list.for_all2_no_exn

jscomp/core/js_packages_info.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ val same_package_by_name : t -> t -> bool
4646

4747
val iter : t -> (package_info -> unit) -> unit
4848

49+
val map : t -> (package_info -> 'a) -> 'a list
50+
4951
val empty : t
5052

5153
val from_name : string -> t

jscomp/core/lam_analysis.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ let rec no_side_effects (lam : Lam.t) : bool =
5858
| [ _; Lconst cst ] -> not_zero_constant cst
5959
| _ -> false)
6060
| Pcreate_extension _ | Pjs_typeof | Pis_null | Pis_not_none | Psome
61-
| Psome_not_nest | Pis_undefined | Pis_null_undefined | Pnull_to_opt
62-
| Pundefined_to_opt | Pnull_undefined_to_opt | Pjs_fn_make _
63-
| Pjs_object_create _
61+
| Psome_not_nest | Pis_undefined | Pis_null_undefined | Pimport
62+
| Pnull_to_opt | Pundefined_to_opt | Pnull_undefined_to_opt
63+
| Pjs_fn_make _ | Pjs_object_create _
6464
(* TODO: check *)
6565
| Pbytes_to_string | Pmakeblock _
6666
(* whether it's mutable or not *)

jscomp/core/lam_compile.ml

Lines changed: 129 additions & 126 deletions
Large diffs are not rendered by default.

jscomp/core/lam_compile.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@
2525
(** Compile single lambda IR to JS IR *)
2626

2727
val compile_recursive_lets :
28-
Lam_compile_context.t -> (Ident.t * Lam.t) list -> Js_output.t
28+
string -> Lam_compile_context.t -> (Ident.t * Lam.t) list -> Js_output.t
2929

30-
val compile_lambda : Lam_compile_context.t -> Lam.t -> Js_output.t
30+
val compile_lambda : string -> Lam_compile_context.t -> Lam.t -> Js_output.t

jscomp/core/lam_compile_main.ml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
(* module S = Js_stmt_make *)
3434

3535

36-
let compile_group (meta : Lam_stats.t)
36+
let compile_group output_prefix (meta : Lam_stats.t)
3737
(x : Lam_group.t) : Js_output.t =
3838
match x with
3939
(*
@@ -60,20 +60,20 @@ let compile_group (meta : Lam_stats.t)
6060
(* let lam = Optimizer.simplify_lets [] lam in *)
6161
(* can not apply again, it's wrong USE it with care*)
6262
(* ([Js_stmt_make.comment (Gen_of_env.query_type id env )], None) ++ *)
63-
Lam_compile.compile_lambda { continuation = Declare (kind, id);
63+
Lam_compile.compile_lambda output_prefix { continuation = Declare (kind, id);
6464
jmp_table = Lam_compile_context.empty_handler_map;
6565
meta
6666
} lam
6767

6868
| Recursive id_lams ->
69-
Lam_compile.compile_recursive_lets
69+
Lam_compile.compile_recursive_lets output_prefix
7070
{ continuation = EffectCall Not_tail;
7171
jmp_table = Lam_compile_context.empty_handler_map;
7272
meta
7373
}
7474
id_lams
7575
| Nop lam -> (* TODO: Side effect callls, log and see statistics *)
76-
Lam_compile.compile_lambda {continuation = EffectCall Not_tail;
76+
Lam_compile.compile_lambda output_prefix {continuation = EffectCall Not_tail;
7777
jmp_table = Lam_compile_context.empty_handler_map;
7878
meta
7979
} lam
@@ -222,7 +222,7 @@ let maybe_pure = no_side_effects groups in
222222
let () = Ext_log.dwarn ~__POS__ "\n@[[TIME:]Pre-compile: %f@]@." (Sys.time () *. 1000.) in
223223
#endif
224224
let body =
225-
Ext_list.map groups (fun group -> compile_group meta group)
225+
Ext_list.map groups (fun group -> compile_group output_prefix meta group)
226226
|> Js_output.concat
227227
|> Js_output.output_as_block
228228
in

jscomp/core/lam_compile_primitive.ml

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,43 @@ let ensure_value_unit (st : Lam_compile_context.continuation) e : E.t =
3636
| EffectCall Not_tail -> e
3737
(* NeedValue should return a meaningful expression*)
3838

39-
let translate loc (cxt : Lam_compile_context.t) (prim : Lam_primitive.t)
40-
(args : J.expression list) : J.expression =
39+
let module_of_expression = function
40+
| J.Var (J.Qualified (module_id, value)) -> [ (module_id, value) ]
41+
| _ -> []
42+
43+
let get_module_system () =
44+
let packages_info = Js_packages_state.get_packages_info () in
45+
let module_systems =
46+
Js_packages_info.map packages_info (fun { module_system } -> module_system)
47+
in
48+
match module_systems with
49+
(* fixme: test mode where the module system is empty *)
50+
| [] -> assert false
51+
| module_system :: _rest -> module_system
52+
53+
let import_of_path path =
54+
E.call
55+
~info:{ arity = Full; call_info = Call_na }
56+
(E.js_global "import")
57+
[ E.str path ]
58+
59+
let wrap_then import value =
60+
let arg = Ident.create "m" in
61+
E.call
62+
~info:{ arity = Full; call_info = Call_na }
63+
(E.dot import "then")
64+
[
65+
E.ocaml_fun ~return_unit:false ~async:false [ arg ]
66+
[
67+
{
68+
statement_desc = J.Return (E.dot (E.var arg) value);
69+
comment = None;
70+
};
71+
];
72+
]
73+
74+
let translate output_prefix loc (cxt : Lam_compile_context.t)
75+
(prim : Lam_primitive.t) (args : J.expression list) : J.expression =
4176
match prim with
4277
| Pis_not_none -> Js_of_lam_option.is_not_none (Ext_list.singleton_exn args)
4378
| Pcreate_extension s -> E.make_exception s
@@ -78,6 +113,27 @@ let translate loc (cxt : Lam_compile_context.t) (prim : Lam_primitive.t)
78113
| _ -> E.runtime_call Js_runtime_modules.option "nullable_to_opt" args
79114
)
80115
| _ -> assert false)
116+
| Pimport -> (
117+
match args with
118+
| [ e ] -> (
119+
let output_dir = Filename.dirname output_prefix in
120+
121+
let module_id, module_value =
122+
match module_of_expression e.expression_desc with
123+
| [ module_ ] -> module_
124+
| _ -> assert false
125+
(* TODO: graceful error message here *)
126+
in
127+
128+
let path =
129+
Js_name_of_module_id.string_of_module_id module_id ~output_dir
130+
(get_module_system ())
131+
in
132+
133+
match module_value with
134+
| Some value -> wrap_then (import_of_path path) value
135+
| None -> import_of_path path)
136+
| _ -> assert false)
81137
| Pjs_function_length -> E.function_length (Ext_list.singleton_exn args)
82138
| Pcaml_obj_length -> E.obj_length (Ext_list.singleton_exn args)
83139
| Pis_null -> E.is_null (Ext_list.singleton_exn args)
@@ -301,7 +357,8 @@ let translate loc (cxt : Lam_compile_context.t) (prim : Lam_primitive.t)
301357
| Backend_type ->
302358
E.make_block E.zero_int_literal
303359
(Blk_constructor { name = "Other"; num_nonconst = 1; tag = 0 })
304-
[ E.str "BS" ] Immutable)
360+
[ E.str "BS" ]
361+
Immutable)
305362
| Pduprecord -> Lam_dispatch_primitive.translate loc "?obj_dup" args
306363
| Plazyforce
307364
(* FIXME: we don't inline lazy force or at least

jscomp/core/lam_compile_primitive.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
*)
3030

3131
val translate :
32+
string ->
3233
Location.t ->
3334
Lam_compile_context.t ->
3435
Lam_primitive.t ->

jscomp/core/lam_convert.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ let convert (exports : Set_ident.t) (lam : Lambda.lambda) :
480480
| "#nullable_to_opt" -> Pnull_undefined_to_opt
481481
| "#null_to_opt" -> Pnull_to_opt
482482
| "#is_nullable" -> Pis_null_undefined
483+
| "#import" ->Pimport
483484
| "#string_append" -> Pstringadd
484485
| "#wrap_exn" -> Pwrap_exn
485486
| "#obj_length" -> Pcaml_obj_length

jscomp/core/lam_primitive.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ type t =
142142
| Pis_null
143143
| Pis_undefined
144144
| Pis_null_undefined
145+
| Pimport
145146
| Pjs_typeof
146147
| Pjs_function_length
147148
| Pcaml_obj_length
@@ -218,6 +219,7 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
218219
| Psome_not_nest -> rhs = Psome_not_nest
219220
| Pis_undefined -> rhs = Pis_undefined
220221
| Pis_null_undefined -> rhs = Pis_null_undefined
222+
| Pimport -> rhs = Pimport
221223
| Pjs_typeof -> rhs = Pjs_typeof
222224
| Pisint -> rhs = Pisint
223225
| Pis_poly_var_block -> rhs = Pis_poly_var_block

jscomp/core/lam_primitive.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ type t =
130130
| Pis_null
131131
| Pis_undefined
132132
| Pis_null_undefined
133+
| Pimport
133134
| Pjs_typeof
134135
| Pjs_function_length
135136
| Pcaml_obj_length

jscomp/core/lam_print.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ let primitive ppf (prim : Lam_primitive.t) =
7878
| Pval_from_option_not_nest -> fprintf ppf "[?unbox-not-nest]"
7979
| Pis_undefined -> fprintf ppf "[?undefined]"
8080
| Pis_null_undefined -> fprintf ppf "[?null?undefined]"
81+
| Pimport -> fprintf ppf "[import]"
8182
| Pmakeblock (tag, _, Immutable) -> fprintf ppf "makeblock %i" tag
8283
| Pmakeblock (tag, _, Mutable) -> fprintf ppf "makemutable %i" tag
8384
| Pfield (n, field_info) -> (

jscomp/frontend/ast_await.ml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,27 @@ let create_await_expression (e : Parsetree.expression) =
55
{ txt = Ldot (Ldot (Lident "Js", "Promise"), "unsafe_await"); loc }
66
in
77
Ast_helper.Exp.apply ~loc unsafe_await [ (Nolabel, e) ]
8+
9+
let create_await_module_expression ~module_type_name (e : Parsetree.module_expr)
10+
=
11+
let open Ast_helper in
12+
{
13+
e with
14+
pmod_desc =
15+
Pmod_unpack
16+
(create_await_expression
17+
(Exp.apply
18+
(Exp.ident ~loc:e.pmod_loc
19+
{
20+
txt = Longident.Ldot (Lident "Js", "import");
21+
loc = e.pmod_loc;
22+
})
23+
[
24+
( Nolabel,
25+
Exp.constraint_ ~loc:e.pmod_loc
26+
(Exp.pack ~loc:e.pmod_loc e)
27+
(Typ.package ~loc:e.pmod_loc
28+
{ txt = Lident module_type_name; loc = e.pmod_loc }
29+
[]) );
30+
]));
31+
}

jscomp/frontend/bs_builtin_ppx.ml

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ let expr_mapper ~async_context ~in_function_def (self : mapper)
227227
match Ast_attributes.has_await_payload e.pexp_attributes with
228228
| None -> result
229229
| Some _ ->
230-
if !async_context = false then
231-
Location.raise_errorf ~loc:e.pexp_loc
232-
"Await on expression not in an async context";
230+
(* if !async_context = false then
231+
Location.raise_errorf ~loc:e.pexp_loc
232+
"Await on expression not in an async context"; *)
233233
Ast_await.create_await_expression result
234234

235235
let typ_mapper (self : mapper) (typ : Parsetree.core_type) =
@@ -434,6 +434,13 @@ let local_module_name =
434434
incr v;
435435
"local_" ^ string_of_int !v
436436

437+
let local_module_type_name =
438+
let v = ref 0 in
439+
fun ({ txt } : Longident.t Location.loc) ->
440+
incr v;
441+
(Longident.flatten txt |> List.fold_left (fun ll l -> ll ^ l) "")
442+
^ string_of_int !v
443+
437444
let expand_reverse (stru : Ast_structure.t) (acc : Ast_structure.t) :
438445
Ast_structure.t =
439446
if stru = [] then acc
@@ -497,6 +504,37 @@ let rec structure_mapper (self : mapper) (stru : Ast_structure.t) =
497504
| _ -> expand_reverse acc (structure_mapper self rest)
498505
in
499506
aux [] stru
507+
| Pstr_module
508+
({
509+
pmb_expr =
510+
{ pmod_desc = Pmod_ident { txt; loc }; pmod_attributes } as me;
511+
} as mb)
512+
(* module M = @res.await Belt.List *)
513+
when Res_parsetree_viewer.hasAwaitAttribute pmod_attributes ->
514+
let item = self.structure_item self item in
515+
let safe_module_type_name = local_module_type_name { txt; loc } in
516+
let module_type_decl =
517+
let open Ast_helper in
518+
Str.modtype ~loc
519+
(Mtd.mk ~loc
520+
{ txt = safe_module_type_name; loc }
521+
~typ:(Mty.typeof_ ~loc me))
522+
in
523+
(* module BeltList0 = module type of Belt.List *)
524+
module_type_decl
525+
:: {
526+
item with
527+
pstr_desc =
528+
Pstr_module
529+
{
530+
mb with
531+
pmb_expr =
532+
Ast_await.create_await_module_expression
533+
~module_type_name:safe_module_type_name mb.pmb_expr;
534+
};
535+
}
536+
(* module M = @res.await Belt.List *)
537+
:: structure_mapper self rest
500538
| _ -> self.structure_item self item :: structure_mapper self rest)
501539

502540
let mapper : mapper =

jscomp/others/js.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ external toOption : 'a nullable -> 'a option = "#nullable_to_opt"
107107
external undefinedToOption : 'a undefined -> 'a option = "#undefined_to_opt"
108108
external nullToOption : 'a null -> 'a option = "#null_to_opt"
109109
external isNullable : 'a nullable -> bool = "#is_nullable"
110+
external import : 'a -> 'a promise = "#import"
110111

111112
external testAny : 'a -> bool = "#is_nullable"
112113
(** The same as {!test} except that it is more permissive on the types of input *)

jscomp/runtime/js.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ external nullToOption : 'a null -> 'a option = "#null_to_opt"
7878

7979
external isNullable : 'a nullable -> bool = "#is_nullable"
8080

81+
external import : 'a -> 'a promise = "#import"
82+
8183
(** The same as {!test} except that it is more permissive on the types of input *)
8284
external testAny : 'a -> bool = "#is_nullable"
8385

jscomp/test/Import.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
var Curry = require("../../lib/js/curry.js");
4+
5+
async function eachIntAsync(list, f) {
6+
return Curry._2(await import("../../lib/js/belt_List.js").then(function (m) {
7+
return m.forEach;
8+
}), list, f);
9+
}
10+
11+
function eachIntLazy(list, f) {
12+
var obj = import("../../lib/js/belt_List.js").then(function (m) {
13+
return m.forEach;
14+
});
15+
var arg1 = function (each) {
16+
return Promise.resolve(Curry._2(each, list, f));
17+
};
18+
return obj.then(arg1);
19+
}
20+
21+
eachIntLazy({
22+
hd: 1,
23+
tl: {
24+
hd: 2,
25+
tl: {
26+
hd: 3,
27+
tl: /* [] */0
28+
}
29+
}
30+
}, (function (n) {
31+
console.log("lazy", n);
32+
}));
33+
34+
eachIntAsync({
35+
hd: 1,
36+
tl: {
37+
hd: 2,
38+
tl: {
39+
hd: 3,
40+
tl: /* [] */0
41+
}
42+
}
43+
}, (function (n) {
44+
console.log("async", n);
45+
}));
46+
47+
var beltAsModule = import("../../lib/js/belt_List.js");
48+
49+
var M = await import("../../lib/js/belt_List.js");
50+
51+
var each = M.forEach;
52+
53+
exports.eachIntAsync = eachIntAsync;
54+
exports.eachIntLazy = eachIntLazy;
55+
exports.beltAsModule = beltAsModule;
56+
exports.M = M;
57+
exports.each = each;
58+
/* Not a pure module */

0 commit comments

Comments
 (0)