diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex index a3bef1fdf0..c9b1209056 100644 --- a/lib/elixir/lib/macro.ex +++ b/lib/elixir/lib/macro.ex @@ -620,7 +620,7 @@ defmodule Macro do # Anything else is just returned defp expand_once(other, _env, cache), do: { other, false, cache } - defp to_erl_env(env, nil), do: :elixir_scope.to_erl_env(env) + defp to_erl_env(env, nil), do: :elixir_env.ex_to_scope(env) defp to_erl_env(_env, cache), do: cache defp is_partial?(args) do diff --git a/lib/elixir/src/elixir_def.erl b/lib/elixir/src/elixir_def.erl index 45eebcf8f5..f7fd352287 100644 --- a/lib/elixir/src/elixir_def.erl +++ b/lib/elixir/src/elixir_def.erl @@ -70,7 +70,7 @@ delete_definition(Module, Tuple) -> %% before evaluating the function body), we would see both definitions. wrap_definition(Kind, Call, Expr, Context, ExEnv) -> - Env = elixir_env:from_ex(ExEnv), + Env = elixir_env:ex_to_env(ExEnv), { EscapedCall, QC } = elixir_quote:escape(Call, true), { EscapedExpr, QE } = elixir_quote:escape(Expr, true), @@ -78,14 +78,14 @@ wrap_definition(Kind, Call, Expr, Context, ExEnv) -> (not QC#elixir_quote.unquoted) andalso (not QE#elixir_quote.unquoted), - Args = [Kind, CheckClauses, EscapedCall, EscapedExpr, elixir_env:to_quote(Env)], + Args = [Kind, CheckClauses, EscapedCall, EscapedExpr, elixir_env:serialize(Env)], { { '.', [], [elixir_def, store_definition] }, [], Args }. % Invoked by the wrap definition with the function abstract tree. % Each function is then added to the function table. store_definition(Kind, CheckClauses, Call, Body, #elixir_env{line=Line} = Env) -> - S = elixir_env:to_scope(Env), + S = elixir_env:env_to_scope(Env), { NameAndArgs, Guards } = elixir_clauses:extract_guards(Call), { Name, Args } = case elixir_clauses:extract_args(NameAndArgs) of @@ -135,7 +135,7 @@ run_on_definition_callbacks(Kind, Line, Module, Name, Args, Guards, Expr, S, CO) true -> ok; _ -> - Env = elixir_scope:to_ex_env({ Line, S }), + Env = elixir_env:scope_to_ex({ Line, S }), Callbacks = 'Elixir.Module':get_attribute(Module, on_definition), [Mod:Fun(Env, Kind, Name, Args, Guards, Expr) || { Mod, Fun } <- Callbacks] end. @@ -201,7 +201,7 @@ translate_clause(Line, Kind, Unpacked, Guards, Body, S) -> true -> FBody = { 'match', Line, { 'var', Line, '__CALLER__' }, - ?wrap_call(Line, elixir_scope, to_ex_env, [{ var, Line, '_@CALLER' }]) + ?wrap_call(Line, elixir_env, scope_to_ex, [{ var, Line, '_@CALLER' }]) }, setelement(5, TClause, [FBody|element(5, TClause)]); false -> TClause diff --git a/lib/elixir/src/elixir_env.erl b/lib/elixir/src/elixir_env.erl new file mode 100644 index 0000000000..79332eb918 --- /dev/null +++ b/lib/elixir/src/elixir_env.erl @@ -0,0 +1,60 @@ +-module(elixir_env). +-include("elixir.hrl"). +-export([ex_to_env/1, env_to_scope/1, env_to_scope_with_vars/2, + ex_to_scope/1, scope_to_ex/1, serialize/1, serialize_with_vars/1]). + +%% Conversion in between #elixir_env, #elixir_scope and Macro.Env + +ex_to_env(Env) when element(1, Env) == 'Elixir.Macro.Env' -> + erlang:setelement(1, Env, elixir_env). + +env_to_scope(#elixir_env{module=Module,file=File, + function=Function,aliases=Aliases,context=Context, + requires=Requires,macros=Macros,functions=Functions, + macro_functions=MacroFunctions,macro_macros=MacroMacros, + context_modules=ContextModules,macro_aliases=MacroAliases, + lexical_tracker=Lexical}) -> + #elixir_scope{module=Module,file=File, + function=Function,aliases=Aliases,context=Context, + requires=Requires,macros=Macros,functions=Functions, + macro_functions=MacroFunctions,macro_macros=MacroMacros, + context_modules=ContextModules,macro_aliases=MacroAliases, + lexical_tracker=Lexical}. + +env_to_scope_with_vars(#elixir_env{} = Env, Vars) -> + (env_to_scope(Env))#elixir_scope{ + vars=orddict:from_list(Vars), + counter=[{'',length(Vars)}] + }. + +scope_to_ex({ Line, #elixir_scope{module=Module,file=File, + function=Function,aliases=Aliases,context=Context, + requires=Requires,macros=Macros,functions=Functions, + context_modules=ContextModules,macro_aliases=MacroAliases, + macro_functions=MacroFunctions, macro_macros=MacroMacros, + vars=Vars,lexical_tracker=Lexical} }) when is_integer(Line) -> + { 'Elixir.Macro.Env', Module, File, Line, Function, Context, Requires, Aliases, + Functions, Macros, MacroAliases, MacroFunctions, MacroMacros, ContextModules, + [Pair || { Pair, _ } <- Vars], Lexical }. + +ex_to_scope(Env) -> + env_to_scope(ex_to_env(Env)). + +%% Serialization + +serialize(#elixir_env{lexical_tracker=nil} = Env) -> + { '{}', [], tuple_to_list(Env) }; +serialize(#elixir_env{lexical_tracker=Lexical} = Env) -> + Expr = { { '.', [], [erlang, binary_to_term] }, [], [term_to_binary(Lexical)] }, + { '{}', [], tuple_to_list(Env#elixir_env{lexical_tracker=Expr}) }. + +serialize_with_vars(#elixir_env{} = Env) -> + { Vars, _ } = lists:mapfoldl(fun var_to_tuple/2, 0, Env#elixir_env.vars), + { serialize(Env), Vars }. + +var_to_tuple({ Key, Kind }, Counter) -> + Var = if is_atom(Kind) -> { Key, [], Kind }; + is_integer(Kind) -> { Key, [{ counter, Kind }], nil } + end, + Args = [Key, Kind, ?atom_concat(["_@", Counter]), Var], + { { '{}', [], Args }, Counter + 1 }. diff --git a/lib/elixir/src/elixir_fn.erl b/lib/elixir/src/elixir_fn.erl index b304005ec2..6547b0d0b9 100644 --- a/lib/elixir/src/elixir_fn.erl +++ b/lib/elixir/src/elixir_fn.erl @@ -80,7 +80,7 @@ capture_import(Meta, { Atom, ImportMeta, Args } = Expr, S, Sequential) -> end. capture_require(Meta, { { '.', _, [Left, Right] }, RequireMeta, Args } = Expr, S, Sequential) -> - { Mod, SE } = 'Elixir.Macro':expand_all(Left, elixir_scope:to_ex_env({ ?line(Meta), S }), S), + { Mod, SE } = 'Elixir.Macro':expand_all(Left, elixir_env:scope_to_ex({ ?line(Meta), S }), S), case Sequential andalso is_atom(Mod) andalso elixir_dispatch:require_function(RequireMeta, Mod, Right, length(Args), SE) of diff --git a/lib/elixir/src/elixir_literal.erl b/lib/elixir/src/elixir_literal.erl index de162b2b3d..21fbfc906b 100644 --- a/lib/elixir/src/elixir_literal.erl +++ b/lib/elixir/src/elixir_literal.erl @@ -145,7 +145,7 @@ extract_bit_values(Meta, [Size|T], default, Types, S) when is_integer(Size) anda extract_bit_values(Meta, T, {integer, Meta, Size}, Types, S); extract_bit_values(Meta, [{ '|', _, [Left, Right] }], Size, Types, S) -> - { Expanded, ES } = 'Elixir.Macro':expand_all(Right, elixir_scope:to_ex_env({ ?line(Meta), S }), S), + { Expanded, ES } = 'Elixir.Macro':expand_all(Right, elixir_env:scope_to_ex({ ?line(Meta), S }), S), extract_bit_values(Meta, [Left|join_expansion(Expanded,[])], Size, Types, ES); extract_bit_values(Meta, [_|_] = All, Size, Types, S) -> @@ -187,7 +187,7 @@ extract_bit_type_or_size(_Value, _Args, _Other, _Types) -> { error, unknown }. handle_unknown_specifier(Meta, [H|T], Size, Types, S) -> - case 'Elixir.Macro':expand_all(H, elixir_scope:to_ex_env({ ?line(Meta), S }), S) of + case 'Elixir.Macro':expand_all(H, elixir_env:scope_to_ex({ ?line(Meta), S }), S) of { H, ES } -> elixir_errors:syntax_error(Meta, ES#elixir_scope.file, "unknown bitstring specifier ~ts", ['Elixir.Macro':to_string(H)]); { E, ES } -> diff --git a/lib/elixir/src/elixir_macros.erl b/lib/elixir/src/elixir_macros.erl index 9dbd750829..0c3c31a3ed 100644 --- a/lib/elixir/src/elixir_macros.erl +++ b/lib/elixir/src/elixir_macros.erl @@ -95,7 +95,7 @@ translate({defmodule, Meta, [Ref, KV]}, S) when is_list(KV) -> { Ref, S } end, - Env = elixir_scope:to_ex_env({ ?line(Meta), FS }), + Env = elixir_env:scope_to_ex({ ?line(Meta), FS }), translate_each(elixir_module:translate(FRef, Block, Env), FS); translate({Kind, Meta, [Call]}, S) when ?defs(Kind) -> @@ -104,7 +104,7 @@ translate({Kind, Meta, [Call]}, S) when ?defs(Kind) -> translate({Kind, Meta, [Call, Expr]}, S) when ?defs(Kind) -> assert_module_scope(Meta, Kind, S), assert_no_function_scope(Meta, Kind, S), - Env = elixir_scope:to_ex_env({ ?line(Meta), S }), + Env = elixir_env:scope_to_ex({ ?line(Meta), S }), Ctx = lists:keymember(context, 1, Meta), translate_each(elixir_def:wrap_definition(Kind, Call, Expr, Ctx, Env), S); diff --git a/lib/elixir/src/elixir_module.erl b/lib/elixir/src/elixir_module.erl index 26ca93cf18..19b41565e6 100644 --- a/lib/elixir/src/elixir_module.erl +++ b/lib/elixir/src/elixir_module.erl @@ -41,7 +41,7 @@ docs_table(Module) -> %% will be passed to the invoked function. translate(Ref, Block, ExEnv) -> - Env = elixir_env:from_ex(ExEnv), + Env = elixir_env:ex_to_env(ExEnv), %% In case we are generating a module from inside a function, %% we get rid of the lexical tracker information as, at this @@ -52,7 +52,7 @@ translate(Ref, Block, ExEnv) -> end, { Escaped, _ } = elixir_quote:escape(Block, false), - { QuotedEnv, QuotedVars } = elixir_env:to_quote_with_vars(LexEnv), + { QuotedEnv, QuotedVars } = elixir_env:serialize_with_vars(LexEnv), Args = [Ref, Escaped, QuotedVars, QuotedEnv], { { '.', [], [elixir_module, compile] }, [], Args }. @@ -61,7 +61,7 @@ translate(Ref, Block, ExEnv) -> compile(Module, Block, Vars, #elixir_env{line=Line} = Env) -> Dict = [{ { Name, Kind }, Value } || { Name, Kind, Value, _ } <- Vars], - compile(Line, Module, Block, Vars, elixir_env:to_scope_with_vars(Env, Dict)). + compile(Line, Module, Block, Vars, elixir_env:env_to_scope_with_vars(Env, Dict)). compile(Line, Module, Block, Vars, #elixir_scope{context_modules=FileModules} = RawS) when is_atom(Module) -> C = elixir_compiler:get_opts(), @@ -151,7 +151,7 @@ eval_form(Line, Module, Block, Vars, S) -> KV = [{ K, V } || { _, _, K, V } <- Vars], { Value, NewS } = elixir_compiler:eval_forms([Block], Line, KV, S), elixir_def_overridable:store_pending(Module), - Env = elixir_scope:to_ex_env({ Line, S }), + Env = elixir_env:scope_to_ex({ Line, S }), eval_callbacks(Line, Module, before_compile, [Env], NewS), elixir_def_overridable:store_pending(Module), Value. @@ -278,7 +278,7 @@ compile_opts(Module) -> load_form(Line, Forms, Opts, #elixir_scope{file=File} = S) -> elixir_compiler:module(Forms, File, Opts, fun(Module, Binary) -> EvalS = scope_for_eval(Module, S), - Env = elixir_scope:to_ex_env({ Line, EvalS }), + Env = elixir_env:scope_to_ex({ Line, EvalS }), eval_callbacks(Line, Module, after_compile, [Env, Binary], EvalS), case get(elixir_compiled) of diff --git a/lib/elixir/src/elixir_scope.erl b/lib/elixir/src/elixir_scope.erl index 06a6780f43..882cd8875f 100644 --- a/lib/elixir/src/elixir_scope.erl +++ b/lib/elixir/src/elixir_scope.erl @@ -2,9 +2,6 @@ -module(elixir_scope). -export([translate_var/5, build_erl_var/2, build_ex_var/2, - serialize/1, deserialize/1, to_erl/1, - serialize_with_vars/2, deserialize_with_vars/2, - to_erl_env/1, to_ex_env/1, load_binding/2, dump_binding/2, umergev/2, umergec/2, umergea/2, merge_clause_vars/2 ]). @@ -81,84 +78,6 @@ build_ex_var(Line, Key, Name, S) when is_integer(Line) -> Var = { ?atom_concat([Name, "@", Counter]), [{line,Line}], Context }, { Var, NS }. -%% Macro.Env <-> #elixir_scope conversion - -to_erl_env({ 'Elixir.Macro.Env', Module, File, _Line, Function, Context, Requires, - Aliases, Functions, Macros, MacroAliases, MacroFunctions, MacroMacros, - ContextModules, _Vars, Lexical }) -> - #elixir_scope{module=Module,file=File, - function=Function,aliases=Aliases,context=Context, - requires=Requires,macros=Macros,functions=Functions, - macro_functions=MacroFunctions, macro_macros=MacroMacros, - context_modules=ContextModules,macro_aliases=MacroAliases, - lexical_tracker=Lexical}. - -to_ex_env({ Line, #elixir_scope{module=Module,file=File, - function=Function,aliases=Aliases,context=Context, - requires=Requires,macros=Macros,functions=Functions, - context_modules=ContextModules,macro_aliases=MacroAliases, - macro_functions=MacroFunctions, macro_macros=MacroMacros, - vars=Vars,lexical_tracker=Lexical} }) when is_integer(Line) -> - { 'Elixir.Macro.Env', Module, File, Line, Function, Context, Requires, Aliases, - Functions, Macros, MacroAliases, MacroFunctions, MacroMacros, ContextModules, - list_vars(Vars), Lexical }. - -list_vars(Vars) -> [Pair || { { _, K } = Pair, _ } <- Vars, is_atom(K)]. - -%% SERIALIZATION - -%% When serializing scopes, we support serialization of pids. -to_erl(Structure) -> - elixir_utils:elixir_to_erl(Structure, fun - (X) when is_pid(X) -> - ?wrap_call(0, erlang, binary_to_term, [elixir_utils:elixir_to_erl(term_to_binary(X))]); - (Other) -> - error({ badarg, Other }) - end). - -serialize(S) -> - to_erl({ S#elixir_scope.file, S#elixir_scope.functions, - S#elixir_scope.requires, S#elixir_scope.macros, S#elixir_scope.aliases, - S#elixir_scope.macro_functions, S#elixir_scope.macro_macros, S#elixir_scope.macro_aliases, - S#elixir_scope.context_modules, S#elixir_scope.lexical_tracker }). - -serialize_with_vars(Line, S) when is_integer(Line) -> - { Vars, _ } = orddict:fold(fun({ Key, Kind }, Value, { Acc, Counter }) -> - KindKey = if - is_atom(Kind) -> atom; - is_integer(Kind) -> integer - end, - - { { cons, Line, { tuple, Line, [ - { atom, Line, Key }, - { KindKey, Line, Kind }, - { atom, Line, ?atom_concat(["_@", Counter]) }, - { var, Line, Value } - ] }, Acc }, Counter + 1 } - end, { { nil, Line }, 0 }, S#elixir_scope.vars), - { serialize(S), Vars }. - -% Fill in the scope with the variables serialization set in serialize_scope. - -deserialize(Tuple) -> deserialize_with_vars(Tuple, []). - -deserialize_with_vars({ File, Functions, Requires, Macros, Aliases, MacroFunctions, - MacroMacros, MacroAliases, FileModules, LexicalTracker }, Vars) -> - #elixir_scope{ - file=File, - functions=Functions, - requires=Requires, - macros=Macros, - aliases=Aliases, - macro_functions=MacroFunctions, - macro_macros=MacroMacros, - macro_aliases=MacroAliases, - context_modules=FileModules, - vars=orddict:from_list(Vars), - lexical_tracker=LexicalTracker, - counter=[{'',length(Vars)}] - }. - %% SCOPE MERGING %% Receives two scopes and return a new scope based on the second diff --git a/lib/elixir/src/elixir_translator.erl b/lib/elixir/src/elixir_translator.erl index b1f5a1848a..d748d8c128 100644 --- a/lib/elixir/src/elixir_translator.erl +++ b/lib/elixir/src/elixir_translator.erl @@ -136,8 +136,8 @@ translate_each({ '__DIR__', _Meta, Atom }, S) when is_atom(Atom) -> translate_each(filename:dirname(S#elixir_scope.file), S); translate_each({ '__ENV__', Meta, Atom }, S) when is_atom(Atom) -> - Env = elixir_scope:to_ex_env({ ?line(Meta), S }), - { elixir_scope:to_erl(Env), S }; + Env = elixir_env:scope_to_ex({ ?line(Meta), S }), + { ex_env_to_erl(Env), S }; translate_each({ '__CALLER__', Meta, Atom }, S) when is_atom(Atom) -> { { var, ?line(Meta), '__CALLER__' }, S#elixir_scope{caller=true} }; @@ -461,12 +461,20 @@ var_kind(Meta, Kind) -> false -> Kind end. +ex_env_to_erl(Structure) -> + elixir_utils:elixir_to_erl(Structure, fun + (X) when is_pid(X) -> + ?wrap_call(0, erlang, binary_to_term, [elixir_utils:elixir_to_erl(term_to_binary(X))]); + (Other) -> + error({ badarg, Other }) + end). + %% Opts translate_opts(Meta, Kind, Allowed, Opts, S) -> { Expanded, TS } = case literal_opts(Opts) orelse skip_expansion(S#elixir_scope.module) of true -> { Opts, S }; - false -> 'Elixir.Macro':expand_all(Opts, elixir_scope:to_ex_env({ ?line(Meta), S }), S) + false -> 'Elixir.Macro':expand_all(Opts, elixir_env:scope_to_ex({ ?line(Meta), S }), S) end, validate_opts(Meta, Kind, Allowed, Expanded, TS), { Expanded, TS }. diff --git a/lib/elixir/src/elixir_try.erl b/lib/elixir/src/elixir_try.erl index 19fdfd795f..d0aa06652e 100644 --- a/lib/elixir/src/elixir_try.erl +++ b/lib/elixir/src/elixir_try.erl @@ -79,7 +79,7 @@ normalize_rescue(_, { in, Meta, [Left, Right] }, S) -> _ when is_list(Right) -> is_valid_rescue_list(Right, S) andalso { Left, Right }; _ -> - { Expanded, ES } = 'Elixir.Macro':expand_all(Right, elixir_scope:to_ex_env({ ?line(Meta), S }), S), + { Expanded, ES } = 'Elixir.Macro':expand_all(Right, elixir_env:scope_to_ex({ ?line(Meta), S }), S), case is_valid_rescue_list(Expanded, ES) of true -> { Left, Expanded }; false ->