Skip to content

Commit da84a79

Browse files
author
José Valim
committed
Module.DispatchTracker also records remotes
1 parent 69df6ea commit da84a79

File tree

4 files changed

+87
-15
lines changed

4 files changed

+87
-15
lines changed

lib/elixir/lib/module/dispatch_tracker.ex

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ defmodule Module.DispatchTracker do
5151
# Public API
5252

5353
@doc """
54-
Returns all the modules which were imported and
55-
made a call to.
54+
Returns all the modules which were imported.
55+
All external dependencies to a module is the sum
56+
of imports and remotes.
5657
"""
5758
def imports(pid) do
5859
d = :gen_server.call(to_pid(pid), :digraph, @timeout)
@@ -61,13 +62,32 @@ defmodule Module.DispatchTracker do
6162

6263
@doc """
6364
Returns all imported modules that had the given
64-
{ name, arity } invoked.
65+
`{ name, arity }` invoked.
6566
"""
6667
def imports_with_dispatch(pid, { name, arity }) do
6768
d = :gen_server.call(to_pid(pid), :digraph, @timeout)
6869
:digraph.out_neighbours(d, { :import, name, arity })
6970
end
7071

72+
@doc """
73+
Returns all the modules which were remotely dispatched
74+
to. All external dependencies to a module is the sum
75+
of imports and remotes.
76+
"""
77+
def remotes(pid) do
78+
d = :gen_server.call(to_pid(pid), :digraph, @timeout)
79+
:digraph.out_neighbours(d, :remote)
80+
end
81+
82+
@doc """
83+
Returns all modules that had the given `{ name, arity }`
84+
invoked remotely.
85+
"""
86+
def remotes_with_dispatch(pid, { name, arity }) do
87+
d = :gen_server.call(to_pid(pid), :digraph, @timeout)
88+
:digraph.out_neighbours(d, { :remote, name, arity })
89+
end
90+
7191
@doc """
7292
Returns all locals that are reachable.
7393
@@ -119,6 +139,12 @@ defmodule Module.DispatchTracker do
119139
:gen_server.cast(pid, { :add_local, from, to })
120140
end
121141

142+
# Adds a remote dispatch to the given target.
143+
@doc false
144+
def add_remote(pid, module, target) when is_atom(module) and is_tuple(target) do
145+
:gen_server.cast(pid, { :add_remote, module, target })
146+
end
147+
122148
# Adds a import dispatch to the given target.
123149
@doc false
124150
def add_import(pid, module, target) when is_atom(module) and is_tuple(target) do
@@ -224,6 +250,7 @@ defmodule Module.DispatchTracker do
224250
d = :digraph.new([:protected])
225251
:digraph.add_vertex(d, :local)
226252
:digraph.add_vertex(d, :import)
253+
:digraph.add_vertex(d, :remote)
227254
:digraph.add_vertex(d, :warn)
228255
{ :ok, d }
229256
end
@@ -253,14 +280,13 @@ defmodule Module.DispatchTracker do
253280
{ :noreply, d }
254281
end
255282

256-
def handle_cast({ :add_import, module, { name, arity } }, d) do
257-
:digraph.add_vertex(d, module)
258-
replace_edge!(d, :import, module)
259-
260-
tuple = { :import, name, arity }
261-
:digraph.add_vertex(d, tuple)
262-
replace_edge!(d, tuple, module)
283+
def handle_cast({ :add_remote, module, { name, arity } }, d) do
284+
record_import_or_remote(d, :remote, module, name, arity)
285+
{ :noreply, d }
286+
end
263287

288+
def handle_cast({ :add_import, module, { name, arity } }, d) do
289+
record_import_or_remote(d, :import, module, name, arity)
264290
{ :noreply, d }
265291
end
266292

@@ -310,6 +336,15 @@ defmodule Module.DispatchTracker do
310336
{ :ok, d }
311337
end
312338

339+
defp record_import_or_remote(d, kind, module, name, arity) do
340+
:digraph.add_vertex(d, module)
341+
replace_edge!(d, kind, module)
342+
343+
tuple = { kind, name, arity }
344+
:digraph.add_vertex(d, tuple)
345+
replace_edge!(d, tuple, module)
346+
end
347+
313348
defp replace_edge!(d, from, to) do
314349
unless :lists.member(to, :digraph.out_neighbours(d, from)) do
315350
[:"$e"|_] = :digraph.add_edge(d, from, to)

lib/elixir/src/elixir_dispatch.erl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ require_function(Meta, Receiver, Name, Arity, S) ->
5050

5151
case is_element(Tuple, get_optional_macros(Receiver)) of
5252
true -> false;
53-
false -> remote_function(Meta, Receiver, Name, Arity, S)
53+
false ->
54+
elixir_tracker:record_remote(Tuple, Receiver, S#elixir_scope.module),
55+
remote_function(Meta, Receiver, Name, Arity, S)
5456
end.
5557

5658
%% Function dispatch
@@ -69,6 +71,7 @@ dispatch_import(Meta, Name, Args, S, Callback) ->
6971
end,
7072
elixir_translator:translate_each({ { '.', Meta, [Endpoint, Name] }, Meta, Args }, S);
7173
{ import, Receiver } ->
74+
elixir_tracker:record_remote(Tuple, Receiver, S#elixir_scope.module),
7275
elixir_translator:translate_each({ { '.', Meta, [Receiver, Name] }, [{require,false}|Meta], Args }, S);
7376
Result ->
7477
case do_expand_import(Meta, Tuple, Args, Module, S, Result) of
@@ -89,13 +92,15 @@ dispatch_require(Meta, Receiver, Name, Args, S, Callback) ->
8992

9093
case (Receiver == ?builtin) andalso is_element(Tuple, in_erlang_functions()) of
9194
true ->
95+
elixir_tracker:record_remote(Tuple, Receiver, S#elixir_scope.module),
9296
{ TArgs, SA } = elixir_translator:translate_args(Args, S),
9397
{ ?wrap_call(?line(Meta), erlang, Name, TArgs), SA };
9498
false ->
9599
case expand_require(Meta, Receiver, Tuple, Args, Module, S) of
96100
{ error, noexpansion } ->
97101
Callback();
98102
{ error, internal } ->
103+
elixir_tracker:record_remote(Tuple, ?builtin, S#elixir_scope.module),
99104
elixir_macros:translate({ Name, Meta, Args }, S);
100105
{ ok, _Receiver, Tree } ->
101106
translate_expansion(Meta, Tree, S)
@@ -137,8 +142,11 @@ expand_require(Meta, ?builtin, { Name, Arity } = Tuple, Args, Module, S) ->
137142
true -> { error, internal };
138143
false ->
139144
case is_element(Tuple, in_elixir_macros()) of
140-
true -> { ok, ?builtin, expand_macro_named(Meta, ?builtin, Name, Arity, Args, Module, S) };
141-
false -> { error, noexpansion }
145+
true ->
146+
elixir_tracker:record_remote(Tuple, ?builtin, S#elixir_scope.module),
147+
{ ok, ?builtin, expand_macro_named(Meta, ?builtin, Name, Arity, Args, Module, S) };
148+
false ->
149+
{ error, noexpansion }
142150
end
143151
end;
144152

@@ -149,7 +157,9 @@ expand_require(Meta, Receiver, { Name, Arity } = Tuple, Args, Module, S) ->
149157
case Fun of
150158
false ->
151159
case is_element(Tuple, get_optional_macros(Receiver)) of
152-
true -> { ok, Receiver, expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, S) };
160+
true ->
161+
elixir_tracker:record_remote(Tuple, Receiver, S#elixir_scope.module),
162+
{ ok, Receiver, expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, S) };
153163
false -> { error, noexpansion }
154164
end;
155165
_ ->

lib/elixir/src/elixir_tracker.erl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-module(elixir_tracker).
44
-export([
55
setup/1, cleanup/1,
6-
record_local/2, record_import/3,
6+
record_local/2, record_import/3, record_remote/3,
77
record_warn/4, record_definition/3,
88
ensure_no_import_conflict/4, ensure_all_imports_used/3,
99
warn_unused_local/3, format_error/1
@@ -53,6 +53,16 @@ record_import(Tuple, Receiver, Module) ->
5353
error:badarg -> false
5454
end.
5555

56+
record_remote(_Tuple, Receiver, Module)
57+
when Module == nil; Module == Receiver -> false;
58+
record_remote(Tuple, Receiver, Module) ->
59+
try
60+
Pid = ets:lookup_element(Module, ?attr, 2),
61+
?tracker:add_remote(Pid, Receiver, Tuple)
62+
catch
63+
error:badarg -> false
64+
end.
65+
5666
record_definition(Tuple, Kind, Module) ->
5767
if_tracker(Module, fun(Pid) ->
5868
?tracker:add_definition(Pid, Kind, Tuple),

lib/elixir/test/elixir/module/dispatch_tracker_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,21 @@ defmodule Module.DispatchTrackerTest do
139139
D.add_import(config[:pid], Module, { :conflict, 1 })
140140
assert { [Module], :conflict, 1 } in D.collect_imports_conflicts(config[:pid], [conflict: 1])
141141
end
142+
143+
## Remotes
144+
145+
test "can add remote", config do
146+
D.add_remote(config[:pid], Module, { :concat, 1 })
147+
end
148+
149+
test "can retrieve remotes", config do
150+
D.add_remote(config[:pid], Module, { :concat, 1 })
151+
assert Module in D.remotes(config[:pid])
152+
end
153+
154+
test "find remotes from dispatch", config do
155+
D.add_remote(config[:pid], Module, { :concat, 1 })
156+
assert Module in D.remotes_with_dispatch(config[:pid], { :concat, 1 })
157+
refute Module in D.remotes_with_dispatch(config[:pid], { :unknown, 1 })
158+
end
142159
end

0 commit comments

Comments
 (0)