@@ -20,7 +20,7 @@ quoted(Forms, File, Callback) ->
2020
2121 elixir_lexical :run (
2222 Env ,
23- fun (LexicalEnv ) -> maybe_fast_compile (Forms , LexicalEnv ) end ,
23+ fun (LexicalEnv ) -> optimize_defmodule (Forms , LexicalEnv ) end ,
2424 fun (#{lexical_tracker := Pid }) -> Callback (File , Pid ) end
2525 ),
2626
@@ -33,45 +33,103 @@ file(File, Callback) ->
3333 {ok , Bin } = file :read_file (File ),
3434 string (elixir_utils :characters_to_list (Bin ), File , Callback ).
3535
36- % % Evaluates the given code through the Erlang compiler.
37- % % It may end-up evaluating the code if it is deemed a
38- % % more efficient strategy depending on the code snippet.
39- maybe_fast_compile (Forms , E ) ->
40- case (? key (E , module ) == nil ) andalso allows_fast_compilation (Forms ) andalso
36+ % % In case the forms only holds defmodules, we optimize
37+ % % it by expanding them directly.
38+ optimize_defmodule (Forms , E ) ->
39+ case (? key (E , module ) == nil ) andalso only_defmodule (Forms ) andalso
4140 (not elixir_config :is_bootstrap ()) of
42- true -> fast_compile (Forms , E );
41+ true -> expand_defmodule (Forms , E );
4342 false -> compile (Forms , [], [], E )
4443 end ,
4544 ok .
4645
47- compile ( Quoted , ArgsList , _CompilerOpts , #{ line : = Line , file : = File } = E ) ->
48- Block = no_tail_optimize ([ {line , Line }], Quoted ),
49- {Expanded , SE , EE } = elixir_expand :expand (Block , elixir_env :env_to_ex (E ), E ),
46+ % % A version of compilation that uses eval (interpreted)
47+ interpret ( Quoted , ArgsList , # {line : = Line } = E ) ->
48+ {Expanded , SE , EE } = elixir_expand :expand (Quoted , elixir_env :env_to_ex (E ), E ),
5049 elixir_env :check_unused_vars (SE , EE ),
5150
5251 {Vars , TS } = elixir_erl_var :from_env (E ),
5352 {ErlExprs , _ } = elixir_erl_pass :translate (Expanded , erl_anno :new (Line ), TS ),
54- Forms = code_eval (ErlExprs , Line , File , Vars ),
5553
56- {value , Fun , _ } = erl_eval :expr (Forms , #{}),
54+ ListBinding = lists :zipwith (fun ({_ , Var }, Arg ) -> {Var , Arg } end , Vars , ArgsList ),
55+ Binding = maps :from_list (ListBinding ),
56+
57+ {value , Result , _ } = elixir :erl_eval (ErlExprs , Binding , E ),
58+ {Result , SE , EE }.
59+
60+ compile (Quoted , ArgsList , CompilerOpts , #{line := Line } = E ) ->
61+ Block = no_tail_optimize ([{line , Line }], Quoted ),
62+ {Expanded , SE , EE } = elixir_expand :expand (Block , elixir_env :env_to_ex (E ), E ),
63+ elixir_env :check_unused_vars (SE , EE ),
64+
65+ {Module , Fun , LabelledLocals } =
66+ elixir_erl_compiler :spawn (fun () -> spawned_compile (Expanded , CompilerOpts , E ) end ),
67+
5768 Args = list_to_tuple (ArgsList ),
58- {Fun ( Args ), SE , EE }.
69+ {dispatch ( Module , Fun , Args , LabelledLocals ), SE , EE }.
5970
60- code_eval (Expr , Line , File , Vars ) when is_binary (File ), is_integer (Line ) ->
71+ spawned_compile (ExExprs , CompilerOpts , #{line := Line , file := File } = E ) ->
72+ {Vars , S } = elixir_erl_var :from_env (E ),
73+ {ErlExprs , _ } = elixir_erl_pass :translate (ExExprs , erl_anno :new (Line ), S ),
74+
75+ Module = retrieve_compiler_module (),
76+ Fun = code_fun (? key (E , module )),
77+ Forms = code_mod (Fun , ErlExprs , Line , File , Module , Vars ),
78+
79+ {Module , Binary } = elixir_erl_compiler :noenv_forms (Forms , File , [nowarn_nomatch | CompilerOpts ]),
80+ code :load_binary (Module , " " , Binary ),
81+ {Module , Fun , is_purgeable (Binary )}.
82+
83+ is_purgeable (<<" FOR1" , _Size :32 , " BEAM" , Rest /binary >>) ->
84+ do_is_purgeable (Rest ).
85+
86+ do_is_purgeable (<<>>) -> true ;
87+ do_is_purgeable (<<" LocT" , 4 :32 , 0 :32 , _ /binary >>) -> true ;
88+ do_is_purgeable (<<" LocT" , _ :32 , _ /binary >>) -> false ;
89+ do_is_purgeable (<<_ :4 /binary , Size :32 , Beam /binary >>) ->
90+ <<_ :(4 * trunc ((Size + 3 ) / 4 ))/binary , Rest /binary >> = Beam ,
91+ do_is_purgeable (Rest ).
92+
93+ dispatch (Module , Fun , Args , Purgeable ) ->
94+ Res = Module :Fun (Args ),
95+ return_compiler_module (Module , Purgeable ),
96+ Res .
97+
98+ code_fun (nil ) -> '__FILE__' ;
99+ code_fun (_ ) -> '__MODULE__' .
100+
101+ code_mod (Fun , Expr , Line , File , Module , Vars ) when is_binary (File ), is_integer (Line ) ->
61102 Ann = erl_anno :new (Line ),
62103 Tuple = {tuple , Ann , [{var , Ann , Var } || {_ , Var } <- Vars ]},
63- {'fun' , Ann , {clauses , [{clause , Ann , [Tuple ], [], [Expr ]}]}}.
64-
65- allows_fast_compilation ({'__block__' , _ , Exprs }) ->
66- lists :all (fun allows_fast_compilation /1 , Exprs );
67- allows_fast_compilation ({defmodule , _ , [_ , [{do , _ }]]}) ->
104+ Relative = elixir_utils :relative_to_cwd (File ),
105+
106+ [{attribute , Ann , file , {elixir_utils :characters_to_list (Relative ), 1 }},
107+ {attribute , Ann , module , Module },
108+ {attribute , Ann , compile , no_auto_import },
109+ {attribute , Ann , export , [{Fun , 1 }, {'__RELATIVE__' , 0 }]},
110+ {function , Ann , Fun , 1 , [
111+ {clause , Ann , [Tuple ], [], [Expr ]}
112+ ]},
113+ {function , Ann , '__RELATIVE__' , 0 , [
114+ {clause , Ann , [], [], [elixir_erl :elixir_to_erl (Relative )]}
115+ ]}].
116+
117+ retrieve_compiler_module () ->
118+ elixir_code_server :call (retrieve_compiler_module ).
119+
120+ return_compiler_module (Module , Purgeable ) ->
121+ elixir_code_server :cast ({return_compiler_module , Module , Purgeable }).
122+
123+ only_defmodule ({'__block__' , _ , Exprs }) ->
124+ lists :all (fun only_defmodule /1 , Exprs );
125+ only_defmodule ({defmodule , _ , [_ , [{do , _ }]]}) ->
68126 true ;
69- allows_fast_compilation (_ ) ->
127+ only_defmodule (_ ) ->
70128 false .
71129
72- fast_compile ({'__block__' , _ , Exprs }, E ) ->
73- lists :foldl (fun (Expr , _ ) -> fast_compile (Expr , E ) end , nil , Exprs );
74- fast_compile ({defmodule , Meta , [Mod , [{do , Block }]]}, NoLineE ) ->
130+ expand_defmodule ({'__block__' , _ , Exprs }, E ) ->
131+ lists :foldl (fun (Expr , _ ) -> expand_defmodule (Expr , E ) end , nil , Exprs );
132+ expand_defmodule ({defmodule , Meta , [Mod , [{do , Block }]]}, NoLineE ) ->
75133 E = NoLineE #{line := ? line (Meta )},
76134
77135 Expanded = case Mod of
0 commit comments