Files
n8n-openai-adapter/lib/elixir/src/elixir_compiler.erl
T
José Valim 4fea9cc942 Recompile Kernel before bootstrapping stdlib (#10002)
Before this patch, we could have slightly different
AST while compiling modules in parallel, depending
if modules were recompiled or not. Now we always
recompile Kernel before compiling the whole stdlib.

We used this opportunity to simplify bootstrap by
breaking it two steps. The first step is the minimal
necessary to compile Kernel itself. The second step
contains the whole compiler.

Closes #10000.
2020-04-30 01:38:04 +02:00

194 lines
6.5 KiB
Erlang

%% Elixir compiler front-end to the Erlang backend.
-module(elixir_compiler).
-export([string/3, quoted/3, bootstrap/0,
file/2, file_to_path/3, eval_forms/3]).
-include("elixir.hrl").
string(Contents, File, Callback) ->
Forms = elixir:'string_to_quoted!'(Contents, 1, 1, File, elixir_config:get(parser_options)),
quoted(Forms, File, Callback).
quoted(Forms, File, Callback) ->
Previous = get(elixir_module_binaries),
try
put(elixir_module_binaries, []),
Env = (elixir_env:new())#{line := 1, file := File, tracers := elixir_config:get(tracers)},
elixir_lexical:run(
Env,
fun (LexicalEnv) -> eval_forms(Forms, [], LexicalEnv) end,
fun (#{lexical_tracker := Pid}) -> Callback(File, Pid) end
),
lists:reverse(get(elixir_module_binaries))
after
put(elixir_module_binaries, Previous)
end.
file(File, Callback) ->
{ok, Bin} = file:read_file(File),
string(elixir_utils:characters_to_list(Bin), File, Callback).
file_to_path(File, Dest, Callback) when is_binary(File), is_binary(Dest) ->
file(File, fun(CallbackFile, CallbackLexical) ->
_ = [binary_to_path(Mod, Dest) || Mod <- get(elixir_module_binaries)],
Callback(CallbackFile, CallbackLexical)
end).
%% Evaluates the given code through the Erlang compiler.
%% It may end-up evaluating the code if it is deemed a
%% more efficient strategy depending on the code snippet.
eval_forms(Forms, Args, E) ->
case (?key(E, module) == nil) andalso allows_fast_compilation(Forms) of
true ->
{Result, _Binding, EE} = elixir:eval_forms(Forms, [], E),
{Result, EE};
false ->
compile(Forms, Args, E)
end.
compile(Quoted, ArgsList, E) ->
Args = list_to_tuple(ArgsList),
{Expanded, EE} = elixir_expand:expand(Quoted, E),
elixir_env:check_unused_vars(EE),
{Module, Fun, Purgeable} =
elixir_erl_compiler:spawn(fun spawned_compile/2, [Expanded, E]),
{dispatch(Module, Fun, Args, Purgeable), EE}.
spawned_compile(ExExprs, #{line := Line, file := File} = E) ->
{Vars, S} = elixir_env:env_to_scope(E),
{ErlExprs, _} = elixir_erl_pass:translate(ExExprs, S),
Module = retrieve_compiler_module(),
Fun = code_fun(?key(E, module)),
Forms = code_mod(Fun, ErlExprs, Line, File, Module, Vars),
{Module, Binary} = elixir_erl_compiler:noenv_forms(Forms, File, [nowarn_nomatch]),
code:load_binary(Module, "", Binary),
{Module, Fun, is_purgeable(Module, Binary)}.
dispatch(Module, Fun, Args, Purgeable) ->
Res = Module:Fun(Args),
code:delete(Module),
Purgeable andalso code:purge(Module),
return_compiler_module(Module, Purgeable),
Res.
code_fun(nil) -> '__FILE__';
code_fun(_) -> '__MODULE__'.
code_mod(Fun, Expr, Line, File, Module, Vars) when is_binary(File), is_integer(Line) ->
Ann = erl_anno:new(Line),
Tuple = {tuple, Ann, [{var, Ann, Var} || {_, Var} <- Vars]},
Relative = elixir_utils:relative_to_cwd(File),
[{attribute, Ann, file, {elixir_utils:characters_to_list(Relative), 1}},
{attribute, Ann, module, Module},
{attribute, Ann, compile, no_auto_import},
{attribute, Ann, export, [{Fun, 1}, {'__RELATIVE__', 0}]},
{function, Ann, Fun, 1, [
{clause, Ann, [Tuple], [], [Expr]}
]},
{function, Ann, '__RELATIVE__', 0, [
{clause, Ann, [], [], [elixir_erl:elixir_to_erl(Relative)]}
]}].
retrieve_compiler_module() ->
elixir_code_server:call(retrieve_compiler_module).
return_compiler_module(Module, Purgeable) ->
elixir_code_server:cast({return_compiler_module, Module, Purgeable}).
is_purgeable(Module, Binary) ->
beam_lib:chunks(Binary, [labeled_locals]) == {ok, {Module, [{labeled_locals, []}]}}.
allows_fast_compilation({'__block__', _, Exprs}) ->
lists:all(fun allows_fast_compilation/1, Exprs);
allows_fast_compilation({defmodule, _, _}) ->
true;
allows_fast_compilation(_) ->
false.
%% Bootstrapper
bootstrap() ->
{ok, _} = application:ensure_all_started(elixir),
elixir_config:put(bootstrap, true),
elixir_config:put(docs, false),
elixir_config:put(relative_paths, false),
elixir_config:put(ignore_module_conflict, true),
elixir_config:put(tracers, []),
elixir_config:put(parser_options, []),
{Init, Main} = bootstrap_files(),
[bootstrap_file(File) || File <- [<<"lib/elixir/lib/kernel.ex">> | Init]],
elixir_config:put(bootstrap, true),
elixir_config:put(docs, true),
[bootstrap_file(File) || File <- [<<"lib/elixir/lib/kernel.ex">> | Main]].
bootstrap_file(File) ->
try
Lists = file(filename:absname(File), fun(_, _) -> ok end),
_ = [binary_to_path(X, "lib/elixir/ebin") || X <- Lists],
io:format("Compiled ~ts~n", [File])
catch
Kind:Reason:Stacktrace ->
io:format("~p: ~p~nstacktrace: ~p~n", [Kind, Reason, Stacktrace]),
erlang:halt(1)
end.
bootstrap_files() ->
{
[
<<"lib/elixir/lib/macro/env.ex">>,
<<"lib/elixir/lib/keyword.ex">>,
<<"lib/elixir/lib/module.ex">>,
<<"lib/elixir/lib/list.ex">>,
<<"lib/elixir/lib/macro.ex">>,
<<"lib/elixir/lib/kernel/typespec.ex">>,
<<"lib/elixir/lib/kernel/utils.ex">>,
<<"lib/elixir/lib/code.ex">>,
<<"lib/elixir/lib/code/identifier.ex">>,
<<"lib/elixir/lib/protocol.ex">>,
<<"lib/elixir/lib/stream/reducers.ex">>,
<<"lib/elixir/lib/enum.ex">>,
<<"lib/elixir/lib/regex.ex">>,
<<"lib/elixir/lib/inspect/algebra.ex">>,
<<"lib/elixir/lib/inspect.ex">>,
<<"lib/elixir/lib/string.ex">>,
<<"lib/elixir/lib/string/chars.ex">>
],
[
<<"lib/elixir/lib/module/checker.ex">>,
<<"lib/elixir/lib/module/locals_tracker.ex">>,
<<"lib/elixir/lib/module/parallel_checker.ex">>,
<<"lib/elixir/lib/module/types/helpers.ex">>,
<<"lib/elixir/lib/module/types/infer.ex">>,
<<"lib/elixir/lib/module/types/pattern.ex">>,
<<"lib/elixir/lib/module/types/expr.ex">>,
<<"lib/elixir/lib/module/types.ex">>,
<<"lib/elixir/lib/exception.ex">>,
<<"lib/elixir/lib/path.ex">>,
<<"lib/elixir/lib/file.ex">>,
<<"lib/elixir/lib/map.ex">>,
<<"lib/elixir/lib/range.ex">>,
<<"lib/elixir/lib/access.ex">>,
<<"lib/elixir/lib/io.ex">>,
<<"lib/elixir/lib/system.ex">>,
<<"lib/elixir/lib/kernel/cli.ex">>,
<<"lib/elixir/lib/kernel/error_handler.ex">>,
<<"lib/elixir/lib/kernel/parallel_compiler.ex">>,
<<"lib/elixir/lib/kernel/lexical_tracker.ex">>
]
}.
binary_to_path({ModuleName, _ModuleMap, Binary}, CompilePath) ->
Path = filename:join(CompilePath, atom_to_list(ModuleName) ++ ".beam"),
case file:write_file(Path, Binary) of
ok -> Path;
{error, Reason} -> error('Elixir.File.Error':exception([{action, "write to"}, {path, Path}, {reason, Reason}]))
end.