Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: ec02551759
Fetching contributors…

Cannot retrieve contributors at this time

686 lines (646 sloc) 29.723 kb
(*
Copyright © 2011 MLstate
This file is part of OPA.
OPA is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License, version 3, as published by
the Free Software Foundation.
OPA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
more details.
You should have received a copy of the GNU Affero General Public License
along with OPA. If not, see <http://www.gnu.org/licenses/>.
*)
module J = JsAst
module List = Base.List
module Format = Base.Format
module String = Base.String
(* Inlining works roughly as described in
http://research.microsoft.com/en-us/um/people/simonpj/Papers/inlining/
Note that it works only on generated code, and not on javascript in general
It cannot deal with code that is too imperative. However the generated code is not
completely functional either because the compilation of tail calls introduces some
assignments.
Due to this imperativeness, inlining is more complicated that what is described
in the paper.
*)
type occur_kind =
| NeverUsed (* in that case, the value of the binding is necessarily read
* if the var never appears and the value of its bindings is not
* read, the variable is not in the map *)
| Once of JsIdentSet.t * bool (* the var appears once after the bindings in the set
* and the value of its binding is not used
* the boolean is true if you must execute the use
* after having executed the defition
* for example, it is false in [x = f(); if bool then x],
* since f() may be a side effect, you cannot inline x
*)
| Multiple (* multiple occurrences after possibly any binding *)
(* BEWARE:
* (a=1)+a counts as two occurrences of a
* when (a=1, a) counts as one occurrence of a
* because (a=1)+a really means (a=1,a)+a
*)
let occurrence_analysis params code =
let acc = JsIdentMap.empty in (* maps identifiers to their occur kind *)
let env = JsIdentSet.empty in (* the set of parameters that have been assigned at the current point in the program *)
let safe_vars = JsIdentSet.empty in (* the set of variables that are always used when defined (if they are used)
* used to compute the value of the boolean in the Once case of occur_kind
* this env is reset when going inside a switch or an if
* It seems to show that we really lack some control flow analysis here *)
let rec aux_s tra_s tra_e _need_value (env,acc,safe_vars) stm =
match stm with
| J.Js_switch (_,e,esl,o) ->
let env, acc, safe_vars = aux_e tra_e tra_s true (env,acc,safe_vars) e in
let acc = List.fold_left
(fun acc (e,s) ->
let _env, acc, _ = aux_e tra_e tra_s true (env,acc,JsIdentSet.empty) e in
(* we can dump this env because we know that no binding occurs in the expression *)
let _, acc, _ = aux_s tra_s tra_e true (env,acc,JsIdentSet.empty) s in
acc
) acc esl in
let _, acc, _ =
match o with
| None -> env, acc, safe_vars
| Some s -> aux_s tra_s tra_e true (env,acc,JsIdentSet.empty) s in
env, acc, safe_vars
| J.Js_if (_,e,s1,o) ->
(* the case None for o is not generated by the backend but can happen
* because Imp_Cleanup generates it on cases like if () then {...} else { /* fall through */ }
* in that case, we do the same same as if the code hadn't been cleaned up *)
let env, acc, safe_vars = aux_e tra_e tra_s true (env,acc,safe_vars) e in
(* dumping the env that comes out *)
let _, acc, _ = aux_s tra_s tra_e true (env,acc, JsIdentSet.empty) s1 in
let acc =
match o with
| None -> acc
| Some s2 ->
let _, acc, _ = aux_s tra_s tra_e true (env,acc, JsIdentSet.empty) s2 in
acc in
env, acc, safe_vars
| J.Js_var (_,i,Some e) ->
assert (not (JsIdentSet.mem i params));
aux_assign tra_e tra_s false (env,acc,safe_vars) i e
| J.Js_expr (_,e) ->
aux_e tra_e tra_s false (env,acc,safe_vars) e
| J.Js_function _
| J.Js_throw _
| J.Js_trycatch _
| J.Js_with _ ->
OManager.i_error "@[<v2>Imp_inlining:@ @[<v2>unexpected construct@ %a@] in@ %a@]@\n"
JsPrint.pp#code [stm] JsPrint.pp#code code
| J.Js_var (_,_,None)
| J.Js_return _
| J.Js_continue _
| J.Js_break _
| J.Js_comment _
| J.Js_label _
| J.Js_block _
| J.Js_while _
| J.Js_dowhile _
| J.Js_for _
| J.Js_forin _ ->
tra_s true (env,acc,safe_vars) stm
and aux_assign tra_e tra_s need_value (env,acc,safe_vars) i e =
let env, acc, safe_vars = aux_e tra_e tra_s true (env,acc,safe_vars) e in
(* beware: do not count i, this is a def, not a use *)
(* beware not to put parameters in the map, we cannot inline them *)
if JsIdentSet.mem i params then
JsIdentSet.add i env, acc, safe_vars
else
env, (if need_value then JsIdentMap.add i NeverUsed acc else acc), (JsIdentSet.add i safe_vars)
and aux_e tra_e tra_s need_value (env,acc,safe_vars) expr =
match expr with
| J.Je_cond (_,e1,e2,e3) ->
(* we don't care about [env] in if then else in expression, because no tail calls appears in them *)
let env, acc, safe_vars = aux_e tra_e tra_s true (env, acc, safe_vars) e1 in
let env, acc, _ = aux_e tra_e tra_s true (env, acc, JsIdentSet.empty) e2 in
let env, acc, _ = aux_e tra_e tra_s true (env, acc, JsIdentSet.empty) e3 in
env, acc, safe_vars
| J.Je_comma (_,el,e) ->
let env, acc, safe_vars = List.fold_left (fun env_acc e -> aux_e tra_e tra_s false env_acc e) (env,acc,safe_vars) el in
let env, acc, safe_vars = aux_e tra_e tra_s need_value (env, acc, safe_vars) e in
env, acc, safe_vars
| J.Je_binop (_,J.Jb_assign,J.Je_ident (_,i),e) ->
aux_assign tra_e tra_s need_value (env,acc,safe_vars) i e
| J.Je_unop (_,op,_) when J.is_assignment_unop op -> assert false (* FIXME *)
| J.Je_binop (_,op,_,_) when J.is_assignment_binop op -> assert false
| J.Je_ident (_,i) ->
if JsIdentSet.mem i params then
(* same remark as in aux_assign *)
env, acc, safe_vars
else
let acc =
try
(match JsIdentMap.find i acc with
(* could actually compute the set of identifiers after which there are
* inline points in the multiple case too *)
| NeverUsed -> JsIdentMap.add i Multiple acc
| Once _ -> JsIdentMap.add i Multiple acc
| Multiple -> acc)
with Not_found ->
JsIdentMap.add i (Once (env, JsIdentSet.mem i safe_vars)) acc in
env, acc, safe_vars
| _ -> tra_e true (env,acc,safe_vars) expr in
let fold_stm (env,acc,safe_vars) stm =
JsWalk.TStatement.traverse_fold_context_down aux_s aux_e true (env,acc,safe_vars) stm in
let _env, acc, _ =
List.fold_left fold_stm (env,acc,safe_vars) code in
(*Printf.printf ">>>\n%!";
JsIdentMap.iter
(fun i k ->
Printf.printf "%s: " (JsIdent.to_string i);
(match k with
| Once (s,b) -> Printf.printf "Once safe:%b" b; JsIdentSet.iter (fun s -> Printf.printf " %s" (JsIdent.to_string s)) s
| Multiple -> Printf.printf "Multiple"
| NeverUsed -> Printf.printf "NeverUsed");
Printf.printf "\n%!"
) acc;*)
acc
let contains_vars params e =
JsWalk.Expr.exists
(function
| J.Je_ident (_,i) -> JsIdentSet.mem i params
| _ -> false)
e
let rec object_depth = function
| J.Je_object (_, fields) -> 1 + (List.fold_left (fun m (_,e) -> max (object_depth e) m ) 0 fields)
| _ -> 0
let local_inlining_maximal_object_depth = 5
let local_inlining_policy = function
| J.Je_ident _
| J.Je_num _
| J.Je_bool _
| J.Je_null _
| J.Je_undefined _ (* beware could be redefined *)
| J.Je_this _ (* beware, do not inline that inside a local function! *)
-> `always
(* we don't want to merge objects that have been carefully splitted in many pieces on purpose *)
| J.Je_object _ as obj when object_depth obj > local_inlining_maximal_object_depth ->
`never
(* beware not to inline side effects, even once
* you can reorder them by doing so *)
| _e ->
(* we must check later whether there are side effects or not
* because we can potentially inline an expression that does side effect
* into one that didn't *)
`once
type inline_kind =
| Safe of J.expr (* you can inline this binding *)
| Unsafe of J.expr (* you must check at the inline point if there was
* an assignment that would make inlining invalid *)
let simplify occur_env params code =
let env = JsIdentSet.empty in (* same as in occurrence_analysis *)
let acc = JsIdentMap.empty in (* maps identifiers to be inlined to their inline_kind *)
let weak_acc = JsIdentMap.empty in (* the set of identifiers to be inlined if no side effect happens
* between the def and the use *)
let set_to_clean_up = ref JsIdentSet.empty in (* the binding of these identifiers and its expression should be removed *)
let rec aux_s =
fun tra_s tra_e (env,acc,weak_acc) stm ->
match stm with
(* FIXME: factorize this fake control flow computation of whatever it is
* with the one in the occurrence analyser *)
| J.Js_if (label,e,s1,o) ->
let (env, acc,weak_acc), e = aux_e tra_e tra_s (env,acc,weak_acc) e in
let (_env, acc,_), s1 = aux_s tra_s tra_e (env, acc, weak_acc) s1 in
let acc, o =
match o with
| None -> acc, None
| Some s2 ->
let (_env, acc,_), s2 = aux_s tra_s tra_e (env, acc, weak_acc) s2 in
acc, Some s2 in
(env, acc,weak_acc), J.Js_if (label,e,s1,o)
| J.Js_switch (label,e,esl,o) ->
let (env,acc,weak_acc), e = aux_e tra_e tra_s (env,acc,weak_acc) e in
let acc, (esl:(J.expr * J.statement) list) =
List.fold_left_map
(fun acc (e,s) ->
let (_env, acc, _), e = aux_e tra_e tra_s (env,acc,weak_acc) e in
let (_env, acc, _), s = aux_s tra_s tra_e (env,acc,weak_acc) s in
acc, (e, s)
) acc (esl:(J.expr * J.statement) list) in
let acc, o =
match o with
| None -> acc, None
| Some s ->
let (_env, acc, _), s = aux_s tra_s tra_e (env, acc, weak_acc) s in
acc, Some s in
(env, acc, weak_acc), J.Js_switch (label,e,esl,o)
| J.Js_var (label,i,o) -> (
assert (not (JsIdentSet.mem i params));
try
let kind = JsIdentMap.find i occur_env in
if kind = NeverUsed then
(env, acc, weak_acc), J.Js_block (label,[])
else (
match o with
| None -> (env, acc, weak_acc), stm (* we don't know yet if this variable is needed *)
| Some e ->
let (env,acc,weak_acc), decision = aux_binding tra_e tra_s (env,acc,weak_acc) kind i e in
match decision with
| `keep_binding e -> (env,acc,weak_acc), J.Js_var (label, i, Some e)
| `delete_binding -> (env,acc,weak_acc), J.Js_block (label,[])
)
with Not_found ->
(* local variable not in the map -> never used -> delete it *)
match o with
| None -> (env,acc,weak_acc), J.Js_block (label,[])
| Some e ->
let (env,acc,weak_acc), e = aux_e tra_e tra_s (env,acc,weak_acc) e in
(env,acc,weak_acc), J.Js_expr (label,e)
)
| J.Js_function _ -> assert false
| _ -> tra_s (env,acc,weak_acc) stm
and aux_binding =
fun tra_e tra_s (env,acc,weak_acc) kind i e ->
(* inline in the body *)
let (env, acc, weak_acc), e = aux_e tra_e tra_s (env,acc,weak_acc) e in
let policy = local_inlining_policy e in
match policy, kind with
| _, NeverUsed -> assert false
| `never, _
| `once, Multiple -> (env, acc, weak_acc), `keep_binding e
| (`once | `always), Once (set,safe) ->
let has_side_effects = Imp_Common.does_side_effects e in
if contains_vars set e || not safe && has_side_effects then
(* cannot inline, because we are in the situation of:
* [y = x, x = 2, _ = y] where we cannot inline y
* because x doesn't mean the same at the definition of y
* and at the use of y
* or [x = f(); if bool then x] because inlining may cause
* the side effect of x not to be executed *)
(env, acc, weak_acc), `keep_binding e
else
if has_side_effects then
(* we put the binding in the environment but we don't
* remove the binding because perhaps we won't able to
* inline after all
* if we do, then acc will be updated to make sure the
* binding is removed after all *)
let weak_acc = JsIdentMap.add i e weak_acc in
(env, acc, weak_acc), `keep_binding e
else
(* inline is safe, go for it and delete the binding *)
let acc = JsIdentMap.add i (Safe e) acc in
(env, acc, weak_acc), `delete_binding
| `always, Multiple ->
(* we put the binding in the environment but we don't
* remove the binding as we don't know if we will be able
* to remove all the uses *)
let acc = JsIdentMap.add i (Unsafe e) acc in
(env, acc, weak_acc), `keep_binding e
and aux_e =
fun tra_e tra_s (env,acc,weak_acc) expr ->
let (env, acc, weak_acc), expr =
match expr with
| J.Je_binop (label1,J.Jb_assign,J.Je_ident (label2,i),e) ->
if JsIdentSet.mem i params then
(* we know the identifier won't be rewritten anyway, so
* we can traverse without caution *)
tra_e (env,acc,weak_acc) expr
else (
try
(* beware not to rewrite the identifier *)
let kind = JsIdentMap.find i occur_env in
if kind = NeverUsed then
(* keep the expression but remove the assigment *)
aux_e tra_e tra_s (env,acc,weak_acc) e
else
let (env,acc,weak_acc), decision = aux_binding tra_e tra_s (env,acc,weak_acc) kind i e in
match decision with
| `keep_binding e -> (env,acc,weak_acc), J.Je_binop (label1,J.Jb_assign,J.Je_ident (label2,i),e)
| `delete_binding -> (env,acc,weak_acc), JsCons.Expr.string "deadcode1"
with Not_found ->
(* the identifier is not in the map means it is unused
* and the binding is unused *)
if Imp_Common.does_side_effects e then
aux_e tra_e tra_s (env, acc, weak_acc) e
else
(env, acc, weak_acc), JsCons.Expr.string "deadcode2"
)
| J.Je_ident (_,i) -> (
try
match JsIdentMap.find i acc with
| Safe e ->
(* don't go down in the expression, it was already
* rewritten before being put in the environment
* plus we know we never inline anything containing
* assigments to parameters so the env doesn't need to
* be updated by looking at the expression *)
(env,acc,weak_acc), e
| Unsafe e ->
if contains_vars env e then
(env,acc,weak_acc), expr (* cannot inline *)
else
(* don't go down either, same reason as above *)
(env,acc,weak_acc), e
with Not_found ->
try
(* same as in the case Safe in acc
* the only difference is that the weak_map
* is reset from times to times
* since we just inlined something that contains a side effect
* we must empty the weak acc *)
let e = JsIdentMap.find i weak_acc in
set_to_clean_up := JsIdentSet.add i !set_to_clean_up;
(env, acc, JsIdentMap.empty), e
with Not_found ->
(* parameter or global variable *)
(env,acc,weak_acc), expr
)
| J.Je_function _ -> assert false
| _ -> tra_e (env,acc,weak_acc) expr in
match expr with
(* put assignments also, delete, etc, same kind of stuff as in does_side_effects? ?? *)
| J.Je_call (_,_,_,false) -> (env, acc, JsIdentMap.empty), expr
| _ -> (env, acc, weak_acc), expr in
let foldmap_stm env_acc stm =
JsWalk.TStatement.traverse_foldmap aux_s aux_e env_acc stm in
let (_env,_acc,_), code = List.fold_left_map foldmap_stm (env,acc,weak_acc) code in
(* clean up *)
let set_to_clean_up = !set_to_clean_up in
let code =
List.map (fun stm ->
JsWalk.TStatement.map
(fun stm ->
match stm with
| J.Js_var (_,i,Some _) when JsIdentSet.mem i set_to_clean_up ->
JsCons.Statement.block []
| _ -> stm)
(fun expr ->
match expr with
| J.Je_binop (_,J.Jb_assign,J.Je_ident (_,i),_) when JsIdentSet.mem i set_to_clean_up ->
JsCons.Expr.string "deadcode3"
| _ -> expr)
stm
) code in
(* simplified code *)
code
let local_inline_stm stm =
let rewrite_body params body =
let params_set = JsIdentSet.from_list params in
let code = ref body in
(* FIXME: don't need to iterate 4 times all the times
* we should stop as soon as the rewriting didn't do anything *)
for _i = 1 to 4 do
let occur_env = occurrence_analysis params_set !code in
code := simplify occur_env params_set !code;
done;
!code in
JsWalk.TStatement.traverse_map
(fun tra _tra_e stm ->
match stm with
| J.Js_function (label,name,env,[J.Js_return (label2,Some (J.Je_function (label3,None,params,body)))]) ->
let body = rewrite_body (env @ params) body in
J.Js_function (label,name,env,[J.Js_return (label2,Some (J.Je_function (label3,None,params,body)))])
| J.Js_function (label,name,params,body) ->
let body = rewrite_body params body in
J.Js_function (label,name,params,body)
| _ -> tra stm
)
(fun tra _tra_s expr ->
match expr with
| J.Je_function (label,name,params,body) ->
let body = rewrite_body params body in
J.Je_function (label,name,params,body)
| _ -> tra expr
)
stm
let local_inline code =
List.map local_inline_stm code
let global_inlining_policy_for_var e =
(* since we can't know whether a global variable is used several times
* we assume global vars are always used several times
* FIXME: actually, we could when the variable is not exported of the compilation unit
* but this information is lost (for now) very early in the compilation *)
match e with
| J.Je_ident _
| J.Je_num _
| J.Je_bool _
| J.Je_null _
| J.Je_undefined _
(* beware could be redefined, assuming it isn't *)
(* beware: do not inline 'this' *)
-> true
| _ -> false
let global_inlining_policy_for_function _name params body =
(* FIXME: same here, when a function is used once, it can be inlined no matter what *)
(* we inline but we do not want to make the code bigger, and it is difficult to know
* beforehand if the inlined code will be simplified or not
* so for now, we are conservative when choosing or not to inline *)
(* BEWARE: should make sure not to put recursive functions in here
* FIXME: should be able to inline functions as:
* function(x) {
* var a;
* return x
* }
* function (x) {
* x.f()
* return void;
* }
*)
let simple_expr ?(param_only=false) = function
(* param only is some kind of attemps to avoid a blowup? *)
| J.Je_ident (_,i) when not param_only || List.mem i params -> true
| J.Je_num _
| J.Je_bool _
| J.Je_null _
| J.Je_string (_, "", _) (* FIXME: which strings are we allowed to inline
here, and in the local inlining ?*)
| J.Je_undefined _ -> true
| _ -> false in
match body with
| [J.Js_return (_,Some e)] -> (
match e with
| J.Je_unop (_,_,e1) when simple_expr e1 -> Some e
(* FIXME: do not inline operators that do assignments (or side effects like delete?) *)
| J.Je_binop (_,_,e1,e2) when simple_expr e1 && simple_expr e2 -> Some e
| J.Je_dot (_,e1,_) when simple_expr e1 -> Some e
| J.Je_call (_, e1, l, _) when simple_expr e1 && List.for_all (simple_expr ~param_only:true) l ->
Some e
| _ -> if simple_expr e then Some e else None
)
| _ -> None
(* alpha converting [vars] in [body], while returning the new names of [vars]
* (and the new body of course)*)
let refresh vars body =
let freshs = List.map (fun _ -> Imp_Env.next_param "inline") vars in
let map =
List.fold_left2
(fun map var fresh -> JsIdentMap.add var fresh map)
JsIdentMap.empty vars freshs in
let body =
JsWalk.OnlyExpr.map
(fun e ->
match e with
| J.Je_ident (label,i) -> (
try J.Je_ident (label, JsIdentMap.find i map)
with Not_found -> e
)
| J.Je_function _ -> assert false
| _ -> e
) body in
freshs, body
type env = {
functions : [`var of J.expr | `fun_ of (JsIdent.t list * J.expr) ] JsIdentMap.t;
(* maps from some global identifiers (the only that we saw fit for inlining)
* to their body *)
closures : JsIdent.t JsIdentMap.t;
(* used to map empty closures to the function they represent
* most probably useless now *)
}
(* utility to save [env] for separated compilation *)
module S =
struct
type t = env
let pass = "pass_JavascriptCompilation_imp_Inlining"
let pp_element f = function
| `var e ->
Format.fprintf f "`var %a" (JsPrint.pp#expr ~leading:true) e
| `fun_ (params,e) ->
Format.fprintf f "`fun %a -> %a"
(Format.pp_list "," (fun f i -> Format.pp_print_string f (JsIdent.to_string i))) params
(JsPrint.pp#expr ~leading:true) e
let pp_functions f m =
JsIdentMap.iter
(fun k v ->
Format.fprintf f "@[<2>%s:@ %a@]@\n" (JsIdent.to_string k) pp_element v
) m
let pp_closures f m =
JsIdentMap.iter
(fun k v ->
Format.fprintf f "@[<2>%s: %s@]@\n" (JsIdent.to_string k) (JsIdent.to_string v)
) m
let pp f env =
Format.fprintf f "@[{@\n @[<2>functions: %a@];@\n @[<2>closures: %a@]@\n}@]"
pp_functions env.functions pp_closures env.closures
end
module R =
struct
include ObjectFiles.Make(S)
let refresh_expr = JsWalk.Refresh.expr
let refresh_element = function
| `var e -> `var (refresh_expr e)
| `fun_ (params,e) -> `fun_ (params,refresh_expr e)
let load env =
fold ~deep:true (* FIXME: shouldn't be true, but the environment
* saved should have been rewritten by the inlining
* actually, if you depend on a plugin, then it won't
* be loaded if one of your deep dependency depends
* on it i think, so it also forces you go deep
*)
(fun {functions=functions1; closures=closures1} {functions=old_functions; closures=old_closures} ->
let functions1 =
JsIdentMap.fold
(fun k v env ->
let v = refresh_element v in
(* we can possibly have collisions in the map, if
* you depend on several independant packages that
* load the same plugins *)
JsIdentMap.add k v env
) old_functions functions1 in
let closures1 = JsIdentMap.merge (fun a _ -> a) closures1 old_closures in
{functions=functions1; closures=closures1}
) env
let save ~env ~loaded_env ~initial_env =
let functions_to_be_saved = JsIdentMap.diff2 env.functions loaded_env.functions initial_env.functions in
let closures_to_be_saved = JsIdentMap.diff2 env.closures loaded_env.closures initial_env.closures in
let env_to_be_saved = {functions = functions_to_be_saved; closures = closures_to_be_saved} in
save env_to_be_saved
end
let empty_env = { functions = JsIdentMap.empty; closures = JsIdentMap.empty }
let env_of_map closure_map =
let closure_map = IdentMap.fold (fun k v acc -> JsIdentMap.add (JsCons.Ident.ident k) (JsCons.Ident.ident v) acc) closure_map JsIdentMap.empty in
{ functions = JsIdentMap.empty; closures = closure_map }
(* analysis of a toplevel statement, it fills the environment *)
let global_inline_analyse_stm (env:env) stm =
JsWalk.OnlyStatement.fold
(fun env -> function
| J.Js_var (_,name, Some (J.Je_function (_, None, params, body)))
| J.Js_function (_,name,params,body) -> (
match global_inlining_policy_for_function name params body with
| None -> env
| Some e -> {env with functions = JsIdentMap.add name (`fun_ (params,e)) env.functions}
)
| J.Js_var (_,v,Some e) ->
if global_inlining_policy_for_var e then
{env with functions = JsIdentMap.add v (`var e) env.functions}
else
env
| _ -> env
) env stm
let global_inline_analyse_code env code =
List.fold_left global_inline_analyse_stm env code
(* rewriting of a toplevel statement, given an inlining environment *)
let global_inline_rewrite_stm (env:env) (stm:JsAst.statement) : JsAst.statement =
let make_var_decl local_vars =
List.map (fun i -> JsCons.Statement.var i ?expr:None) local_vars in
let rewrite_expr_aux =
(fun self tra self_stm _tra_stm toplevel local_vars e ->
match e with
| J.Je_ident (_,i) -> (
try
match JsIdentMap.find i env.functions with
| `var e -> self toplevel local_vars e
| `fun_ _ -> tra toplevel local_vars e
with Not_found -> tra toplevel local_vars e
)
| J.Je_call (label,J.Je_ident (_,J.ExprIdent (Ident.FakeSource s)),J.Je_ident (label2,clos) :: args,pure)
when String.is_contained "clos_arg" s (* FIXME: export a function in qmlClosure that does this check instead
* (this is safe, but fragile) *)
&& JsIdentMap.mem clos env.closures ->
let e = J.Je_call (label, J.Je_ident (label2, JsIdentMap.find clos env.closures), args, pure) in
self toplevel local_vars e
| J.Je_call (_,J.Je_ident (_,i), args,_) -> (
try
let rec aux i =
match JsIdentMap.find i env.functions with
| `var (J.Je_ident (_,j)) -> aux j
| `fun_ (params,body) when List.length params = List.length args ->
(* not inlining when there are arity problems, but it could be done
* easily (right List.map2 raises an exception) *)
let params, body = refresh params body in
let assignments = List.map2 (fun l p -> JsCons.Expr.assign_ident l p) params args in
let e = JsCons.Expr.comma assignments body in
let local_vars = params @ local_vars in
self toplevel local_vars e
| `var _
| `fun_ _ ->
tra toplevel local_vars e in
aux i
with Not_found ->
tra toplevel local_vars e
)
| J.Je_function (label,name,params,body) ->
let new_local_vars, body = List.fold_left_map (self_stm false) [] body in
let body = make_var_decl new_local_vars @ body in
local_vars, J.Je_function (label, name, params, body)
| _ -> tra toplevel local_vars e
) in
let rewrite_stm_aux =
(fun self tra self_expr _tra_expr toplevel local_vars stm ->
match stm with
| J.Js_function (label,name,params,body) ->
let new_local_vars, body = List.fold_left_map (self false) [] body in
let body = make_var_decl new_local_vars @ body in
local_vars, J.Js_function (label, name, params, body)
| J.Js_var (label,i,Some e) when toplevel ->
let new_local_vars, e = self_expr false [] e in
let e = JsCons.Expr.maybe_scope new_local_vars e in
local_vars, J.Js_var (label, i, Some e)
| J.Js_return _
| J.Js_switch _
| J.Js_if _
| J.Js_throw _
| J.Js_trycatch _
| J.Js_for _
| J.Js_forin _
| J.Js_dowhile _
| J.Js_while _
| J.Js_with _ when toplevel -> assert false (* no expression at toplevel are treated *)
| _ -> tra toplevel local_vars stm) in
let local_vars = [] in
let local_vars, stm =
JsWalk.TStatement.self_traverse_foldmap_context_down rewrite_stm_aux rewrite_expr_aux true local_vars stm in
assert (local_vars = []);
stm
Jump to Line
Something went wrong with that request. Please try again.