diff options
author | José Valim <[email protected]> | 2017-03-08 13:25:35 +0100 |
---|---|---|
committer | José Valim <[email protected]> | 2017-04-25 13:24:22 +0200 |
commit | dfb899c0229f7ff7dbfad34d496e0429562728bf (patch) | |
tree | cfe4bd8963e33651e7552092dbd22d76492917e1 /lib/compiler/src | |
parent | 621cedccc78581330b9628c559b0d851c303564f (diff) | |
download | otp-dfb899c0229f7ff7dbfad34d496e0429562728bf.tar.gz otp-dfb899c0229f7ff7dbfad34d496e0429562728bf.tar.bz2 otp-dfb899c0229f7ff7dbfad34d496e0429562728bf.zip |
Store abstract code in the Dbgi chunk
The new Dbgi chunk returns data in the following format:
{debug_info_v1, Backend, Data}
This allows compilers to store the debug info in different
formats. In order to retrieve a particular format, for
instance, Erlang Abstract Format, one may invoke:
Backend:debug_info(erlang_v1, Module, Data, Opts)
Besides introducing the chunk above, this commit also:
* Changes beam_lib:chunk(Beam, [:abstract_code]) to
read from the new Dbgi chunk while keeping backwards
compatibility with old .beams
* Adds the {debug_info, {Backend, Data}} option to
compile:file/2 and friends that are stored in the
Dbgi chunk. This allows the debug info encryption
mechanism to work across compilers
* Improves dialyzer to work directly on Core Erlang,
allowing languages that do not have the Erlang
Abstract Format to be dialyzer as long as they emit
the new chunk and their backend implementation is
available
Backwards compatibility is kept across the board except
for those calling beam_lib:chunk(Beam, ["Abst"]), as the
old chunk is no longer available. Note however the "Abst"
chunk has always been optional.
Future OTP versions may remove parsing the "Abst" chunk
altogether from beam_lib once Erlang 19 and earlier is no
longer supported.
The current Dialyzer implementation still supports earlier
.beam files and such may also be removed in future versions.
Diffstat (limited to 'lib/compiler/src')
-rw-r--r-- | lib/compiler/src/compile.erl | 89 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 12 | ||||
-rw-r--r-- | lib/compiler/src/v3_kernel.erl | 2 |
3 files changed, 57 insertions, 46 deletions
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) -> |