Skip to content

Commit 4b79979

Browse files
committed
Write file later, use fresher check for verification
1 parent 70c8f41 commit 4b79979

1 file changed

Lines changed: 62 additions & 54 deletions

File tree

lib/elixir/scripts/infer.exs

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,80 +5,88 @@
55
# for stdlib itself.
66
parent = self()
77
ebin = Path.expand("../ebin", __DIR__)
8-
{:ok, checker} = Module.ParallelChecker.start_link()
98

109
# Validate we are loading Elixir modules and that they are all in place
1110
[:elixir] = Code.get_compiler_option(:infer_signatures)
1211

13-
# Now we prefill all exports so the cache is prebuilt instead of depending
14-
# on the concurrency of the async stream below
1512
[_ | _] =
1613
modules =
1714
for module <- Application.spec(:elixir, :modules),
1815
match?("Elixir." <> _, Atom.to_string(module)) do
19-
Enum.each(module.__info__(:functions), fn {fun, arity} ->
20-
Module.ParallelChecker.fetch_export(checker, module, fun, arity, true)
21-
end)
22-
2316
module
2417
end
2518

26-
{time, modules} =
27-
:timer.tc(fn ->
28-
modules
29-
|> Task.async_stream(
30-
fn module ->
31-
path = Path.join(ebin, "#{module}.beam")
32-
Module.ParallelChecker.put(parent, checker)
33-
cache = Module.ParallelChecker.get()
34-
binary = File.read!(path)
35-
36-
{:ok, {_, [{:debug_info, debug_info}, {_, checker_blob}]}} =
37-
:beam_lib.chunks(binary, [:debug_info, ~c"ExCk"])
38-
39-
{:debug_info_v1, _backend, {:elixir_v1, module_map, _specs}} = debug_info
40-
41-
%{module: module, file: file, attributes: attributes, definitions: definitions} =
42-
module_map
43-
44-
{_, checker} = :erlang.binary_to_term(checker_blob)
45-
env = :elixir_env.new()
46-
47-
# We assume that all private functions have been invoked at this point
48-
private =
49-
for {fun_arity, kind, _, _} <- definitions, kind in [:defp, :defmacrop], do: fun_arity
50-
51-
{signatures, _} =
52-
Module.Types.infer(module, file, attributes, definitions, private, env, cache)
53-
54-
checker =
55-
update_in(checker.exports, fn exports ->
56-
for {fun, info} <- exports do
57-
{fun, %{info | sig: Map.get(signatures, fun, info.sig)}}
58-
end
59-
end)
60-
61-
[{"ExCk", checker_chunk}] = :elixir_erl.checker_chunk(checker, [:deterministic])
62-
{:ok, ^module, chunks} = :beam_lib.all_chunks(binary)
63-
64-
{:ok, new_binary} =
65-
chunks
66-
|> List.keyreplace(~c"ExCk", 0, {~c"ExCk", checker_chunk})
67-
|> :beam_lib.build_module()
19+
# Do a quick sanity check that some modules are defined
20+
true = URI in modules and Version.Requirement in modules
6821

22+
{time, modules_paths} =
23+
:timer.tc(fn ->
24+
{:ok, checker} = Module.ParallelChecker.start_link()
25+
26+
try do
27+
modules
28+
|> Task.async_stream(
29+
fn module ->
30+
path = Path.join(ebin, "#{module}.beam")
31+
Module.ParallelChecker.put(parent, checker)
32+
cache = Module.ParallelChecker.get()
33+
binary = File.read!(path)
34+
35+
{:ok, {_, [{:debug_info, debug_info}, {_, checker_blob}]}} =
36+
:beam_lib.chunks(binary, [:debug_info, ~c"ExCk"])
37+
38+
{:debug_info_v1, _backend, {:elixir_v1, module_map, _specs}} = debug_info
39+
40+
%{module: module, file: file, attributes: attributes, definitions: definitions} =
41+
module_map
42+
43+
{_, checker} = :erlang.binary_to_term(checker_blob)
44+
env = :elixir_env.new()
45+
46+
# We assume that all private functions have been invoked at this point
47+
private =
48+
for {fun_arity, kind, _, _} <- definitions, kind in [:defp, :defmacrop], do: fun_arity
49+
50+
{signatures, _} =
51+
Module.Types.infer(module, file, attributes, definitions, private, env, cache)
52+
53+
checker =
54+
update_in(checker.exports, fn exports ->
55+
for {fun, info} <- exports do
56+
{fun, %{info | sig: Map.get(signatures, fun, info.sig)}}
57+
end
58+
end)
59+
60+
[{"ExCk", checker_chunk}] = :elixir_erl.checker_chunk(checker, [:deterministic])
61+
{:ok, ^module, chunks} = :beam_lib.all_chunks(binary)
62+
63+
{:ok, new_binary} =
64+
chunks
65+
|> List.keyreplace(~c"ExCk", 0, {~c"ExCk", checker_chunk})
66+
|> :beam_lib.build_module()
67+
68+
{module, path, new_binary}
69+
end,
70+
timeout: :infinity
71+
)
72+
|> Enum.map(fn {:ok, {module, path, new_binary}} ->
73+
# Only write to files once we are done to avoid the result
74+
# of one task affecting other ones
6975
File.write!(path, new_binary)
7076
{module, path}
71-
end,
72-
timeout: :infinity
73-
)
74-
|> Enum.map(fn {:ok, result} -> result end)
77+
end)
78+
after
79+
Module.ParallelChecker.stop(checker)
80+
end
7581
end)
7682

7783
IO.puts(:stderr, ["Type inferred stdlib in ", Integer.to_string(div(time, 1000)), "ms"])
7884

7985
{time, _} =
8086
:timer.tc(fn ->
81-
Module.ParallelChecker.verify(checker, modules)
87+
# We start a new one so it uses the new cache
88+
{:ok, checker} = Module.ParallelChecker.start_link()
89+
Module.ParallelChecker.verify(checker, modules_paths)
8290
end)
8391

8492
IO.puts(:stderr, ["Type checked stdlib in ", Integer.to_string(div(time, 1000)), "ms"])

0 commit comments

Comments
 (0)