Skip to content

[WIP] - Preserve JSX by adding extra app flag #7387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion analysis/src/CompletionFrontEnd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,14 @@ let rec exprToContextPathInner ~(inJsxContext : bool) (e : Parsetree.expression)
};
args =
[(_, lhs); (_, {pexp_desc = Pexp_apply {funct = d; args; partial}})];
transformed_jsx;
} ->
(* Transform away pipe with apply call *)
exprToContextPath ~inJsxContext
{
pexp_desc =
Pexp_apply {funct = d; args = (Nolabel, lhs) :: args; partial};
Pexp_apply
{funct = d; args = (Nolabel, lhs) :: args; partial; transformed_jsx};
pexp_loc;
pexp_attributes;
}
Expand All @@ -284,6 +286,7 @@ let rec exprToContextPathInner ~(inJsxContext : bool) (e : Parsetree.expression)
(_, lhs); (_, {pexp_desc = Pexp_ident id; pexp_loc; pexp_attributes});
];
partial;
transformed_jsx;
} ->
(* Transform away pipe with identifier *)
exprToContextPath ~inJsxContext
Expand All @@ -294,6 +297,7 @@ let rec exprToContextPathInner ~(inJsxContext : bool) (e : Parsetree.expression)
funct = {pexp_desc = Pexp_ident id; pexp_loc; pexp_attributes};
args = [(Nolabel, lhs)];
partial;
transformed_jsx;
};
pexp_loc;
pexp_attributes;
Expand Down
10 changes: 6 additions & 4 deletions compiler/core/js_call_info.ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ type call_info =
{[ fun x y -> (f x y) === f ]} when [f] is an atom
*)

type t = {call_info: call_info; arity: arity}
type t = {call_info: call_info; arity: arity; call_transformed_jsx: bool}

let dummy = {arity = NA; call_info = Call_na}
let dummy = {arity = NA; call_info = Call_na; call_transformed_jsx = false}

let builtin_runtime_call = {arity = Full; call_info = Call_builtin_runtime}
let builtin_runtime_call =
{arity = Full; call_info = Call_builtin_runtime; call_transformed_jsx = false}

let ml_full_call = {arity = Full; call_info = Call_ml}
let ml_full_call =
{arity = Full; call_info = Call_ml; call_transformed_jsx = false}
2 changes: 1 addition & 1 deletion compiler/core/js_call_info.mli
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type call_info =
{[ fun x y -> f x y === f ]} when [f] is an atom
*)

type t = {call_info: call_info; arity: arity}
type t = {call_info: call_info; arity: arity; call_transformed_jsx: bool}

val dummy : t

Expand Down
92 changes: 92 additions & 0 deletions compiler/core/js_dump.ml
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,45 @@ and expression_desc cxt ~(level : int) f x : cxt =
when Ext_list.length_equal el i
]}
*)
| Call (e, el, {call_transformed_jsx = true}) -> (
match el with
| [
tag;
{
expression_desc =
Caml_block (el, _mutable_flag, _, Lambda.Blk_record {fields});
};
] ->
let fields =
Ext_list.array_list_filter_map fields el (fun (f, opt) x ->
match x.expression_desc with
| Undefined _ when opt -> None
| _ -> Some (f, x))
in
print_jsx cxt ~level f tag fields
| [
tag;
{
expression_desc =
Caml_block (el, _mutable_flag, _, Lambda.Blk_record {fields});
};
key;
] ->
let fields =
Ext_list.array_list_filter_map fields el (fun (f, opt) x ->
match x.expression_desc with
| Undefined _ when opt -> None
| _ -> Some (f, x))
in
let fields = ("key", key) :: fields in
print_jsx cxt ~level f tag fields
| _ ->
expression_desc cxt ~level f
(Call
( e,
el,
{call_transformed_jsx = false; arity = Full; call_info = Call_ml}
)))
| Call (e, el, info) ->
P.cond_paren_group f (level > 15) (fun _ ->
P.group f 0 (fun _ ->
Expand Down Expand Up @@ -681,6 +720,7 @@ and expression_desc cxt ~(level : int) f x : cxt =
P.cond_paren_group f (level > 12) (fun _ ->
let cxt = expression ~level:0 cxt f prop in
P.string f " in ";
P.string f " in ";
expression ~level:0 cxt f obj)
| Typeof e ->
P.string f "typeof";
Expand Down Expand Up @@ -956,6 +996,58 @@ and expression_desc cxt ~(level : int) f x : cxt =
P.string f "...";
expression ~level:13 cxt f e)

and print_jsx cxt ~(level : int) f (tag : J.expression)
(fields : (string * J.expression) list) : cxt =
let print_tag () =
match tag.expression_desc with
| J.Str {txt} -> P.string f txt
(* fragment *)
| J.Var (J.Qualified ({id = {name = "JsxRuntime"}}, Some "Fragment")) -> ()
| _ ->
let _ = expression ~level cxt f tag in
()
in
let children_opt =
List.find_map (fun (n, e) -> if n = "children" then Some e else None) fields
in
let print_props () =
let props = List.filter (fun (n, _) -> n <> "children") fields in
if not (List.is_empty props) then
(List.iter (fun (n, x) ->
P.space f;
P.string f n;
P.string f "=";
P.string f "{";
let _ = expression ~level:0 cxt f x in
P.string f "}"))
props
in
(match children_opt with
| None ->
P.string f "<";
print_tag ();
print_props ();
P.string f "/>"
| Some children ->
let child_is_jsx =
match children.expression_desc with
| J.Call (_, _, {call_transformed_jsx = is_jsx}) -> is_jsx
| _ -> false
in

P.string f "<";
print_tag ();
print_props ();
P.string f ">";
if not child_is_jsx then P.string f "{";
let _ = expression ~level cxt f children in
if not child_is_jsx then P.string f "}";
P.string f "</";
print_tag ();
P.string f ">");

cxt

and property_name_and_value_list cxt f (l : J.property_map) =
iter_lst cxt f l
(fun cxt f (pn, e) ->
Expand Down
47 changes: 47 additions & 0 deletions compiler/core/jsx_help.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
let lambda_tag_info_to_string (e : Lambda.tag_info) =
match e with
| Lambda.Blk_constructor _ -> "Blk_constructor"
| Lambda.Blk_record_inlined _ -> "Blk_record_inlined"
| Lambda.Blk_tuple -> "Blk_tuple"
| Lambda.Blk_poly_var _ -> "Blk_poly_var"
| Lambda.Blk_record _ -> "Blk_record"
| Lambda.Blk_module _ -> "Blk_module"
| Lambda.Blk_module_export _ -> "Blk_module_export"
| Lambda.Blk_extension -> "Blk_extension"
| Lambda.Blk_some -> "Blk_some"
| Lambda.Blk_some_not_nested -> "Blk_some_not_nested"
| Lambda.Blk_record_ext _ -> "Blk_record_ext"
| Lambda.Blk_lazy_general -> "Blk_lazy_general"

let j_exp_to_string (e : J.expression) =
match e.J.expression_desc with
| J.Object _ -> "Object"
| J.Str _ -> "String"
| J.Var _ -> "Var"
| J.Call _ -> "Call"
| J.Fun _ -> "Fun"
| J.Array _ -> "Array"
| J.Bin _ -> "Bin"
| J.Cond _ -> "Cond"
| J.New _ -> "New"
| J.Seq _ -> "Seq"
| J.Number _ -> "Number"
| J.Bool _ -> "Bool"
| J.Null -> "Null"
| J.Undefined _ -> "Undefined"
| J.Is_null_or_undefined _ -> "Is_null_or_undefined"
| J.Js_not _ -> "Js_not"
| J.Typeof _ -> "Typeof"
| J.String_index _ -> "String_index"
| J.Array_index _ -> "Array_index"
| J.Static_index _ -> "Static_index"
| J.Length _ -> "Length"
| J.Caml_block (_, _, _, tag) ->
Format.sprintf "Caml_block (%s)" (lambda_tag_info_to_string tag)
| J.Caml_block_tag _ -> "Caml_block_tag"
| J.Tagged_template _ -> "Tagged_template"
| J.Optional_block _ -> "Optional_block"
| J.Spread _ -> "Spread"
| J.Await _ -> "Await"
| J.Raw_js_code _ -> "Raw_js_code"
| _ -> "Other"
37 changes: 25 additions & 12 deletions compiler/core/lam.ml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ module Types = struct
*)
and prim_info = {primitive: Lam_primitive.t; args: t list; loc: Location.t}

and apply = {ap_func: t; ap_args: t list; ap_info: ap_info}
and apply = {
ap_func: t;
ap_args: t list;
ap_info: ap_info;
ap_transformed_jsx: bool;
}

and t =
| Lvar of ident
Expand Down Expand Up @@ -121,7 +126,12 @@ module X = struct
loc: Location.t;
}

and apply = Types.apply = {ap_func: t; ap_args: t list; ap_info: ap_info}
and apply = Types.apply = {
ap_func: t;
ap_args: t list;
ap_info: ap_info;
ap_transformed_jsx: bool;
}

and lfunction = Types.lfunction = {
arity: int;
Expand Down Expand Up @@ -159,10 +169,10 @@ include Types
let inner_map (l : t) (f : t -> X.t) : X.t =
match l with
| Lvar (_ : ident) | Lconst (_ : Lam_constant.t) -> ((* Obj.magic *) l : X.t)
| Lapply {ap_func; ap_args; ap_info} ->
| Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
let ap_func = f ap_func in
let ap_args = Ext_list.map ap_args f in
Lapply {ap_func; ap_args; ap_info}
Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx}
| Lfunction {body; arity; params; attr} ->
let body = f body in
Lfunction {body; arity; params; attr}
Expand Down Expand Up @@ -279,7 +289,7 @@ let rec is_eta_conversion_exn params inner_args outer_args : t list =
| _, _, _ -> raise_notrace Not_simple_form

(** FIXME: more robust inlining check later, we should inline it before we add stub code*)
let rec apply fn args (ap_info : ap_info) : t =
let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
match fn with
| Lfunction
{
Expand All @@ -300,15 +310,16 @@ let rec apply fn args (ap_info : ap_info) : t =
Lprim
{primitive = wrap; args = [Lprim {primitive_call with args; loc}]; loc}
| exception Not_simple_form ->
Lapply {ap_func = fn; ap_args = args; ap_info})
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
| Lfunction
{
params;
body = Lprim ({primitive = _; args = inner_args} as primitive_call);
} -> (
match is_eta_conversion_exn params inner_args args with
| args -> Lprim {primitive_call with args; loc = ap_info.ap_loc}
| exception _ -> Lapply {ap_func = fn; ap_args = args; ap_info})
| exception _ ->
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
| Lfunction
{
params;
Expand All @@ -321,17 +332,17 @@ let rec apply fn args (ap_info : ap_info) : t =
| args ->
Lsequence (Lprim {primitive_call with args; loc = ap_info.ap_loc}, const)
| exception _ ->
Lapply {ap_func = fn; ap_args = args; ap_info}
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
(* | Lfunction {params;body} when Ext_list.same_length params args ->
Ext_list.fold_right2 (fun p arg acc ->
Llet(Strict,p,arg,acc)
) params args body *)
(* TODO: more rigirous analysis on [let_kind] *))
| Llet (kind, id, e, (Lfunction _ as fn)) ->
Llet (kind, id, e, apply fn args ap_info)
Llet (kind, id, e, apply fn args ap_info ~ap_transformed_jsx)
(* | Llet (kind0, id0, e0, Llet (kind,id, e, (Lfunction _ as fn))) ->
Llet(kind0,id0,e0,Llet (kind, id, e, apply fn args loc status)) *)
| _ -> Lapply {ap_func = fn; ap_args = args; ap_info}
| _ -> Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}

let rec eq_approx (l1 : t) (l2 : t) =
match l1 with
Expand Down Expand Up @@ -712,10 +723,12 @@ let result_wrap loc (result_type : External_ffi_types.return_wrapper) result =
prim ~primitive:Pundefined_to_opt ~args:[result] loc
| Return_unset | Return_identity -> result

let handle_bs_non_obj_ffi (arg_types : External_arg_spec.params)
let handle_bs_non_obj_ffi ?(transformed_jsx = false)
(arg_types : External_arg_spec.params)
(result_type : External_ffi_types.return_wrapper) ffi args loc prim_name
~dynamic_import =
result_wrap loc result_type
(prim
~primitive:(Pjs_call {prim_name; arg_types; ffi; dynamic_import})
~primitive:
(Pjs_call {prim_name; arg_types; ffi; dynamic_import; transformed_jsx})
~args loc)
10 changes: 8 additions & 2 deletions compiler/core/lam.mli
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ type lambda_switch = {
sw_names: Ast_untagged_variants.switch_names option;
}

and apply = private {ap_func: t; ap_args: t list; ap_info: ap_info}
and apply = private {
ap_func: t;
ap_args: t list;
ap_info: ap_info;
ap_transformed_jsx: bool;
}

and lfunction = {
arity: int;
Expand Down Expand Up @@ -85,6 +90,7 @@ and t = private
val inner_map : t -> (t -> t) -> t

val handle_bs_non_obj_ffi :
?transformed_jsx:bool ->
External_arg_spec.params ->
External_ffi_types.return_wrapper ->
External_ffi_types.external_spec ->
Expand All @@ -103,7 +109,7 @@ val global_module : ?dynamic_import:bool -> ident -> t

val const : Lam_constant.t -> t

val apply : t -> t list -> ap_info -> t
val apply : ?ap_transformed_jsx:bool -> t -> t list -> ap_info -> t

val function_ :
attr:Lambda.function_attribute ->
Expand Down
4 changes: 2 additions & 2 deletions compiler/core/lam_bounded_vars.ml
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ let rewrite (map : _ Hash_ident.t) (lam : Lam.t) : Lam.t =
(* here it makes sure that global vars are not rebound *)
Lam.prim ~primitive ~args:(Ext_list.map args aux) loc
| Lglobal_module _ -> lam
| Lapply {ap_func; ap_args; ap_info} ->
| Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
let fn = aux ap_func in
let args = Ext_list.map ap_args aux in
Lam.apply fn args ap_info
Lam.apply ~ap_transformed_jsx fn args ap_info
| Lswitch
( l,
{
Expand Down
Loading
Loading