aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/preloaded/src/add_abstract_code14
-rw-r--r--lib/compiler/doc/src/compile.xml25
-rw-r--r--lib/compiler/src/compile.erl89
-rw-r--r--lib/compiler/src/v3_core.erl12
-rw-r--r--lib/compiler/src/v3_kernel.erl2
-rw-r--r--lib/compiler/test/compilation_SUITE.erl2
-rw-r--r--lib/compiler/test/compile_SUITE.erl44
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl86
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl11
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl298
-rw-r--r--lib/dialyzer/src/typer.erl36
-rw-r--r--lib/stdlib/doc/src/beam_lib.xml46
-rw-r--r--lib/stdlib/src/Makefile1
-rw-r--r--lib/stdlib/src/beam_lib.erl105
-rw-r--r--lib/stdlib/src/erl_abstract_code.erl28
-rw-r--r--lib/stdlib/src/stdlib.app.src1
-rw-r--r--lib/stdlib/test/beam_lib_SUITE.erl40
17 files changed, 476 insertions, 364 deletions
diff --git a/erts/preloaded/src/add_abstract_code b/erts/preloaded/src/add_abstract_code
index 943987872e..f53791cdc2 100644
--- a/erts/preloaded/src/add_abstract_code
+++ b/erts/preloaded/src/add_abstract_code
@@ -28,12 +28,12 @@
main([BeamFile,AbstrFile]) ->
{ok,_,Chunks0} = beam_lib:all_chunks(BeamFile),
{ok,Abstr} = file:consult(AbstrFile),
- Chunks1 = lists:keyreplace("Abst", 1, Chunks0,
- {"Abst",term_to_binary({raw_abstract_v1,Abstr})}),
- {"CInf",CInf0} = lists:keyfind("CInf", 1, Chunks1),
- CInf = fix_options(CInf0),
- Chunks = lists:keyreplace("CInf", 1, Chunks1, {"CInf",CInf}),
- {ok,Module} = beam_lib:build_module(Chunks),
+ {"CInf",CInf0} = lists:keyfind("CInf", 1, Chunks0),
+ {CInf, COpts} = fix_options(CInf0),
+ Chunks1 = lists:keyreplace("CInf", 1, Chunks0, {"CInf",CInf}),
+ Chunks2 = lists:keyreplace("Dbgi", 1, Chunks1,
+ {"Dbgi",term_to_binary({debug_info_v1,erl_abstract_code,{Abstr, COpts}})}),
+ {ok,Module} = beam_lib:build_module(Chunks2),
ok = file:write_file(BeamFile, Module),
init:stop().
@@ -42,4 +42,4 @@ fix_options(CInf0) ->
{options,Opts0} = lists:keyfind(options, 1, CInf1),
Opts = Opts0 -- [from_asm],
CInf = lists:keyreplace(options, 1, CInf1, {options,Opts}),
- term_to_binary(CInf).
+ {term_to_binary(CInf), Opts}.
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index ed04dac1c0..94bda0d5e3 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -132,12 +132,10 @@
<tag><c>debug_info</c></tag>
<item>
<marker id="debug_info"></marker>
- <p>Includes debug information in the form of abstract code
- (see
- <seealso marker="erts:absform">The Abstract Format</seealso>
- in ERTS User's Guide) in the compiled beam module. Tools
- such as Debugger, Xref, and Cover require
- the debug information to be included.</p>
+ <p>Includes debug information in the form of <seealso marker="erts:absform">
+ Erlang Abstract Format</seealso> in the <c>debug_info</c>
+ chunk of the compiled beam module. Tools such as Debugger,
+ Xref, and Cover require the debug information to be included.</p>
<p><em>Warning</em>: Source code can be reconstructed from
the debug information. Use encrypted debug information
@@ -147,6 +145,21 @@
<seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p>
</item>
+ <tag><c>{debug_info, {Backend, Data}}</c></tag>
+ <item>
+ <marker id="debug_info"></marker>
+ <p>Includes custom debug information in the form of a
+ <c>Backend</c> module with custom <c>Data</c> in the compiled beam module.
+ The given module must implement a <c>debug_info/4</c> function
+ and is responsible for generating different code representations,
+ as described in the <c>debug_info</c> under
+ <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p>
+
+ <p><em>Warning</em>: Source code can be reconstructed from
+ the debug information. Use encrypted debug information
+ (<c>encrypt_debug_info</c>) to prevent this.</p>
+ </item>
+
<tag><c>{debug_info_key,KeyString}</c></tag>
<item></item>
<tag><c>{debug_info_key,{Mode,KeyString}}</c></tag>
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 019d8ba864..b106f856ee 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -198,9 +198,9 @@ expand_opts(Opts0) ->
%% {debug_info_key,Key} implies debug_info.
Opts = case {proplists:get_value(debug_info_key, Opts0),
proplists:get_value(encrypt_debug_info, Opts0),
- proplists:get_bool(debug_info, Opts0)} of
+ proplists:get_value(debug_info, Opts0)} of
{undefined,undefined,_} -> Opts0;
- {_,_,false} -> [debug_info|Opts0];
+ {_,_,undefined} -> [debug_info|Opts0];
{_,_,_} -> Opts0
end,
foldr(fun expand_opt/2, [], Opts).
@@ -302,7 +302,7 @@ format_error_reason(Reason) ->
ofile="" :: file:filename(),
module=[] :: module() | [],
core_code=[] :: cerl:c_module() | [],
- abstract_code=[] :: binary() | [], %Abstract code for debugger.
+ abstract_code=[] :: abstract_code(), %Abstract code for debugger.
options=[] :: [option()], %Options for compilation
mod_options=[] :: [option()], %Options for module_info
encoding=none :: none | epp:source_encoding(),
@@ -1315,44 +1315,43 @@ core_inline_module(Code0, #compile{options=Opts}=St) ->
Code = cerl_inline:core_transform(Code0, Opts),
{ok,Code,St}.
-save_abstract_code(Code, #compile{ifile=File}=St) ->
- case abstract_code(Code, St) of
- {ok,Abstr} ->
- {ok,Code,St#compile{abstract_code=Abstr}};
- {error,Es} ->
- {error,St#compile{errors=St#compile.errors ++ [{File,Es}]}}
- end.
+save_abstract_code(Code, St) ->
+ {ok,Code,St#compile{abstract_code=erl_parse:anno_to_term(Code)}}.
+
+debug_info(#compile{module=Module,mod_options=Opts0,ofile=OFile,abstract_code=Abst}) ->
+ AbstOpts = cleanup_compile_options(Opts0),
+ Opts1 = proplists:delete(debug_info, Opts0),
+ {Backend,Metadata,Opts2} =
+ case proplists:get_value(debug_info, Opts0, false) of
+ {OptBackend,OptMetadata} when is_atom(OptBackend) -> {OptBackend,OptMetadata,Opts1};
+ false -> {erl_abstract_code,{none,AbstOpts},Opts1};
+ true -> {erl_abstract_code,{Abst,AbstOpts},[debug_info | Opts1]}
+ end,
+ DebugInfo = erlang:term_to_binary({debug_info_v1,Backend,Metadata}, [compressed]),
-abstract_code(Code0, #compile{options=Opts,ofile=OFile}) ->
- Code = erl_parse:anno_to_term(Code0),
- Abstr = erlang:term_to_binary({raw_abstract_v1,Code}, [compressed]),
- case member(encrypt_debug_info, Opts) of
+ case member(encrypt_debug_info, Opts2) of
true ->
- case keyfind(debug_info_key, 1, Opts) of
- {_,Key} ->
- encrypt_abs_code(Abstr, Key);
+ case lists:keytake(debug_info_key, 1, Opts2) of
+ {value,{_, Key},Opts3} ->
+ encrypt_debug_info(DebugInfo, Key, [{debug_info_key,'********'} | Opts3]);
false ->
- %% Note: #compile.module has not been set yet.
- %% Here is an approximation that should work for
- %% all valid cases.
- Module = list_to_atom(filename:rootname(filename:basename(OFile))),
- Mode = proplists:get_value(crypto_mode, Opts, des3_cbc),
+ Mode = proplists:get_value(crypto_mode, Opts2, des3_cbc),
case beam_lib:get_crypto_key({debug_info, Mode, Module, OFile}) of
error ->
{error, [{none,?MODULE,no_crypto_key}]};
Key ->
- encrypt_abs_code(Abstr, {Mode, Key})
+ encrypt_debug_info(DebugInfo, {Mode, Key}, Opts2)
end
end;
false ->
- {ok,Abstr}
+ {ok,DebugInfo,Opts2}
end.
-encrypt_abs_code(Abstr, Key0) ->
+encrypt_debug_info(DebugInfo, Key, Opts) ->
try
- RealKey = generate_key(Key0),
+ RealKey = generate_key(Key),
case start_crypto() of
- ok -> {ok,encrypt(RealKey, Abstr)};
+ ok -> {ok,encrypt(RealKey, DebugInfo),Opts};
{error,_}=E -> E
end
catch
@@ -1360,6 +1359,20 @@ encrypt_abs_code(Abstr, Key0) ->
{error,[{none,?MODULE,bad_crypto_key}]}
end.
+cleanup_compile_options(Opts) ->
+ lists:filter(fun keep_compile_option/1, Opts).
+
+%% We are storing abstract, not asm or core.
+keep_compile_option(from_asm) -> false;
+keep_compile_option(from_core) -> false;
+%% Parse transform and macros have already been applied.
+keep_compile_option({parse_transform, _}) -> false;
+keep_compile_option({d, _, _}) -> false;
+%% Do not affect compilation result on future calls.
+keep_compile_option({outdir, _}) -> false;
+keep_compile_option(warnings_as_errors) -> false;
+keep_compile_option(Option) -> effects_code_generation(Option).
+
start_crypto() ->
try crypto:start() of
{error,{already_started,crypto}} -> ok;
@@ -1386,16 +1399,16 @@ encrypt({des3_cbc=Type,Key,IVec,BlockSize}, Bin0) ->
save_core_code(Code, St) ->
{ok,Code,St#compile{core_code=cerl:from_records(Code)}}.
-beam_asm(Code0, #compile{ifile=File,abstract_code=Abst,extra_chunks=ExtraChunks,
- options=CompilerOpts,mod_options=Opts0}=St) ->
- Source = paranoid_absname(File),
- Opts1 = lists:map(fun({debug_info_key,_}) -> {debug_info_key,'********'};
- (Other) -> Other
- end, Opts0),
- Opts2 = [O || O <- Opts1, effects_code_generation(O)],
- Chunks = [{<<"Abst">>, Abst} | ExtraChunks],
- case beam_asm:module(Code0, Chunks, Source, Opts2, CompilerOpts) of
- {ok,Code} -> {ok,Code,St#compile{abstract_code=[]}}
+beam_asm(Code0, #compile{ifile=File,extra_chunks=ExtraChunks,options=CompilerOpts}=St) ->
+ case debug_info(St) of
+ {ok,DebugInfo,Opts0} ->
+ Source = paranoid_absname(File),
+ Opts1 = [O || O <- Opts0, effects_code_generation(O)],
+ Chunks = [{<<"Dbgi">>, DebugInfo} | ExtraChunks],
+ {ok,Code} = beam_asm:module(Code0, Chunks, Source, Opts1, CompilerOpts),
+ {ok,Code,St#compile{abstract_code=[]}};
+ {error,Es} ->
+ {error,St#compile{errors=St#compile.errors ++ [{File,Es}]}}
end.
paranoid_absname(""=File) ->
@@ -1479,7 +1492,7 @@ embed_native_code(Code, {Architecture,NativeCode}) ->
%% errors will be reported).
effects_code_generation(Option) ->
- case Option of
+ case Option of
beam -> false;
report_warnings -> false;
report_errors -> false;
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 8dea7ec03a..fcfc1a4076 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -184,11 +184,8 @@ form({function,_,_,_,_}=F0, Module, Opts) ->
form({attribute,_,module,Mod}, Module, _Opts) ->
true = is_atom(Mod),
Module#imodule{name=Mod};
-form({attribute,_,file,{File,_Line}}, Module, _Opts) ->
- Module#imodule{file=File};
-form({attribute,_,compile,_}, Module, _Opts) ->
- %% Ignore compilation options.
- Module;
+form({attribute,_,file,{File,_Line}}=F, #imodule{attrs=As}=Module, _Opts) ->
+ Module#imodule{file=File, attrs=[attribute(F)|As]};
form({attribute,_,import,_}, Module, _Opts) ->
%% Ignore. We have no futher use for imports.
Module;
@@ -201,9 +198,8 @@ form(_, Module, _Opts) ->
%% Ignore uninteresting forms such as 'eof'.
Module.
-attribute(Attribute) ->
- Fun = fun(A) -> [erl_anno:location(A)] end,
- {attribute,Line,Name,Val0} = erl_parse:map_anno(Fun, Attribute),
+attribute({attribute,A,Name,Val0}) ->
+ Line = [erl_anno:location(A)],
Val = if
is_list(Val0) -> Val0;
true -> [Val0]
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 4b5d7d919c..7a0a63d286 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -148,6 +148,8 @@ include_attribute(opaque) -> false;
include_attribute(export_type) -> false;
include_attribute(record) -> false;
include_attribute(optional_callbacks) -> false;
+include_attribute(file) -> false;
+include_attribute(compile) -> false;
include_attribute(_) -> true.
function({#c_var{name={F,Arity}=FA},Body}, St0) ->
diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl
index cd1bc099e9..c98aa07a4f 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -319,7 +319,7 @@ self_compile_1(Config, Prefix, Opts) ->
%% Compile the compiler. (In this node to get better coverage.)
CompA = make_compiler_dir(Priv, Prefix++"compiler_a"),
VsnA = Version ++ ".0",
- compile_compiler(compiler_src(), CompA, VsnA, [clint0,clint|Opts]),
+ compile_compiler(compiler_src(), CompA, VsnA, Opts),
%% Compile the compiler again using the newly compiled compiler.
%% (In another node because reloading the compiler would disturb cover.)
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 474c3e2ca0..11a62ce753 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -27,6 +27,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
app_test/1,appup_test/1,
+ debug_info/4, custom_debug_info/1,
file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1,
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, kernel_listing/1, encrypted_abstr/1,
@@ -51,7 +52,7 @@ all() ->
strict_record, utf8_atoms, extra_chunks,
cover, env, core, core_roundtrip, asm, optimized_guards,
sys_pre_attributes, dialyzer, warnings, pre_load_check,
- env_compiler_options].
+ env_compiler_options, custom_debug_info].
groups() ->
[].
@@ -504,17 +505,23 @@ encrypted_abstr_1(Simple, Target) ->
{ok,simple} = compile:file(Simple,
[debug_info,{debug_info_key,Key},
{outdir,TargetDir}]),
- verify_abstract(Target),
+ verify_abstract(Target, erl_abstract_code),
{ok,simple} = compile:file(Simple,
[{debug_info_key,Key},
{outdir,TargetDir}]),
- verify_abstract(Target),
+ verify_abstract(Target, erl_abstract_code),
{ok,simple} = compile:file(Simple,
[debug_info,{debug_info_key,{des3_cbc,Key}},
{outdir,TargetDir}]),
- verify_abstract(Target),
+ verify_abstract(Target, erl_abstract_code),
+
+ {ok,simple} = compile:file(Simple,
+ [{debug_info,{?MODULE,ok}},
+ {debug_info_key,Key},
+ {outdir,TargetDir}]),
+ verify_abstract(Target, ?MODULE),
{ok,{simple,[{compile_info,CInfo}]}} =
beam_lib:chunks(Target, [compile_info]),
@@ -539,7 +546,7 @@ encrypted_abstr_1(Simple, Target) ->
NewKey = "better use another key here",
write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]),
{ok,simple} = compile:file(Simple, [encrypt_debug_info,report]),
- verify_abstract("simple.beam"),
+ verify_abstract("simple.beam", erl_abstract_code),
ok = file:delete(".erlang.crypt"),
beam_lib:clear_crypto_key_fun(),
{error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} =
@@ -572,9 +579,10 @@ encrypted_abstr_no_crypto(Simple, Target) ->
{outdir,TargetDir},report]),
ok.
-verify_abstract(Target) ->
- {ok,{simple,[Chunk]}} = beam_lib:chunks(Target, [abstract_code]),
- {abstract_code,{raw_abstract_v1,_}} = Chunk.
+verify_abstract(Beam, Backend) ->
+ {ok,{simple,[Abst, Dbgi]}} = beam_lib:chunks(Beam, [abstract_code, debug_info]),
+ {abstract_code,{raw_abstract_v1,_}} = Abst,
+ {debug_info,{debug_info_v1,Backend,_}} = Dbgi.
has_crypto() ->
try
@@ -593,6 +601,26 @@ install_crypto_key(Key) ->
ok = beam_lib:crypto_key_fun(F).
%% Miscellanous tests, mainly to get better coverage.
+debug_info(erlang_v1, Module, ok, _Opts) ->
+ {ok, [Module]};
+debug_info(erlang_v1, Module, error, _Opts) ->
+ {error, unknown_format}.
+
+custom_debug_info(Config) when is_list(Config) ->
+ {Simple,_} = get_files(Config, simple, "file_1"),
+
+ {ok,simple,OkBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,ok}}]), %Coverage
+ {ok,{simple,[{abstract_code,{raw_abstract_v1,[simple]}}]}} =
+ beam_lib:chunks(OkBin, [abstract_code]),
+ {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,ok}}]}} =
+ beam_lib:chunks(OkBin, [debug_info]),
+
+ {ok,simple,ErrorBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,error}}]), %Coverage
+ {ok,{simple,[{abstract_code,no_abstract_code}]}} =
+ beam_lib:chunks(ErrorBin, [abstract_code]),
+ {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,error}}]}} =
+ beam_lib:chunks(ErrorBin, [debug_info]).
+
cover(Config) when is_list(Config) ->
io:format("~p\n", [compile:options()]),
ok.
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index aeeb895a0c..051b700231 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -385,68 +385,46 @@ compile_src(File, Includes, Defines, Callgraph, CServer, UseContracts,
DefaultIncludes = default_includes(filename:dirname(File)),
SrcCompOpts = dialyzer_utils:src_compiler_opts(),
CompOpts = SrcCompOpts ++ Includes ++ Defines ++ DefaultIncludes,
- case dialyzer_utils:get_abstract_code_from_src(File, CompOpts) of
+ case dialyzer_utils:get_core_from_src(File, CompOpts) of
{error, _Msg} = Error -> Error;
- {ok, AbstrCode} ->
- compile_common(File, AbstrCode, CompOpts, Callgraph, CServer,
- UseContracts, LegalWarnings)
+ {ok, Core} ->
+ compile_common(Core, Callgraph, CServer, UseContracts, LegalWarnings)
end.
compile_byte(File, Callgraph, CServer, UseContracts, LegalWarnings) ->
- case dialyzer_utils:get_abstract_code_from_beam(File) of
- error ->
- {error, " Could not get abstract code for: " ++ File ++ "\n" ++
- " Recompile with +debug_info or analyze starting from source code"};
- {ok, AbstrCode} ->
- compile_byte(File, AbstrCode, Callgraph, CServer, UseContracts,
- LegalWarnings)
- end.
-
-compile_byte(File, AbstrCode, Callgraph, CServer, UseContracts,
- LegalWarnings) ->
- case dialyzer_utils:get_compile_options_from_beam(File) of
- error ->
- {error, " Could not get compile options for: " ++ File ++ "\n" ++
- " Recompile or analyze starting from source code"};
- {ok, CompOpts} ->
- compile_common(File, AbstrCode, CompOpts, Callgraph, CServer,
- UseContracts, LegalWarnings)
+ case dialyzer_utils:get_core_from_beam(File) of
+ {error, _} = Error -> Error;
+ {ok, Core} ->
+ compile_common(Core, Callgraph, CServer, UseContracts, LegalWarnings)
end.
-compile_common(File, AbstrCode, CompOpts, Callgraph, CServer,
- UseContracts, LegalWarnings) ->
- case dialyzer_utils:get_core_from_abstract_code(AbstrCode, CompOpts) of
- error -> {error, " Could not get core Erlang code for: " ++ File};
- {ok, Core} ->
- Mod = cerl:concrete(cerl:module_name(Core)),
- case dialyzer_utils:get_record_and_type_info(AbstrCode) of
+compile_common(Core, Callgraph, CServer, UseContracts, LegalWarnings) ->
+ Mod = cerl:concrete(cerl:module_name(Core)),
+ case dialyzer_utils:get_record_and_type_info(Core) of
+ {error, _} = Error -> Error;
+ {ok, RecInfo} ->
+ CServer1 =
+ dialyzer_codeserver:store_temp_records(Mod, RecInfo, CServer),
+ case dialyzer_utils:get_fun_meta_info(Mod, Core, LegalWarnings) of
{error, _} = Error -> Error;
- {ok, RecInfo} ->
- CServer1 =
- dialyzer_codeserver:store_temp_records(Mod, RecInfo, CServer),
- case
- dialyzer_utils:get_fun_meta_info(Mod, AbstrCode, LegalWarnings)
- of
- {error, _} = Error -> Error;
- MetaFunInfo ->
- CServer2 =
- dialyzer_codeserver:insert_fun_meta_info(MetaFunInfo, CServer1),
- case UseContracts of
- true ->
- case dialyzer_utils:get_spec_info(Mod, AbstrCode, RecInfo) of
- {error, _} = Error -> Error;
- {ok, SpecInfo, CallbackInfo} ->
- CServer3 =
- dialyzer_codeserver:store_temp_contracts(Mod, SpecInfo,
- CallbackInfo,
- CServer2),
- store_core(Mod, Core, Callgraph, CServer3)
- end;
- false ->
- store_core(Mod, Core, Callgraph, CServer2)
- end
- end
+ MetaFunInfo ->
+ CServer2 =
+ dialyzer_codeserver:insert_fun_meta_info(MetaFunInfo, CServer1),
+ case UseContracts of
+ true ->
+ case dialyzer_utils:get_spec_info(Mod, Core, RecInfo) of
+ {error, _} = Error -> Error;
+ {ok, SpecInfo, CallbackInfo} ->
+ CServer3 =
+ dialyzer_codeserver:store_temp_contracts(Mod, SpecInfo,
+ CallbackInfo,
+ CServer2),
+ store_core(Mod, Core, Callgraph, CServer3)
+ end;
+ false ->
+ store_core(Mod, Core, Callgraph, CServer2)
end
+ end
end.
store_core(Mod, Core, Callgraph, CServer) ->
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index bfd3f84fc5..847faab2f4 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -470,12 +470,11 @@ compute_md5_from_file(File) ->
Msg = io_lib:format("Not a regular file: ~s\n", [File]),
throw({dialyzer_error, Msg});
true ->
- case dialyzer_utils:get_abstract_code_from_beam(File) of
- error ->
- Msg = io_lib:format("Could not get abstract code for file: ~s (please recompile it with +debug_info)\n", [File]),
- throw({dialyzer_error, Msg});
- {ok, Abs} ->
- erlang:md5(term_to_binary(Abs))
+ case dialyzer_utils:get_core_from_beam(File) of
+ {error, Error} ->
+ throw({dialyzer_error, Error});
+ {ok, Core} ->
+ erlang:md5(term_to_binary(Core))
end
end.
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 9eaf95c1a2..e0497159b3 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -24,14 +24,10 @@
-export([
format_sig/1,
format_sig/2,
- get_abstract_code_from_beam/1,
- get_compile_options_from_beam/1,
- get_abstract_code_from_src/1,
- get_abstract_code_from_src/2,
- get_core_from_abstract_code/1,
- get_core_from_abstract_code/2,
get_core_from_src/1,
get_core_from_src/2,
+ get_core_from_beam/1,
+ get_core_from_beam/2,
get_record_and_type_info/1,
get_spec_info/3,
get_fun_meta_info/3,
@@ -77,9 +73,7 @@ print_types1([{record, _Name} = Key|T], RecDict) ->
%% ----------------------------------------------------------------------------
--type abstract_code() :: [erl_parse:abstract_form()].
-type comp_options() :: [compile:option()].
--type mod_or_fname() :: module() | file:filename().
-type fa() :: {atom(), arity()}.
-type codeserver() :: dialyzer_codeserver:codeserver().
@@ -89,63 +83,82 @@ print_types1([{record, _Name} = Key|T], RecDict) ->
%%
%% ============================================================================
--spec get_abstract_code_from_src(mod_or_fname()) ->
- {'ok', abstract_code()} | {'error', [string()]}.
+-type get_core_from_src_ret() :: {'ok', cerl:c_module()} | {'error', string()}.
+
+-spec get_core_from_src(file:filename()) -> get_core_from_src_ret().
-get_abstract_code_from_src(File) ->
- get_abstract_code_from_src(File, src_compiler_opts()).
+get_core_from_src(File) ->
+ get_core_from_src(File, []).
--spec get_abstract_code_from_src(mod_or_fname(), comp_options()) ->
- {'ok', abstract_code()} | {'error', [string()]}.
+-spec get_core_from_src(file:filename(), comp_options()) -> get_core_from_src_ret().
-get_abstract_code_from_src(File, Opts) ->
- case compile:noenv_file(File, [to_pp, binary|Opts]) of
+get_core_from_src(File, Opts) ->
+ case compile:noenv_file(File, Opts ++ src_compiler_opts()) of
error -> {error, []};
{error, Errors, _} -> {error, format_errors(Errors)};
- {ok, _, AbstrCode} -> {ok, AbstrCode}
+ {ok, _, Core} -> {ok, Core}
end.
--type get_core_from_src_ret() :: {'ok', cerl:c_module()} | {'error', string()}.
+-type get_core_from_beam_ret() :: {'ok', cerl:c_module()} | {'error', string()}.
--spec get_core_from_src(file:filename()) -> get_core_from_src_ret().
+-spec get_core_from_beam(file:filename()) -> get_core_from_beam_ret().
-get_core_from_src(File) ->
- get_core_from_src(File, []).
+get_core_from_beam(File) ->
+ get_core_from_beam(File, []).
--spec get_core_from_src(file:filename(), comp_options()) -> get_core_from_src_ret().
+-spec get_core_from_beam(file:filename(), comp_options()) -> get_core_from_beam_ret().
-get_core_from_src(File, Opts) ->
- case get_abstract_code_from_src(File, Opts) of
- {error, _} = Error -> Error;
+get_core_from_beam(File, Opts) ->
+ case beam_lib:chunks(File, [debug_info]) of
+ {ok, {Module, [{debug_info, {debug_info_v1, Backend, Metadata}}]}} ->
+ case Backend:debug_info(core_v1, Module, Metadata, Opts ++ src_compiler_opts()) of
+ {ok, Core} ->
+ {ok, Core};
+ {error, _} ->
+ {error, " Could not get Core Erlang code for: " ++ File ++ "\n"}
+ end;
+ _ ->
+ deprecated_get_core_from_beam(File, Opts)
+ end.
+
+deprecated_get_core_from_beam(File, Opts) ->
+ case get_abstract_code_from_beam(File) of
+ error ->
+ {error, " Could not get abstract code for: " ++ File ++ "\n" ++
+ " Recompile with +debug_info or analyze starting from source code"};
{ok, AbstrCode} ->
- case get_core_from_abstract_code(AbstrCode, Opts) of
- error -> {error, " Could not get Core Erlang code from abstract code"};
- {ok, _Core} = C -> C
+ case get_compile_options_from_beam(File) of
+ error ->
+ {error, " Could not get compile options for: " ++ File ++ "\n" ++
+ " Recompile or analyze starting from source code"};
+ {ok, CompOpts} ->
+ case get_core_from_abstract_code(AbstrCode, Opts ++ CompOpts) of
+ error ->
+ {error, " Could not get core Erlang code for: " ++ File};
+ {ok, _} = Core ->
+ Core
+ end
end
end.
--spec get_abstract_code_from_beam(file:filename()) -> 'error' | {'ok', abstract_code()}.
-
get_abstract_code_from_beam(File) ->
case beam_lib:chunks(File, [abstract_code]) of
{ok, {_, List}} ->
case lists:keyfind(abstract_code, 1, List) of
- {abstract_code, {raw_abstract_v1, Abstr}} -> {ok, Abstr};
- _ -> error
+ {abstract_code, {raw_abstract_v1, Abstr}} -> {ok, Abstr};
+ _ -> error
end;
_ ->
%% No or unsuitable abstract code.
error
end.
--spec get_compile_options_from_beam(file:filename()) -> 'error' | {'ok', [compile:option()]}.
-
get_compile_options_from_beam(File) ->
case beam_lib:chunks(File, [compile_info]) of
{ok, {_, List}} ->
case lists:keyfind(compile_info, 1, List) of
- {compile_info, CompInfo} -> compile_info_to_options(CompInfo);
- _ -> error
+ {compile_info, CompInfo} -> compile_info_to_options(CompInfo);
+ _ -> error
end;
_ ->
%% No or unsuitable compile info.
@@ -158,15 +171,6 @@ compile_info_to_options(CompInfo) ->
_ -> error
end.
--type get_core_from_abs_ret() :: {'ok', cerl:c_module()} | 'error'.
-
--spec get_core_from_abstract_code(abstract_code()) -> get_core_from_abs_ret().
-
-get_core_from_abstract_code(AbstrCode) ->
- get_core_from_abstract_code(AbstrCode, []).
-
--spec get_core_from_abstract_code(abstract_code(), comp_options()) -> get_core_from_abs_ret().
-
get_core_from_abstract_code(AbstrCode, Opts) ->
%% We do not want the parse_transforms around since we already
%% performed them. In some cases we end up in trouble when
@@ -175,12 +179,31 @@ get_core_from_abstract_code(AbstrCode, Opts) ->
%% Remove parse_transforms (and other options) from compile options.
Opts2 = cleanup_compile_options(Opts),
try compile:noenv_forms(AbstrCode1, Opts2 ++ src_compiler_opts()) of
- {ok, _, Core} -> {ok, Core};
- _What -> error
+ {ok, _, Core} -> {ok, Core};
+ _What -> error
catch
error:_ -> error
end.
+cleanup_parse_transforms([{attribute, _, compile, {parse_transform, _}}|Left]) ->
+ cleanup_parse_transforms(Left);
+cleanup_parse_transforms([Other|Left]) ->
+ [Other|cleanup_parse_transforms(Left)];
+cleanup_parse_transforms([]) ->
+ [].
+
+cleanup_compile_options(Opts) ->
+ lists:filter(fun keep_compile_option/1, Opts).
+
+%% Using abstract, not asm or core.
+keep_compile_option(from_asm) -> false;
+keep_compile_option(from_core) -> false;
+%% The parse transform will already have been applied, may cause
+%% problems if it is re-applied.
+keep_compile_option({parse_transform, _}) -> false;
+keep_compile_option(warnings_as_errors) -> false;
+keep_compile_option(_) -> true.
+
%% ============================================================================
%%
%% Typed Records
@@ -189,55 +212,50 @@ get_core_from_abstract_code(AbstrCode, Opts) ->
-type type_table() :: erl_types:type_table().
--spec get_record_and_type_info(abstract_code()) ->
- {'ok', type_table()} | {'error', string()}.
+-spec get_record_and_type_info(cerl:module()) ->
+ {'ok', type_table()} | {'error', string()}.
-get_record_and_type_info(AbstractCode) ->
- Module = get_module(AbstractCode),
- get_record_and_type_info(AbstractCode, Module, maps:new()).
+get_record_and_type_info(Core) ->
+ Module = cerl:concrete(cerl:module_name(Core)),
+ Tuples = core_to_attr_tuples(Core),
+ get_record_and_type_info(Tuples, Module, maps:new(), "nofile").
--spec get_record_and_type_info(abstract_code(), module(), type_table()) ->
- {'ok', type_table()} | {'error', string()}.
-
-get_record_and_type_info(AbstractCode, Module, RecDict) ->
- get_record_and_type_info(AbstractCode, Module, RecDict, "nofile").
-
-get_record_and_type_info([{attribute, A, record, {Name, Fields0}}|Left],
+get_record_and_type_info([{record, Line, [{Name, Fields0}]}|Left],
Module, RecDict, File) ->
{ok, Fields} = get_record_fields(Fields0, RecDict),
Arity = length(Fields),
- FN = {File, erl_anno:line(A)},
+ FN = {File, Line},
NewRecDict = maps:put({record, Name}, {FN, [{Arity,Fields}]}, RecDict),
get_record_and_type_info(Left, Module, NewRecDict, File);
-get_record_and_type_info([{attribute, A, type, {{record, Name}, Fields0, []}}
+get_record_and_type_info([{type, Line, [{{record, Name}, Fields0, []}]}
|Left], Module, RecDict, File) ->
%% This overrides the original record declaration.
{ok, Fields} = get_record_fields(Fields0, RecDict),
Arity = length(Fields),
- FN = {File, erl_anno:line(A)},
+ FN = {File, Line},
NewRecDict = maps:put({record, Name}, {FN, [{Arity, Fields}]}, RecDict),
get_record_and_type_info(Left, Module, NewRecDict, File);
-get_record_and_type_info([{attribute, A, Attr, {Name, TypeForm}}|Left],
+get_record_and_type_info([{Attr, Line, [{Name, TypeForm}]}|Left],
Module, RecDict, File)
when Attr =:= 'type'; Attr =:= 'opaque' ->
- FN = {File, erl_anno:line(A)},
+ FN = {File, Line},
try add_new_type(Attr, Name, TypeForm, [], Module, FN, RecDict) of
NewRecDict ->
get_record_and_type_info(Left, Module, NewRecDict, File)
catch
throw:{error, _} = Error -> Error
end;
-get_record_and_type_info([{attribute, A, Attr, {Name, TypeForm, Args}}|Left],
+get_record_and_type_info([{Attr, Line, [{Name, TypeForm, Args}]}|Left],
Module, RecDict, File)
when Attr =:= 'type'; Attr =:= 'opaque' ->
- FN = {File, erl_anno:line(A)},
+ FN = {File, Line},
try add_new_type(Attr, Name, TypeForm, Args, Module, FN, RecDict) of
NewRecDict ->
get_record_and_type_info(Left, Module, NewRecDict, File)
catch
throw:{error, _} = Error -> Error
end;
-get_record_and_type_info([{attribute, _, file, {IncludeFile, _}}|Left],
+get_record_and_type_info([{file, _, [{IncludeFile, _}]}|Left],
Module, RecDict, _File) ->
get_record_and_type_info(Left, Module, RecDict, IncludeFile);
get_record_and_type_info([_Other|Left], Module, RecDict, File) ->
@@ -460,23 +478,18 @@ merge_types(CServer, Plt) ->
-type spec_map() :: dialyzer_codeserver:contracts().
-type callback_map() :: dialyzer_codeserver:contracts().
--spec get_spec_info(module(), abstract_code(), type_table()) ->
+-spec get_spec_info(module(), cerl:module(), type_table()) ->
{'ok', spec_map(), callback_map()} | {'error', string()}.
-get_spec_info(ModName, AbstractCode, RecordsMap) ->
- OptionalCallbacks0 = get_optional_callbacks(AbstractCode, ModName),
+get_spec_info(ModName, Core, RecordsMap) ->
+ Tuples = core_to_attr_tuples(Core),
+ OptionalCallbacks0 = get_optional_callbacks(Tuples, ModName),
OptionalCallbacks = gb_sets:from_list(OptionalCallbacks0),
- get_spec_info(AbstractCode, maps:new(), maps:new(),
+ get_spec_info(Tuples, maps:new(), maps:new(),
RecordsMap, ModName, OptionalCallbacks, "nofile").
-get_optional_callbacks(Abs, ModName) ->
- [{ModName, F, A} || {F, A} <- get_optional_callbacks(Abs)].
-
-get_optional_callbacks(Abs) ->
- L = [O ||
- {attribute, _, optional_callbacks, O} <- Abs,
- is_fa_list(O)],
- lists:append(L).
+get_optional_callbacks(Tuples, ModName) ->
+ [{ModName, F, A} || {optional_callbacks, _, O} <- Tuples, is_fa_list(O), {F, A} <- O].
%% TypeSpec is a list of conditional contracts for a function.
%% Each contract is of the form {[Argument], Range, [Constraint]} where
@@ -484,11 +497,10 @@ get_optional_callbacks(Abs) ->
%% - Constraint is of the form {subtype, T1, T2} where T1 and T2
%% are erl_types:erl_type()
-get_spec_info([{attribute, Anno, Contract, {Id, TypeSpec}}|Left],
+get_spec_info([{Contract, Ln, [{Id, TypeSpec}]}|Left],
SpecMap, CallbackMap, RecordsMap, ModName, OptCb, File)
when ((Contract =:= 'spec') or (Contract =:= 'callback')),
is_list(TypeSpec) ->
- Ln = erl_anno:line(Anno),
MFA = case Id of
{_, _, _} = T -> T;
{F, A} -> {ModName, F, A}
@@ -523,7 +535,7 @@ get_spec_info([{attribute, Anno, Contract, {Id, TypeSpec}}|Left],
{error, flat_format(" Error while parsing contract in line ~w: ~s\n",
[Ln, Error])}
end;
-get_spec_info([{attribute, _, file, {IncludeFile, _}}|Left],
+get_spec_info([{file, _, [{IncludeFile, _}]}|Left],
SpecMap, CallbackMap, RecordsMap, ModName, OptCb, _File) ->
get_spec_info(Left, SpecMap, CallbackMap,
RecordsMap, ModName, OptCb, IncludeFile);
@@ -535,15 +547,25 @@ get_spec_info([], SpecMap, CallbackMap,
_RecordsMap, _ModName, _OptCb, _File) ->
{ok, SpecMap, CallbackMap}.
--spec get_fun_meta_info(module(), abstract_code(), [dial_warn_tag()]) ->
+core_to_attr_tuples(Core) ->
+ [{cerl:concrete(Key), get_core_line(cerl:get_ann(Key)), cerl:concrete(Value)} ||
+ {Key, Value} <- cerl:module_attrs(Core)].
+
+get_core_line([L | _As]) when is_integer(L) -> L;
+get_core_line([_ | As]) -> get_core_line(As);
+get_core_line([]) -> undefined.
+
+-spec get_fun_meta_info(module(), cerl:module(), [dial_warn_tag()]) ->
dialyzer_codeserver:fun_meta_info() | {'error', string()}.
-get_fun_meta_info(M, Abs, LegalWarnings) ->
+get_fun_meta_info(M, Core, LegalWarnings) ->
+ Functions = lists:map(fun cerl:var_name/1, cerl:module_vars(Core)),
try
- {get_nowarn_unused_function(M, Abs), get_func_suppressions(M, Abs)}
+ {get_nowarn_unused_function(M, Core, Functions),
+ get_func_suppressions(M, Core, Functions)}
of
{NoWarn, FuncSupp} ->
- Warnings0 = get_options(Abs, LegalWarnings),
+ Warnings0 = get_options(Core, LegalWarnings),
Warnings = ordsets:to_list(Warnings0),
ModuleWarnings = [{M, W} || W <- Warnings],
RawProps = lists:append([NoWarn, FuncSupp, ModuleWarnings]),
@@ -569,49 +591,45 @@ process_options([{{_M, _F, _A}=MFA, Opts}|Left], Warnings) ->
end;
process_options([], _Warnings) -> [].
--spec get_nowarn_unused_function(module(), abstract_code()) ->
+-spec get_nowarn_unused_function(module(), cerl:module(), [fa()]) ->
[{mfa(), 'no_unused'}].
-get_nowarn_unused_function(M, Abs) ->
- Opts = get_options_with_tag(compile, Abs),
+get_nowarn_unused_function(M, Core, Functions) ->
+ Opts = get_options_with_tag(compile, Core),
Warn = erl_lint:bool_option(warn_unused_function, nowarn_unused_function,
true, Opts),
- Functions = [{F, A} || {function, _, F, A, _} <- Abs],
- AttrFile = collect_attribute(Abs, compile),
+ AttrFile = collect_attribute(Core, compile),
TagsFaList = check_fa_list(AttrFile, nowarn_unused_function, Functions),
FAs = case Warn of
false -> Functions;
true ->
- [FA || {{nowarn_unused_function,_L,_File}, FA} <- TagsFaList]
+ [FA || {{[nowarn_unused_function],_L,_File}, FA} <- TagsFaList]
end,
[{{M, F, A}, no_unused} || {F, A} <- FAs].
--spec get_func_suppressions(module(), abstract_code()) ->
+-spec get_func_suppressions(module(), cerl:module(), [fa()]) ->
[{mfa(), 'nowarn_function' | dial_warn_tag()}].
-get_func_suppressions(M, Abs) ->
- Functions = [{F, A} || {function, _, F, A, _} <- Abs],
- AttrFile = collect_attribute(Abs, dialyzer),
+get_func_suppressions(M, Core, Functions) ->
+ AttrFile = collect_attribute(Core, dialyzer),
TagsFAs = check_fa_list(AttrFile, '*', Functions),
%% Check the options:
- Fun = fun({{nowarn_function, _L, _File}, _FA}) -> ok;
+ Fun = fun({{[nowarn_function], _L, _File}, _FA}) -> ok;
({OptLFile, _FA}) ->
_ = get_options1([OptLFile], ordsets:new())
end,
lists:foreach(Fun, TagsFAs),
- [{{M, F, A}, W} || {{W, _L, _File}, {F, A}} <- TagsFAs].
+ [{{M, F, A}, W} || {{Warnings, _L, _File}, {F, A}} <- TagsFAs, W <- Warnings].
--spec get_options(abstract_code(), [dial_warn_tag()]) ->
+-spec get_options(cerl:module(), [dial_warn_tag()]) ->
ordsets:ordset(dial_warn_tag()).
-get_options(Abs, LegalWarnings) ->
- AttrFile = collect_attribute(Abs, dialyzer),
+get_options(Core, LegalWarnings) ->
+ AttrFile = collect_attribute(Core, dialyzer),
get_options1(AttrFile, LegalWarnings).
get_options1([{Args, L, File}|Left], Warnings) ->
- Opts = [O ||
- O <- lists:flatten([Args]),
- is_atom(O)],
+ Opts = [O || O <- Args, is_atom(O)],
try dialyzer_options:build_warnings(Opts, Warnings) of
NewWarnings ->
get_options1(Left, NewWarnings)
@@ -624,19 +642,24 @@ get_options1([], Warnings) ->
Warnings.
-type collected_attribute() ::
- {Args :: term(), erl_anno:line(), file:filename()}.
-
-collect_attribute(Abs, Tag) ->
- collect_attribute(Abs, Tag, "nofile").
-
-collect_attribute([{attribute, L, Tag, Args}|Left], Tag, File) ->
- CollAttr = {Args, L, File},
- [CollAttr | collect_attribute(Left, Tag, File)];
-collect_attribute([{attribute, _, file, {IncludeFile, _}}|Left], Tag, _) ->
- collect_attribute(Left, Tag, IncludeFile);
-collect_attribute([_Other|Left], Tag, File) ->
- collect_attribute(Left, Tag, File);
-collect_attribute([], _Tag, _File) -> [].
+ {Args :: [term()], erl_anno:line(), file:filename()}.
+
+collect_attribute(Core, Tag) ->
+ collect_attribute(cerl:module_attrs(Core), Tag, "nofile").
+
+collect_attribute([{Key, Value}|T], Tag, File) ->
+ case cerl:concrete(Key) of
+ Tag ->
+ [{cerl:concrete(Value), get_core_line(cerl:get_ann(Key)), File} |
+ collect_attribute(T, Tag, File)];
+ file ->
+ [{IncludeFile, _}] = cerl:concrete(Value),
+ collect_attribute(T, Tag, IncludeFile);
+ _ ->
+ collect_attribute(T, Tag, File)
+ end;
+collect_attribute([], _Tag, _File) ->
+ [].
-spec is_suppressed_fun(mfa(), codeserver()) -> boolean().
@@ -687,35 +710,6 @@ src_compiler_opts() ->
no_inline, strict_record_tests, strict_record_updates,
dialyzer].
--spec get_module(abstract_code()) -> module().
-
-get_module([{attribute, _, module, {M, _As}} | _]) -> M;
-get_module([{attribute, _, module, M} | _]) -> M;
-get_module([_ | Rest]) -> get_module(Rest).
-
--spec cleanup_parse_transforms(abstract_code()) -> abstract_code().
-
-cleanup_parse_transforms([{attribute, _, compile, {parse_transform, _}}|Left]) ->
- cleanup_parse_transforms(Left);
-cleanup_parse_transforms([Other|Left]) ->
- [Other|cleanup_parse_transforms(Left)];
-cleanup_parse_transforms([]) ->
- [].
-
--spec cleanup_compile_options([compile:option()]) -> [compile:option()].
-
-cleanup_compile_options(Opts) ->
- lists:filter(fun keep_compile_option/1, Opts).
-
-%% Using abstract, not asm or core.
-keep_compile_option(from_asm) -> false;
-keep_compile_option(from_core) -> false;
-%% The parse transform will already have been applied, may cause
-%% problems if it is re-applied.
-keep_compile_option({parse_transform, _}) -> false;
-keep_compile_option(warnings_as_errors) -> false;
-keep_compile_option(_) -> true.
-
-spec format_errors([{module(), string()}]) -> [string()].
format_errors([{Mod, Errors}|Left]) ->
@@ -741,10 +735,12 @@ format_sig(Type, RecDict) ->
flat_format(Fmt, Lst) ->
lists:flatten(io_lib:format(Fmt, Lst)).
--spec get_options_with_tag(atom(), abstract_code()) -> [term()].
+-spec get_options_with_tag(atom(), cerl:module()) -> [term()].
-get_options_with_tag(Tag, Abs) ->
- lists:flatten([O || {attribute, _, Tag0, O} <- Abs, Tag =:= Tag0]).
+get_options_with_tag(Tag, Core) ->
+ [O || {Key, Value} <- cerl:module_attrs(Core),
+ cerl:concrete(Key) =:= Tag,
+ O <- cerl:concrete(Value)].
%% Check F/A, and collect (unchecked) warning tags with line and file.
-spec check_fa_list([collected_attribute()], atom(), [fa()]) ->
@@ -755,8 +751,8 @@ check_fa_list(AttrFile, Tag, Functions) ->
check_fa_list1(AttrFile, Tag, FuncTab).
check_fa_list1([{Args, L, File}|Left], Tag, Funcs) ->
- TermsL = [{{Tag0, L, File}, Term} ||
- {Tags, Terms0} <- lists:flatten([Args]),
+ TermsL = [{{[Tag0], L, File}, Term} ||
+ {Tags, Terms0} <- Args,
Tag0 <- lists:flatten([Tags]),
Tag =:= '*' orelse Tag =:= Tag0,
Term <- lists:flatten([Terms0])],
diff --git a/lib/dialyzer/src/typer.erl b/lib/dialyzer/src/typer.erl
index 18c4fe902d..86c7c62f98 100644
--- a/lib/dialyzer/src/typer.erl
+++ b/lib/dialyzer/src/typer.erl
@@ -109,12 +109,12 @@ extract(#analysis{macros = Macros,
AllIncludes = [filename:dirname(filename:dirname(File)) | Includes],
Is = [{i, Dir} || Dir <- AllIncludes],
CompOpts = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
- case dialyzer_utils:get_abstract_code_from_src(File, CompOpts) of
- {ok, AbstractCode} ->
- case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ case dialyzer_utils:get_core_from_src(File, CompOpts) of
+ {ok, Core} ->
+ case dialyzer_utils:get_record_and_type_info(Core) of
{ok, RecDict} ->
Mod = list_to_atom(filename:basename(File, ".erl")),
- case dialyzer_utils:get_spec_info(Mod, AbstractCode, RecDict) of
+ case dialyzer_utils:get_spec_info(Mod, Core, RecDict) of
{ok, SpecDict, CbDict} ->
CS1 = dialyzer_codeserver:store_temp_records(Mod, RecDict, CS),
dialyzer_codeserver:store_temp_contracts(Mod, SpecDict, CbDict, CS1);
@@ -846,26 +846,22 @@ collect_one_file_info(File, Analysis) ->
Includes = [filename:dirname(File)|Analysis#analysis.includes],
Is = [{i,Dir} || Dir <- Includes],
Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
- case dialyzer_utils:get_abstract_code_from_src(File, Options) of
+ case dialyzer_utils:get_core_from_src(File, Options) of
{error, Reason} ->
%% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
compile_error(Reason);
- {ok, AbstractCode} ->
- case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
- error -> compile_error(["Could not get core erlang for "++File]);
- {ok, Core} ->
- case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ {ok, Core} ->
+ case dialyzer_utils:get_record_and_type_info(Core) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, Records} ->
+ Mod = cerl:concrete(cerl:module_name(Core)),
+ case dialyzer_utils:get_spec_info(Mod, Core, Records) of
{error, Reason} -> compile_error([Reason]);
- {ok, Records} ->
- Mod = cerl:concrete(cerl:module_name(Core)),
- case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
- {error, Reason} -> compile_error([Reason]);
- {ok, SpecInfo, CbInfo} ->
- ExpTypes = get_exported_types_from_core(Core),
- analyze_core_tree(Core, Records, SpecInfo, CbInfo,
- ExpTypes, Analysis, File)
- end
- end
+ {ok, SpecInfo, CbInfo} ->
+ ExpTypes = get_exported_types_from_core(Core),
+ analyze_core_tree(Core, Records, SpecInfo, CbInfo,
+ ExpTypes, Analysis, File)
+ end
end
end.
diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml
index d5ec90b060..031d79d0e2 100644
--- a/lib/stdlib/doc/src/beam_lib.xml
+++ b/lib/stdlib/doc/src/beam_lib.xml
@@ -42,10 +42,10 @@
and the corresponding identifiers are as follows:</p>
<list type="bulleted">
- <item><c>abstract_code ("Abst")</c></item>
<item><c>atoms ("Atom")</c></item>
<item><c>attributes ("Attr")</c></item>
<item><c>compile_info ("CInf")</c></item>
+ <item><c>debug_info ("Dbgi")</c></item>
<item><c>exports ("ExpT")</c></item>
<item><c>imports ("ImpT")</c></item>
<item><c>indexed_imports ("ImpT")</c></item>
@@ -60,9 +60,8 @@
<title>Debug Information/Abstract Code</title>
<p>Option <c>debug_info</c> can be specified to the Compiler (see
<seealso marker="compiler:compile#debug_info"><c>compile(3)</c></seealso>)
- to have debug information in the form of abstract code (see section
- <seealso marker="erts:absform">The Abstract Format</seealso> in the
- ERTS User's Guide) stored in the <c>abstract_code</c> chunk.
+ to have debug information, such as <seealso marker="erts:absform">Erlang
+ Abstract Format</seealso>, stored in the <c>debug_info</c> chunk.
Tools such as Debugger and Xref require the debug information to
be included.</p>
@@ -79,7 +78,7 @@
<section>
<title>Reconstruct Source Code</title>
- <p>The following example shows how to reconstruct source code from
+ <p>The following example shows how to reconstruct Erlang source code from
the debug information in a BEAM file <c>Beam</c>:</p>
<code type="none">
@@ -117,7 +116,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<list type="ordered">
<item>
- <p>Use Compiler option <c>{debug_info,Key}</c>, see
+ <p>Use Compiler option <c>{debug_info_key,Key}</c>, see
<seealso marker="compiler:compile#debug_info_key"><c>compile(3)</c></seealso>
and function
<seealso marker="#crypto_key_fun/1"><c>crypto_key_fun/1</c></seealso>
@@ -198,18 +197,40 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<datatype>
<name name="chunkid"/>
<desc>
- <p>"Abst" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom"</p>
+ <p>"Attr" | "CInf" | "Dbgi" | "ExpT" | "ImpT" | "LocT" | "AtU8"</p>
</desc>
</datatype>
<datatype>
<name name="dataB"/>
</datatype>
<datatype>
+ <name name="debug_info"/>
+ <desc>
+ <p>The format stored in the <c>debug_info</c> chunk.
+ To retrieve particular code representation from the backend,
+ <c>Backend:debug_info(Format, Module, Data, Opts)</c> must be
+ invoked. <c>Format</c> is an atom, such as <c>erlang_v1</c> for
+ the Erlang Abstract Format or <c>core_v1</c> for Core Erlang.
+ <c>Module</c> is the module represented by the beam file and
+ <c>Data</c> is the value stored in the debug info chunk.
+ <c>Opts</c> is any list of values supported by the <c>Backend</c>.
+ <c>Backend:debug_info/4</c> must return <c>{ok, Code}</c> or
+ <c>{error, Term}</c>.</p>
+
+ <p>Developers must always invoke the <c>debug_info/4</c> function
+ and never rely on the <c>Data</c> stored in the <c>debug_info</c>
+ chunk, as it is opaque and may change at any moment. <c>no_debug_info</c>
+ means that chunk <c>"Dbgi"</c> is present, but empty.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="abst_code"/>
<desc>
<p>It is not checked that the forms conform to the abstract format
indicated by <c><anno>AbstVersion</anno></c>. <c>no_abstract_code</c>
means that chunk <c>"Abst"</c> is present, but empty.</p>
+ <p>For modules compiled with OTP 20 onwards, the <c>abst_code</c> chunk
+ is automatically computed from the <c>debug_info</c> chunk.</p>
</desc>
</datatype>
<datatype>
@@ -346,7 +367,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<desc>
<p>Registers an unary fun
that is called if <c>beam_lib</c> must read an
- <c>abstract_code</c> chunk that has been encrypted. The fun
+ <c>debug_info</c> chunk that has been encrypted. The fun
is held in a process that is started by the function.</p>
<p>If a fun is already registered when attempting to
register a fun, <c>{error, exists}</c> is returned.</p>
@@ -443,7 +464,8 @@ CryptoKeyFun(clear) -> term()</code>
<desc>
<p>Removes all chunks from a BEAM
file except those needed by the loader. In particular,
- the debug information (chunk <c>abstract_code</c>) is removed.</p>
+ the debug information (chunk <c>debug_info</c> and <c>abstract_code</c>)
+ is removed.</p>
</desc>
</func>
@@ -454,9 +476,9 @@ CryptoKeyFun(clear) -> term()</code>
<desc>
<p>Removes all chunks except
those needed by the loader from BEAM files. In particular,
- the debug information (chunk <c>abstract_code</c>) is removed.
- The returned list contains one element for each specified filename,
- in the same order as in <c>Files</c>.</p>
+ the debug information (chunk <c>debug_info</c> and <c>abstract_code</c>)
+ is removed. The returned list contains one element for each
+ specified filename, in the same order as in <c>Files</c>.</p>
</desc>
</func>
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index ed3dfb342c..0cbcaa0a58 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -58,6 +58,7 @@ MODULES= \
edlin \
edlin_expand \
epp \
+ erl_abstract_code \
erl_anno \
erl_bits \
erl_compile \
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 461acf03be..9e5e7b2e7e 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -54,6 +54,7 @@
%%-------------------------------------------------------------------------
-type beam() :: module() | file:filename() | binary().
+-type debug_info() :: {DbgiVersion :: atom(), Backend :: module(), Data :: term()} | 'no_debug_info'.
-type forms() :: [erl_parse:abstract_form() | erl_parse:form_info()].
@@ -63,8 +64,9 @@
-type label() :: integer().
-type chunkid() :: nonempty_string(). % approximation of the strings below
-%% "Abst" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom" | "AtU8".
--type chunkname() :: 'abstract_code' | 'attributes' | 'compile_info'
+%% "Abst" | "Dbgi" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom" | "AtU8".
+-type chunkname() :: 'abstract_code' | 'debug_info'
+ | 'attributes' | 'compile_info'
| 'exports' | 'labeled_exports'
| 'imports' | 'indexed_imports'
| 'locals' | 'labeled_locals'
@@ -77,6 +79,7 @@
-type chunkdata() :: {chunkid(), dataB()}
| {'abstract_code', abst_code()}
+ | {'debug_info', debug_info()}
| {'attributes', [attrib_entry()]}
| {'compile_info', [compinfo_entry()]}
| {'exports', [{atom(), arity()}]}
@@ -99,7 +102,7 @@
| {'file_error', file:filename(), file:posix()}.
-type chnk_rsn() :: {'unknown_chunk', file:filename(), atom()}
| {'key_missing_or_invalid', file:filename(),
- 'abstract_code'}
+ 'abstract_code' | 'debug_info'}
| info_rsn().
-type cmp_rsn() :: {'modules_different', module(), module()}
| {'chunks_different', chunkid()}
@@ -267,9 +270,9 @@ format_error({modules_different, Module1, Module2}) ->
[Module1, Module2]);
format_error({not_a_directory, Name}) ->
io_lib:format("~tp: Not a directory~n", [Name]);
-format_error({key_missing_or_invalid, File, abstract_code}) ->
- io_lib:format("~tp: Cannot decrypt abstract code because key is missing or invalid",
- [File]);
+format_error({key_missing_or_invalid, File, ChunkId}) ->
+ io_lib:format("~tp: Cannot decrypt ~ts because key is missing or invalid",
+ [File, ChunkId]);
format_error(badfun) ->
"not a fun or the fun has the wrong arity";
format_error(exists) ->
@@ -510,9 +513,9 @@ read_chunk_data(File0, ChunkNames) ->
read_chunk_data(File0, ChunkNames0, Options)
when is_atom(File0); is_list(File0); is_binary(File0) ->
File = beam_filename(File0),
- {ChunkIds, Names} = check_chunks(ChunkNames0, File, [], []),
+ {ChunkIds, Names, Optional} = check_chunks(ChunkNames0, File, [], [], []),
AllowMissingChunks = member(allow_missing_chunks, Options),
- {ok, Module, Chunks} = scan_beam(File, ChunkIds, AllowMissingChunks),
+ {ok, Module, Chunks} = scan_beam(File, ChunkIds, AllowMissingChunks, Optional),
AT = ets:new(beam_symbols, []),
T = {empty, AT},
try chunks_to_data(Names, Chunks, File, Chunks, Module, T, [])
@@ -520,31 +523,34 @@ read_chunk_data(File0, ChunkNames0, Options)
end.
%% -> {ok, list()} | throw(Error)
-check_chunks([atoms | Ids], File, IL, L) ->
- check_chunks(Ids, File, ["Atom", "AtU8" | IL], [{atom_chunk, atoms} | L]);
-check_chunks([ChunkName | Ids], File, IL, L) when is_atom(ChunkName) ->
+check_chunks([atoms | Ids], File, IL, L, O) ->
+ check_chunks(Ids, File, ["Atom", "AtU8" | IL],
+ [{atom_chunk, atoms} | L], ["Atom", "AtU8" | O]);
+check_chunks([abstract_code | Ids], File, IL, L, O) ->
+ check_chunks(Ids, File, ["Abst", "Dbgi" | IL],
+ [{abst_chunk, abstract_code} | L], ["Abst", "Dbgi" | O]);
+check_chunks([ChunkName | Ids], File, IL, L, O) when is_atom(ChunkName) ->
ChunkId = chunk_name_to_id(ChunkName, File),
- check_chunks(Ids, File, [ChunkId | IL], [{ChunkId, ChunkName} | L]);
-check_chunks([ChunkId | Ids], File, IL, L) -> % when is_list(ChunkId)
- check_chunks(Ids, File, [ChunkId | IL], [{ChunkId, ChunkId} | L]);
-check_chunks([], _File, IL, L) ->
- {lists:usort(IL), reverse(L)}.
+ check_chunks(Ids, File, [ChunkId | IL], [{ChunkId, ChunkName} | L], O);
+check_chunks([ChunkId | Ids], File, IL, L, O) -> % when is_list(ChunkId)
+ check_chunks(Ids, File, [ChunkId | IL], [{ChunkId, ChunkId} | L], O);
+check_chunks([], _File, IL, L, O) ->
+ {lists:usort(IL), reverse(L), O}.
%% -> {ok, Module, Data} | throw(Error)
scan_beam(File, What) ->
- scan_beam(File, What, false).
+ scan_beam(File, What, false, []).
%% -> {ok, Module, Data} | throw(Error)
-scan_beam(File, What0, AllowMissingChunks) ->
+scan_beam(File, What0, AllowMissingChunks, OptionalChunks) ->
case scan_beam1(File, What0) of
{missing, _FD, Mod, Data, What} when AllowMissingChunks ->
{ok, Mod, [{Id, missing_chunk} || Id <- What] ++ Data};
- {missing, _FD, Mod, Data, ["Atom"]} ->
- {ok, Mod, Data};
- {missing, _FD, Mod, Data, ["AtU8"]} ->
- {ok, Mod, Data};
- {missing, FD, _Mod, _Data, What} ->
- error({missing_chunk, filename(FD), hd(What)});
+ {missing, FD, Mod, Data, What} ->
+ case What -- OptionalChunks of
+ [] -> {ok, Mod, Data};
+ [Missing | _] -> error({missing_chunk, filename(FD), Missing})
+ end;
R ->
R
end.
@@ -638,6 +644,22 @@ get_chunk(Id, Pos, Size, FD) ->
chunks_to_data([{atom_chunk, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) ->
{NewAtoms, Ret} = chunk_to_data(Name, <<"">>, File, Cs, Atoms, Module),
chunks_to_data(CNs, Chunks, File, Cs, Module, NewAtoms, [Ret | L]);
+chunks_to_data([{abst_chunk, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) ->
+ DbgiChunk = proplists:get_value("Dbgi", Chunks, <<"">>),
+ {NewAtoms, Ret} =
+ case catch chunk_to_data(debug_info, DbgiChunk, File, Cs, Atoms, Module) of
+ {DbgiAtoms, {debug_info, {debug_info_v1, Backend, Metadata}}} ->
+ case Backend:debug_info(erlang_v1, Module, Metadata, []) of
+ {ok, Code} -> {DbgiAtoms, {abstract_code, {raw_abstract_v1, Code}}};
+ {error, _} -> {DbgiAtoms, {abstract_code, no_abstract_code}}
+ end;
+ {error,beam_lib,{key_missing_or_invalid,Path,debug_info}} ->
+ error({key_missing_or_invalid,Path,abstract_code});
+ _ ->
+ AbstChunk = proplists:get_value("Abst", Chunks, <<"">>),
+ chunk_to_data(Name, AbstChunk, File, Cs, Atoms, Module)
+ end,
+ chunks_to_data(CNs, Chunks, File, Cs, Module, NewAtoms, [Ret | L]);
chunks_to_data([{Id, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) ->
{_Id, Chunk} = lists:keyfind(Id, 1, Chunks),
{NewAtoms, Ret} = chunk_to_data(Name, Chunk, File, Cs, Atoms, Module),
@@ -660,13 +682,30 @@ chunk_to_data(compile_info=Id, Chunk, File, _Cs, AtomTable, _Mod) ->
error:badarg ->
error({invalid_chunk, File, chunk_name_to_id(Id, File)})
end;
+chunk_to_data(debug_info=Id, Chunk, File, _Cs, AtomTable, Mod) ->
+ case Chunk of
+ <<>> ->
+ {AtomTable, {Id, no_debug_info}};
+ <<0:8,N:8,Mode0:N/binary,Rest/binary>> ->
+ Mode = binary_to_atom(Mode0, utf8),
+ Term = decrypt_chunk(Mode, Mod, File, Id, Rest),
+ {AtomTable, {Id, Term}};
+ _ ->
+ case catch binary_to_term(Chunk) of
+ {'EXIT', _} ->
+ error({invalid_chunk, File, chunk_name_to_id(Id, File)});
+ Term ->
+ {AtomTable, {Id, Term}}
+ end
+ end;
chunk_to_data(abstract_code=Id, Chunk, File, _Cs, AtomTable, Mod) ->
case Chunk of
<<>> ->
{AtomTable, {Id, no_abstract_code}};
<<0:8,N:8,Mode0:N/binary,Rest/binary>> ->
Mode = binary_to_atom(Mode0, utf8),
- decrypt_abst(Mode, Mod, File, Id, AtomTable, Rest);
+ Term = decrypt_chunk(Mode, Mod, File, Id, Rest),
+ {AtomTable, {Id, anno_from_term(Term)}};
_ ->
case catch binary_to_term(Chunk) of
{'EXIT', _} ->
@@ -705,6 +744,7 @@ chunk_name_to_id(locals, _) -> "LocT";
chunk_name_to_id(labeled_locals, _) -> "LocT";
chunk_name_to_id(attributes, _) -> "Attr";
chunk_name_to_id(abstract_code, _) -> "Abst";
+chunk_name_to_id(debug_info, _) -> "Dbgi";
chunk_name_to_id(compile_info, _) -> "CInf";
chunk_name_to_id(Other, File) ->
error({unknown_chunk, File, Other}).
@@ -894,23 +934,18 @@ mandatory_chunks() ->
-define(CRYPTO_KEY_SERVER, beam_lib__crypto_key_server).
-decrypt_abst(Type, Module, File, Id, AtomTable, Bin) ->
+decrypt_chunk(Type, Module, File, Id, Bin) ->
try
KeyString = get_crypto_key({debug_info, Type, Module, File}),
- Key = make_crypto_key(Type, KeyString),
- Term = decrypt_abst_1(Key, Bin),
- {AtomTable, {Id, Term}}
+ {Type,Key,IVec,_BlockSize} = make_crypto_key(Type, KeyString),
+ ok = start_crypto(),
+ NewBin = crypto:block_decrypt(Type, Key, IVec, Bin),
+ binary_to_term(NewBin)
catch
_:_ ->
error({key_missing_or_invalid, File, Id})
end.
-decrypt_abst_1({Type,Key,IVec,_BlockSize}, Bin) ->
- ok = start_crypto(),
- NewBin = crypto:block_decrypt(Type, Key, IVec, Bin),
- Term = binary_to_term(NewBin),
- anno_from_term(Term).
-
anno_from_term({raw_abstract_v1, Forms}) ->
{raw_abstract_v1, anno_from_forms(Forms)};
anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; Tag =:= abstract_v2 ->
diff --git a/lib/stdlib/src/erl_abstract_code.erl b/lib/stdlib/src/erl_abstract_code.erl
new file mode 100644
index 0000000000..6e45f11aa3
--- /dev/null
+++ b/lib/stdlib/src/erl_abstract_code.erl
@@ -0,0 +1,28 @@
+-module(erl_abstract_code).
+-export([debug_info/4]).
+
+debug_info(_Format, _Module, {none,_CompilerOpts}, _Opts) ->
+ {error, missing};
+debug_info(erlang_v1, _Module, {AbstrCode,_CompilerOpts}, _Opts) ->
+ {ok, AbstrCode};
+debug_info(core_v1, _Module, {AbstrCode,CompilerOpts}, Opts) ->
+ CoreOpts = add_core_returns(delete_reports(CompilerOpts ++ Opts)),
+ try compile:noenv_forms(AbstrCode, CoreOpts) of
+ {ok, _, Core, _} -> {ok, Core};
+ _What -> {error, failed_conversion}
+ catch
+ error:_ -> {error, failed_conversion}
+ end;
+debug_info(_, _, _, _) ->
+ {error, unknown_format}.
+
+delete_reports(Opts) ->
+ [Opt || Opt <- Opts, not is_report_option(Opt)].
+
+is_report_option(report) -> true;
+is_report_option(report_errors) -> true;
+is_report_option(report_warnings) -> true;
+is_report_option(_) -> false.
+
+add_core_returns(Opts) ->
+ [to_core, return_errors, return_warnings] ++ Opts.
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 82ab484ea6..9e901ab69a 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -39,6 +39,7 @@
edlin_expand,
epp,
eval_bits,
+ erl_abstract_code,
erl_anno,
erl_bits,
erl_compile,
diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl
index 1baf7d0a94..93d51d17b3 100644
--- a/lib/stdlib/test/beam_lib_SUITE.erl
+++ b/lib/stdlib/test/beam_lib_SUITE.erl
@@ -85,6 +85,8 @@ normal(Conf) when is_list(Conf) ->
do_normal(Source, PrivDir, BeamFile, [no_utf8_atoms]),
{ok,_} = compile:file(Source, [{outdir,PrivDir}, no_debug_info]),
+ {ok, {simple, [{debug_info, {debug_info_v1, erl_abstract_code, {none, _}}}]}} =
+ beam_lib:chunks(BeamFile, [debug_info]),
{ok, {simple, [{abstract_code, no_abstract_code}]}} =
beam_lib:chunks(BeamFile, [abstract_code]),
@@ -130,8 +132,10 @@ do_normal(BeamFile, Opts) ->
{ok, {simple, [{labeled_locals, _LLocals}]}} =
beam_lib:chunks(BeamFile, [labeled_locals]),
{ok, {simple, [_Vsn]}} = beam_lib:version(BeamFile),
- {ok, {simple, [{abstract_code, _}]}} =
+ {ok, {simple, [{abstract_code, {_, _}}]}} =
beam_lib:chunks(BeamFile, [abstract_code]),
+ {ok, {simple, [{debug_info, {debug_info_v1, erl_abstract_code, _}}]}} =
+ beam_lib:chunks(BeamFile, [debug_info]),
%% Test reading optional chunks.
All = ["Atom", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT", "AtU8"],
@@ -197,11 +201,11 @@ error(Conf) when is_list(Conf) ->
LastChunk = last_chunk(Binary),
verify(chunk_too_big, beam_lib:chunks(Binary1, [LastChunk])),
Chunks = chunk_info(Binary),
- {value, {_, AbstractStart, _}} = lists:keysearch("Abst", 1, Chunks),
- {Binary2, _} = split_binary(Binary, AbstractStart),
- verify(chunk_too_big, beam_lib:chunks(Binary2, ["Abst"])),
- {Binary3, _} = split_binary(Binary, AbstractStart-4),
- verify(invalid_beam_file, beam_lib:chunks(Binary3, ["Abst"])),
+ {value, {_, DebugInfoStart, _}} = lists:keysearch("Dbgi", 1, Chunks),
+ {Binary2, _} = split_binary(Binary, DebugInfoStart),
+ verify(chunk_too_big, beam_lib:chunks(Binary2, ["Dbgi"])),
+ {Binary3, _} = split_binary(Binary, DebugInfoStart-4),
+ verify(invalid_beam_file, beam_lib:chunks(Binary3, ["Dbgi"])),
%% Instead of the 5:32 field below, there used to be control characters
%% (including zero bytes) directly in the string. Because inferior programs
@@ -228,7 +232,7 @@ do_error(BeamFile, ACopy) ->
Chunks = chunk_info(BeamFile),
{value, {_, AtomStart, _}} = lists:keysearch("AtU8", 1, Chunks),
{value, {_, ImportStart, _}} = lists:keysearch("ImpT", 1, Chunks),
- {value, {_, AbstractStart, _}} = lists:keysearch("Abst", 1, Chunks),
+ {value, {_, DebugInfoStart, _}} = lists:keysearch("Dbgi", 1, Chunks),
{value, {_, AttributesStart, _}} =
lists:keysearch("Attr", 1, Chunks),
{value, {_, CompileInfoStart, _}} =
@@ -238,8 +242,8 @@ do_error(BeamFile, ACopy) ->
verify(invalid_chunk, beam_lib:chunks(BF2, [imports])),
BF3 = set_byte(ACopy, BeamFile, AtomStart-6, 17),
verify(missing_chunk, beam_lib:chunks(BF3, [imports])),
- BF4 = set_byte(ACopy, BeamFile, AbstractStart+10, 17),
- verify(invalid_chunk, beam_lib:chunks(BF4, [abstract_code])),
+ BF4 = set_byte(ACopy, BeamFile, DebugInfoStart+10, 17),
+ verify(invalid_chunk, beam_lib:chunks(BF4, [debug_info])),
BF5 = set_byte(ACopy, BeamFile, AttributesStart+8, 17),
verify(invalid_chunk, beam_lib:chunks(BF5, [attributes])),
@@ -550,11 +554,11 @@ encrypted_abstr_1(Conf) ->
ok.
do_encrypted_abstr(Beam, Key) ->
- verify(key_missing_or_invalid, beam_lib:chunks(Beam, [abstract_code])),
+ verify(key_missing_or_invalid, beam_lib:chunks(Beam, [debug_info])),
- %% The raw chunk "Abst" can still be read even without a key.
- {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]),
- <<0:8,8:8,"des3_cbc",_/binary>> = Abst,
+ %% The raw chunk "Dbgi" can still be read even without a key.
+ {ok,{simple,[{"Dbgi",Dbgi}]}} = beam_lib:chunks(Beam, ["Dbgi"]),
+ <<0:8,8:8,"des3_cbc",_/binary>> = Dbgi,
%% Try som invalid funs.
bad_fun(badfun, fun() -> ok end),
@@ -585,7 +589,7 @@ do_encrypted_abstr(Beam, Key) ->
{ok,_} = beam_lib:clear_crypto_key_fun(),
ok = beam_lib:crypto_key_fun(simple_crypto_fun(Key)),
verify_abstract(Beam),
- {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]),
+ {ok,{simple,[{"Dbgi",Dbgi}]}} = beam_lib:chunks(Beam, ["Dbgi"]),
%% Installing a new key fun is not possible without clearing the old.
verify(exists, beam_lib:crypto_key_fun(ets_crypto_fun(Key))),
@@ -594,7 +598,7 @@ do_encrypted_abstr(Beam, Key) ->
{ok,_} = beam_lib:clear_crypto_key_fun(),
ok = beam_lib:crypto_key_fun(ets_crypto_fun(Key)),
verify_abstract(Beam),
- {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]),
+ {ok,{simple,[{"Dbgi",Dbgi}]}} = beam_lib:chunks(Beam, ["Dbgi"]),
{ok,cleared} = beam_lib:clear_crypto_key_fun(),
@@ -617,10 +621,10 @@ bad_fun(F) ->
bad_fun(S, F) ->
verify(S, beam_lib:crypto_key_fun(F)).
-
verify_abstract(Beam) ->
- {ok,{simple,[Chunk]}} = beam_lib:chunks(Beam, [abstract_code]),
- {abstract_code,{raw_abstract_v1,_}} = Chunk.
+ {ok,{simple,[Abst, Dbgi]}} = beam_lib:chunks(Beam, [abstract_code, debug_info]),
+ {abstract_code,{raw_abstract_v1,_}} = Abst,
+ {debug_info,{debug_info_v1,erl_abstract_code,_}} = Dbgi.
simple_crypto_fun(Key) ->
fun(init) -> ok;