diff options
author | Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com> | 2019-12-06 20:53:32 +0100 |
---|---|---|
committer | Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com> | 2019-12-07 13:32:29 +0100 |
commit | cdcda87282f8e22fa768152fd20ab74dbc186965 (patch) | |
tree | 18bc01845ab02cad2674cce9130e08f114b53c96 | |
parent | fb2a991fcaf2449ab6dd90632efdc806f4dd86b5 (diff) | |
download | elixir-emj/remove-chunk-signatures.tar.gz |
Build checker chunk before group passemj/remove-chunk-signatures
-rw-r--r-- | lib/elixir/lib/code.ex | 4 | ||||
-rw-r--r-- | lib/elixir/lib/kernel/parallel_compiler.ex | 30 | ||||
-rw-r--r-- | lib/elixir/lib/module/checker.ex | 28 | ||||
-rw-r--r-- | lib/elixir/lib/module/parallel_checker.ex | 60 | ||||
-rw-r--r-- | lib/elixir/src/elixir_erl.erl | 30 | ||||
-rw-r--r-- | lib/elixir/test/elixir/module/checker_test.exs | 2 |
6 files changed, 72 insertions, 82 deletions
diff --git a/lib/elixir/lib/code.ex b/lib/elixir/lib/code.ex index 8fb96c83b..37d78ad44 100644 --- a/lib/elixir/lib/code.ex +++ b/lib/elixir/lib/code.ex @@ -1376,7 +1376,7 @@ defmodule Code do defp verify_loaded(loaded) do maps_binaries = Enum.map(loaded, fn {_module, map, binary} -> {map, binary} end) - {loaded, _warnings} = Module.ParallelChecker.verify(maps_binaries, []) - loaded + Module.ParallelChecker.verify(maps_binaries, []) + Enum.map(loaded, fn {module, map, _binary} -> {module, map} end) end end diff --git a/lib/elixir/lib/kernel/parallel_compiler.ex b/lib/elixir/lib/kernel/parallel_compiler.ex index 722d11065..7994560a0 100644 --- a/lib/elixir/lib/kernel/parallel_compiler.ex +++ b/lib/elixir/lib/kernel/parallel_compiler.ex @@ -355,32 +355,34 @@ defmodule Kernel.ParallelCompiler do defp each_cycle_return(other), do: other defp write_and_verify_modules(result, warnings, dependent_modules, state) do - %{output: output, beam_timestamp: beam_timestamp} = state - - {binaries, checker_warnings} = maybe_check_modules(result, dependent_modules, state) - write_module_binaries(output, beam_timestamp, binaries) + modules = write_module_binaries(result, state) + checker_warnings = maybe_check_modules(result, dependent_modules, state) warnings = Enum.reverse(warnings, checker_warnings) - {:ok, Enum.map(binaries, &elem(&1, 0)), warnings} + {:ok, modules, warnings} end - defp write_module_binaries({:compile, path}, timestamp, result) do - Enum.each(result, fn {module, binary} -> - full_path = Path.join(path, Atom.to_string(module) <> ".beam") - File.write!(full_path, binary) - if timestamp, do: File.touch!(full_path, timestamp) + defp write_module_binaries(result, %{output: {:compile, path}, beam_timestamp: timestamp}) do + Enum.flat_map(result, fn + {{:module, module}, {binary, _map}} -> + full_path = Path.join(path, Atom.to_string(module) <> ".beam") + File.write!(full_path, binary) + if timestamp, do: File.touch!(full_path, timestamp) + [module] + + _ -> + [] end) end - defp write_module_binaries(_output, _timestamp, _result) do - :ok + defp write_module_binaries(result, _state) do + for {{:module, module}, _} <- result, do: module end defp maybe_check_modules(result, runtime_modules, state) do %{schedulers: schedulers, profile: profile} = state if :elixir_config.get(:bootstrap) do - binaries = for {{:module, module}, {binary, _map}} <- result, do: {module, binary} - {binaries, []} + [] else compiled_modules = checker_compiled_modules(result) runtime_modules = checker_runtime_modules(runtime_modules) diff --git a/lib/elixir/lib/module/checker.ex b/lib/elixir/lib/module/checker.ex index 38ef5671f..452f6cd15 100644 --- a/lib/elixir/lib/module/checker.ex +++ b/lib/elixir/lib/module/checker.ex @@ -9,10 +9,10 @@ defmodule Module.Checker do undefined_and_deprecation_warnings = undefined_and_deprecation_warnings(map, cache) infer_warnings = infer_definitions(map) warnings = infer_warnings ++ undefined_and_deprecation_warnings - {build_chunk(map), emit_warnings(warnings)} + emit_warnings(warnings) :error -> - {nil, []} + [] end end @@ -21,9 +21,9 @@ defmodule Module.Checker do %{ module: module, file: module_map.file, - no_warn_undefined: no_warn_undefined(module_map.compile_opts), definitions: module_map.definitions, - deprecated: module_map.deprecated + deprecated: module_map.deprecated, + no_warn_undefined: no_warn_undefined(module_map.compile_opts) }} end @@ -33,8 +33,8 @@ defmodule Module.Checker do {:ok, %{ module: module, - definitions: debug_info.definitions, file: debug_info.file, + definitions: debug_info.definitions, deprecated: checker_info.deprecated, no_warn_undefined: checker_info.no_warn_undefined }} @@ -69,24 +69,6 @@ defmodule Module.Checker do end end - defp build_chunk(map) do - exports = ParallelChecker.definitions_to_exports(map.definitions) - deprecated = Map.new(map.deprecated) - - exports = - Enum.map(exports, fn {function, kind} -> - deprecated_reason = Map.get(deprecated, function) - {function, %{kind: kind, deprecated_reason: deprecated_reason}} - end) - - contents = %{ - exports: Enum.sort(exports), - no_warn_undefined: map.no_warn_undefined - } - - {'ExCk', :erlang.term_to_binary({:elixir_checker_v1, contents})} - end - defp infer_definitions(map) do results = Module.Types.infer_definitions(map.file, map.module, map.definitions) Enum.flat_map(results, fn {_function, reasons} -> reasons end) diff --git a/lib/elixir/lib/module/parallel_checker.ex b/lib/elixir/lib/module/parallel_checker.ex index ca737457f..a16bf106a 100644 --- a/lib/elixir/lib/module/parallel_checker.ex +++ b/lib/elixir/lib/module/parallel_checker.ex @@ -10,8 +10,7 @@ defmodule Module.ParallelChecker do the modules and adds the ExCk chunk to the binaries. Returns the updated binaries and a list of warnings from the verification. """ - @spec verify([{%{}, binary()}], [{module(), binary()}], pos_integer()) :: - {[{module(), binary()}], [warning()]} + @spec verify([{map(), binary()}], [{module(), binary()}], pos_integer()) :: [warning()] def verify(compiled_modules, runtime_binaries, schedulers \\ nil) do compiled_maps = Enum.map(compiled_modules, fn {map, _binary} -> {map.module, map} end) check_modules = compiled_maps ++ runtime_binaries @@ -21,36 +20,20 @@ defmodule Module.ParallelChecker do preload_cache(get_ets(server), check_modules) start(server) - compiled_binaries = Enum.map(compiled_modules, fn {map, binary} -> {map.module, binary} end) - old_binaries = Map.new(compiled_binaries ++ runtime_binaries) - collect_results(old_binaries, [], []) + collect_results(length(check_modules), []) end - defp collect_results(old_binaries, binaries, warnings) when map_size(old_binaries) == 0 do - {binaries, warnings} + defp collect_results(0, warnings) do + warnings end - defp collect_results(old_binaries, binaries, warnings) do + defp collect_results(count, warnings) do receive do - {__MODULE__, module, chunk, new_warnings} -> - {binary, old_binaries} = Map.pop(old_binaries, module) - binaries = [{module, add_chunk(chunk, binary)} | binaries] - - warnings = new_warnings ++ warnings - collect_results(old_binaries, binaries, warnings) + {__MODULE__, _module, new_warnings} -> + collect_results(count - 1, new_warnings ++ warnings) end end - defp add_chunk(nil, binary) do - binary - end - - defp add_chunk(chunk, binary) do - {:ok, _module, chunks} = :beam_lib.all_chunks(binary) - {:ok, binary} = :beam_lib.build_module([chunk | :lists.keydelete('ExCk', 1, chunks)]) - binary - end - @doc """ Preloads a module into the cache. Call this function before any other cache lookups for the module. @@ -98,21 +81,6 @@ defmodule Module.ParallelChecker do |> Enum.sort() end - @doc """ - Collects all exported functions and macros from the module definition ASTs. - """ - @spec definitions_to_exports([{atom(), arity(), term(), term()}]) :: - [{{atom(), arity()}, kind()}] - def definitions_to_exports(definitions) do - Enum.flat_map(definitions, fn {function, kind, _meta, _clauses} -> - if kind in [:def, :defmacro] do - [{function, kind}] - else - [] - end - end) - end - def init([modules, send_results, schedulers]) do ets = :ets.new(:checker_cache, [:set, :public, {:read_concurrency, true}]) @@ -208,8 +176,8 @@ defmodule Module.ParallelChecker do send_results_pid = state.send_results spawn_link(fn -> - {chunk, warnings} = Module.Checker.verify(verify, {parent, ets}) - send(send_results_pid, {__MODULE__, module, chunk, warnings}) + warnings = Module.Checker.verify(verify, {parent, ets}) + send(send_results_pid, {__MODULE__, module, warnings}) send(parent, {__MODULE__, :done}) end) @@ -299,4 +267,14 @@ defmodule Module.ParallelChecker do :ets.insert(ets, {{:all_exports, module}, exports}) :ets.insert(ets, {{:cached, module}, true}) end + + defp definitions_to_exports(definitions) do + Enum.flat_map(definitions, fn {function, kind, _meta, _clauses} -> + if kind in [:def, :defmacro] do + [{function, kind}] + else + [] + end + end) + end end diff --git a/lib/elixir/src/elixir_erl.erl b/lib/elixir/src/elixir_erl.erl index 8eb87e2e3..954d6b05a 100644 --- a/lib/elixir/src/elixir_erl.erl +++ b/lib/elixir/src/elixir_erl.erl @@ -146,8 +146,9 @@ spawned_compile(Map, Set, _Bag, TranslatedTypespecs) -> #{module := Module, line := Line} = Map, DocsChunk = docs_chunk(Set, Module, Line, Def, Defmacro, Types, Callbacks), + CheckerChunk = checker_chunk(Map), - load_form(Map, Prefix, Forms, TypeSpecs, DocsChunk). + load_form(Map, Prefix, Forms, TypeSpecs, DocsChunk ++ CheckerChunk). dynamic_form(#{module := Module, line := Line, relative_file := RelativeFile, attributes := Attributes, definitions := Definitions, unreachable := Unreachable, @@ -548,6 +549,33 @@ signature_to_binary(Module, '__struct__', []) -> signature_to_binary(_, Name, Signature) -> 'Elixir.Macro':to_string({Name, [], Signature}). +checker_chunk(#{definitions := Definitions, deprecated := Deprecated, compile_opts := CompileOpts}) -> + DeprecatedMap = maps:from_list(Deprecated), + + Exports = + lists:foldl(fun({Function, Kind, _Meta, _Clauses}, Acc) -> + case Kind of + _ when Kind == def orelse Kind == defmacro -> + Reason = maps:get(Function, DeprecatedMap, nil), + [{Function, #{kind => Kind, deprecated_reason => Reason}} | Acc]; + _ -> + Acc + end + end, [], Definitions), + + Contents = #{ + exports => lists:sort(Exports), + no_warn_undefined => no_warn_undefined(CompileOpts) + }, + + [{<<"ExCk">>, erlang:term_to_binary({elixir_checker_v1, Contents})}]. + +no_warn_undefined(CompileOpts) -> + [Value || {no_warn_undefined, Values} <- CompileOpts, Value <- list_wrap(Values)]. + +list_wrap(List) when is_list(List) -> List; +list_wrap(Other) -> [Other]. + %% Errors form_error(#{line := Line, file := File}, Error) -> diff --git a/lib/elixir/test/elixir/module/checker_test.exs b/lib/elixir/test/elixir/module/checker_test.exs index 543d7f300..07efb2120 100644 --- a/lib/elixir/test/elixir/module/checker_test.exs +++ b/lib/elixir/test/elixir/module/checker_test.exs @@ -156,7 +156,7 @@ defmodule Module.CheckerTest do assert_warnings(files, warning) end - test " reports missing functions respecting arity" do + test "reports missing functions respecting arity" do files = %{ "a.ex" => """ defmodule A do |