1-module(elixir_aliases). 2-export([inspect/1, last/1, concat/1, safe_concat/1, format_error/1, 3 ensure_loaded/3, expand/2, store/5]). 4-include("elixir.hrl"). 5 6inspect(Atom) when is_atom(Atom) -> 7 case elixir_config:get(bootstrap) of 8 true -> atom_to_binary(Atom, utf8); 9 false -> 'Elixir.Code.Identifier':inspect_as_atom(Atom) 10 end. 11 12%% Store an alias in the given scope 13store(Meta, New, New, _TOpts, #{aliases := Aliases, macro_aliases := MacroAliases}) -> 14 {remove_alias(New, Aliases), remove_macro_alias(Meta, New, MacroAliases)}; 15store(Meta, New, Old, TOpts, #{aliases := Aliases, macro_aliases := MacroAliases} = E) -> 16 elixir_env:trace({alias, Meta, Old, New, TOpts}, E), 17 {store_alias(New, Old, Aliases), store_macro_alias(Meta, New, Old, MacroAliases)}. 18 19store_alias(New, Old, Aliases) -> 20 lists:keystore(New, 1, Aliases, {New, Old}). 21 22store_macro_alias(Meta, New, Old, Aliases) -> 23 case lists:keyfind(counter, 1, Meta) of 24 {counter, Counter} -> 25 lists:keystore(New, 1, Aliases, {New, {Counter, Old}}); 26 false -> 27 Aliases 28 end. 29 30remove_alias(Atom, Aliases) -> 31 lists:keydelete(Atom, 1, Aliases). 32 33remove_macro_alias(Meta, Atom, Aliases) -> 34 case lists:keyfind(counter, 1, Meta) of 35 {counter, _Counter} -> 36 lists:keydelete(Atom, 1, Aliases); 37 false -> 38 Aliases 39 end. 40 41%% Expand an alias. It returns an atom (meaning that there 42%% was an expansion) or a list of atoms. 43 44expand({'__aliases__', _Meta, ['Elixir' | _] = List}, _E) -> 45 concat(List); 46 47expand({'__aliases__', Meta, _} = Alias, #{aliases := Aliases, macro_aliases := MacroAliases} = E) -> 48 case lists:keyfind(alias, 1, Meta) of 49 {alias, false} -> 50 expand(Alias, MacroAliases, E); 51 {alias, Atom} when is_atom(Atom) -> 52 Atom; 53 false -> 54 expand(Alias, Aliases, E) 55 end. 56 57expand({'__aliases__', Meta, [H | T]}, Aliases, E) when is_atom(H) -> 58 Lookup = list_to_atom("Elixir." ++ atom_to_list(H)), 59 Counter = case lists:keyfind(counter, 1, Meta) of 60 {counter, C} -> C; 61 _ -> nil 62 end, 63 case lookup(Lookup, Aliases, Counter) of 64 Lookup -> [H | T]; 65 Atom -> 66 elixir_env:trace({alias_expansion, Meta, Lookup, Atom}, E), 67 case T of 68 [] -> Atom; 69 _ -> concat([Atom | T]) 70 end 71 end; 72 73expand({'__aliases__', _Meta, List}, _Aliases, _E) -> 74 List. 75 76%% Ensure a module is loaded before its usage. 77 78ensure_loaded(_Meta, 'Elixir.Kernel', _E) -> ok; 79ensure_loaded(Meta, Module, E) -> 80 case code:ensure_loaded(Module) of 81 {module, Module} -> 82 ok; 83 84 _ -> 85 case wait_for_module(Module) of 86 found -> 87 ok; 88 89 Wait -> 90 Kind = case lists:member(Module, ?key(E, context_modules)) of 91 true -> 92 case ?key(E, module) of 93 Module -> circular_module; 94 _ -> scheduled_module 95 end; 96 false when Wait == deadlock -> 97 deadlock_module; 98 false -> 99 unloaded_module 100 end, 101 102 elixir_errors:form_error(Meta, E, ?MODULE, {Kind, Module}) 103 end 104 end. 105 106wait_for_module(Module) -> 107 case is_pid(erlang:get(elixir_compiler_pid)) of 108 true -> 'Elixir.Kernel.ErrorHandler':ensure_compiled(Module, module, hard); 109 false -> not_found 110 end. 111 112%% Receives an atom and returns the last bit as an alias. 113 114last(Atom) -> 115 Last = last(lists:reverse(atom_to_list(Atom)), []), 116 list_to_atom("Elixir." ++ Last). 117 118last([$. | _], Acc) -> Acc; 119last([H | T], Acc) -> last(T, [H | Acc]); 120last([], Acc) -> Acc. 121 122%% Receives a list of atoms, binaries or lists 123%% representing modules and concatenates them. 124 125concat(Args) -> binary_to_atom(do_concat(Args), utf8). 126safe_concat(Args) -> binary_to_existing_atom(do_concat(Args), utf8). 127 128do_concat([H | T]) when is_atom(H), H /= nil -> 129 do_concat([atom_to_binary(H, utf8) | T]); 130do_concat([<<"Elixir.", _/binary>>=H | T]) -> 131 do_concat(T, H); 132do_concat([<<"Elixir">>=H | T]) -> 133 do_concat(T, H); 134do_concat(T) -> 135 do_concat(T, <<"Elixir">>). 136 137do_concat([nil | T], Acc) -> 138 do_concat(T, Acc); 139do_concat([H | T], Acc) when is_atom(H) -> 140 do_concat(T, <<Acc/binary, $., (to_partial(atom_to_binary(H, utf8)))/binary>>); 141do_concat([H | T], Acc) when is_binary(H) -> 142 do_concat(T, <<Acc/binary, $., (to_partial(H))/binary>>); 143do_concat([], Acc) -> 144 Acc. 145 146to_partial(<<"Elixir.", Arg/binary>>) -> Arg; 147to_partial(<<".", Arg/binary>>) -> Arg; 148to_partial(Arg) when is_binary(Arg) -> Arg. 149 150%% Lookup an alias in the current scope. 151 152lookup(Else, Dict, Counter) -> 153 case lists:keyfind(Else, 1, Dict) of 154 {Else, {Counter, Value}} -> lookup(Value, Dict, Counter); 155 {Else, Value} when is_atom(Value) -> lookup(Value, Dict, Counter); 156 _ -> Else 157 end. 158 159%% Errors 160 161format_error({unloaded_module, Module}) -> 162 io_lib:format("module ~ts is not loaded and could not be found", [inspect(Module)]); 163 164format_error({deadlock_module, Module}) -> 165 io_lib:format("module ~ts is not loaded and could not be found. " 166 "This may be happening because the module you are trying to load " 167 "directly or indirectly depends on the current module", 168 [inspect(Module)]); 169 170format_error({scheduled_module, Module}) -> 171 io_lib:format( 172 "module ~ts is not loaded but was defined. This happens when you depend on " 173 "a module in the same context in which it is defined. For example:\n" 174 "\n" 175 " defmodule MyApp do\n" 176 " defmodule Mod do\n" 177 " end\n" 178 "\n" 179 " use Mod\n" 180 " end\n" 181 "\n" 182 "Try defining the module outside the context that uses it:\n" 183 "\n" 184 " defmodule MyApp.Mod do\n" 185 " end\n" 186 "\n" 187 " defmodule MyApp do\n" 188 " use MyApp.Mod\n" 189 " end\n" 190 "\n" 191 "If the module is defined at the top-level and you are trying to " 192 "use it at the top-level, this is not supported by Elixir", 193 [inspect(Module)]); 194 195format_error({circular_module, Module}) -> 196 io_lib:format( 197 "you are trying to use the module ~ts which is currently being defined.\n" 198 "\n" 199 "This may happen if you accidentally override the module you want to use. For example:\n" 200 "\n" 201 " defmodule MyApp do\n" 202 " defmodule Supervisor do\n" 203 " use Supervisor\n" 204 " end\n" 205 " end\n" 206 "\n" 207 "In the example above, the new Supervisor conflicts with Elixir's Supervisor. " 208 "This may be fixed by using the fully qualified name in the definition:\n" 209 "\n" 210 " defmodule MyApp.Supervisor do\n" 211 " use Supervisor\n" 212 " end\n", 213 [inspect(Module)]). 214