diff options
Diffstat (limited to 'lib/compiler')
-rw-r--r-- | lib/compiler/doc/src/compile.xml | 25 | ||||
-rw-r--r-- | lib/compiler/src/beam_type.erl | 44 | ||||
-rw-r--r-- | lib/compiler/src/beam_validator.erl | 16 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 97 | ||||
-rw-r--r-- | lib/compiler/src/core_scan.erl | 8 | ||||
-rw-r--r-- | lib/compiler/src/erl_bifs.erl | 3 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 12 | ||||
-rw-r--r-- | lib/compiler/src/v3_kernel.erl | 2 | ||||
-rw-r--r-- | lib/compiler/test/beam_type_SUITE.erl | 11 | ||||
-rw-r--r-- | lib/compiler/test/compilation_SUITE.erl | 2 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 113 |
11 files changed, 242 insertions, 91 deletions
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/beam_type.erl b/lib/compiler/src/beam_type.erl index 2b5d558ee4..7e9a243ada 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -26,6 +26,8 @@ -import(lists, [filter/2,foldl/3,keyfind/3,member/2, reverse/1,reverse/2,sort/1]). +-define(UNICODE_INT, {integer,{0,16#10FFFF}}). + -spec module(beam_utils:module_code(), [compile:option()]) -> {'ok',beam_utils:module_code()}. @@ -494,6 +496,10 @@ update({test,test_arity,_Fail,[Src,Arity]}, Ts0) -> tdb_update([{Src,{tuple,Arity,[]}}], Ts0); update({test,is_map,_Fail,[Src]}, Ts0) -> tdb_update([{Src,map}], Ts0); +update({get_map_elements,_,Src,{list,Elems0}}, Ts0) -> + {_Ss,Ds} = beam_utils:split_even(Elems0), + Elems = [{Dst,kill} || Dst <- Ds], + tdb_update([{Src,map}|Elems], Ts0); update({test,is_nonempty_list,_Fail,[Src]}, Ts0) -> tdb_update([{Src,nonempty_list}], Ts0); update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) -> @@ -507,10 +513,39 @@ update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) -> end; update({test,is_record,_Fail,[Src,Tag,{integer,Arity}]}, Ts) -> tdb_update([{Src,{tuple,Arity,[Tag]}}], Ts); -update({test,_Test,_Fail,_Other}, Ts) -> - Ts; + +%% Binary matching + update({test,bs_get_integer2,_,_,Args,Dst}, Ts) -> tdb_update([{Dst,get_bs_integer_type(Args)}], Ts); +update({test,bs_get_utf8,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,?UNICODE_INT}], Ts); +update({test,bs_get_utf16,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,?UNICODE_INT}], Ts); +update({test,bs_get_utf32,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,?UNICODE_INT}], Ts); +update({bs_init,_,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,kill}], Ts); +update({bs_put,_,_,_}, Ts) -> + Ts; +update({bs_save2,_,_}, Ts) -> + Ts; +update({bs_restore2,_,_}, Ts) -> + Ts; +update({bs_context_to_binary,Dst}, Ts) -> + tdb_update([{Dst,kill}], Ts); +update({test,bs_start_match2,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,kill}], Ts); +update({test,bs_get_binary2,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,kill}], Ts); +update({test,bs_get_float2,_,_,_,Dst}, Ts) -> + tdb_update([{Dst,float}], Ts); + +update({test,_Test,_Fail,_Other}, Ts) -> + Ts; + +%% Calls + update({call_ext,Ar,{extfunc,math,Math,Ar}}, Ts) -> case is_math_bif(Math, Ar) of true -> tdb_update([{{x,0},float}], Ts); @@ -537,9 +572,10 @@ update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) -> update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts); update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts); update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts); +update({call_fun, _}, Ts) -> tdb_kill_xregs(Ts); +update({apply, _}, Ts) -> tdb_kill_xregs(Ts); + update({line,_}, Ts) -> Ts; -update({bs_save2,_,_}, Ts) -> Ts; -update({bs_restore2,_,_}, Ts) -> Ts; %% The instruction is unknown. Kill all information. update(_I, _Ts) -> tdb_new(). diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index c26e5719aa..ca60e1b2de 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -623,17 +623,17 @@ valfun_4({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) -> valfun_4({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) -> validate_bs_skip_utf(Fail, Ctx, Live, Vst); valfun_4({test,bs_get_integer2,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> - validate_bs_get(Fail, Ctx, Live, Dst, Vst); + validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst); valfun_4({test,bs_get_float2,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> - validate_bs_get(Fail, Ctx, Live, Dst, Vst); + validate_bs_get(Fail, Ctx, Live, {float, []}, Dst, Vst); valfun_4({test,bs_get_binary2,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> - validate_bs_get(Fail, Ctx, Live, Dst, Vst); + validate_bs_get(Fail, Ctx, Live, term, Dst, Vst); valfun_4({test,bs_get_utf8,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> - validate_bs_get(Fail, Ctx, Live, Dst, Vst); + validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst); valfun_4({test,bs_get_utf16,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> - validate_bs_get(Fail, Ctx, Live, Dst, Vst); + validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst); valfun_4({test,bs_get_utf32,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> - validate_bs_get(Fail, Ctx, Live, Dst, Vst); + validate_bs_get(Fail, Ctx, Live, {integer, []}, Dst, Vst); valfun_4({bs_save2,Ctx,SavePoint}, Vst) -> bsm_save(Ctx, SavePoint, Vst); valfun_4({bs_restore2,Ctx,SavePoint}, Vst) -> @@ -794,12 +794,12 @@ verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> %% %% Common code for validating bs_get* instructions. %% -validate_bs_get(Fail, Ctx, Live, Dst, Vst0) -> +validate_bs_get(Fail, Ctx, Live, Type, Dst, Vst0) -> bsm_validate_context(Ctx, Vst0), verify_live(Live, Vst0), Vst1 = prune_x_regs(Live, Vst0), Vst = branch_state(Fail, Vst1), - set_type_reg(term, Dst, Vst). + set_type_reg(Type, Dst, Vst). %% %% Common code for validating bs_skip_utf* instructions. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 03b52932d1..be0f45323f 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). @@ -213,14 +213,6 @@ expand_opt(report, Os) -> [report_errors,report_warnings|Os]; expand_opt(return, Os) -> [return_errors,return_warnings|Os]; -expand_opt(r12, Os) -> - [no_recv_opt,no_line_info,no_utf8_atoms|Os]; -expand_opt(r13, Os) -> - [no_record_opt,no_recv_opt,no_line_info,no_utf8_atoms|Os]; -expand_opt(r14, Os) -> - [no_record_opt,no_line_info,no_utf8_atoms|Os]; -expand_opt(r15, Os) -> - [no_record_opt,no_utf8_atoms|Os]; expand_opt(r16, Os) -> [no_record_opt,no_utf8_atoms|Os]; expand_opt(r17, Os) -> @@ -310,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(), @@ -1323,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 @@ -1368,6 +1359,18 @@ 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(Option) -> effects_code_generation(Option). + start_crypto() -> try crypto:start() of {error,{already_started,crypto}} -> ok; @@ -1394,16 +1397,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) -> @@ -1487,15 +1490,17 @@ 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; return_errors-> false; return_warnings-> false; + warnings_as_errors -> false; binary -> false; verbose -> false; {cwd,_} -> false; + {outdir, _} -> false; _ -> true end. diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl index 15bfc78c8b..d7d5f900de 100644 --- a/lib/compiler/src/core_scan.erl +++ b/lib/compiler/src/core_scan.erl @@ -283,10 +283,12 @@ scan1([$$|Cs0], Toks, Pos) -> %Character constant scan1(Cs, [{char,Pos,C}|Toks], Pos1); scan1([$'|Cs0], Toks, Pos) -> %Atom (always quoted) {S,Cs1,Pos1} = scan_string(Cs0, $', Pos), - case catch list_to_atom(S) of + try binary_to_atom(list_to_binary(S), utf8) of A when is_atom(A) -> - scan1(Cs1, [{atom,Pos,A}|Toks], Pos1); - _Error -> scan_error({illegal,atom}, Pos) + scan1(Cs1, [{atom,Pos,A}|Toks], Pos1) + catch + error:_ -> + scan_error({illegal,atom}, Pos) end; scan1([$"|Cs0], Toks, Pos) -> %String {S,Cs1,Pos1} = scan_string(Cs0, $", Pos), diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index d60f73d421..b1f1db6fa3 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -108,11 +108,14 @@ is_pure(erlang, list_to_binary, 1) -> true; is_pure(erlang, list_to_float, 1) -> true; is_pure(erlang, list_to_integer, 1) -> true; is_pure(erlang, list_to_pid, 1) -> true; +is_pure(erlang, list_to_port, 1) -> true; +is_pure(erlang, list_to_ref, 1) -> true; is_pure(erlang, list_to_tuple, 1) -> true; is_pure(erlang, max, 2) -> true; is_pure(erlang, min, 2) -> true; is_pure(erlang, phash, 2) -> false; is_pure(erlang, pid_to_list, 1) -> true; +is_pure(erlang, port_to_list, 1) -> true; is_pure(erlang, round, 1) -> true; is_pure(erlang, setelement, 3) -> true; is_pure(erlang, size, 1) -> true; 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/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 7ca544a537..c11883d5ff 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -22,7 +22,7 @@ -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, integers/1,coverage/1,booleans/1,setelement/1,cons/1, - tuple/1,record_float/1]). + tuple/1,record_float/1,binary_float/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -38,7 +38,8 @@ groups() -> setelement, cons, tuple, - record_float + record_float, + binary_float ]}]. init_per_suite(Config) -> @@ -143,6 +144,12 @@ record_float(R, N0) -> N end. +binary_float(_Config) -> + <<-1/float>> = binary_negate_float(<<1/float>>), + ok. + +binary_negate_float(<<Float/float>>) -> + <<-Float/float>>. id(I) -> I. 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 10740ac2b0..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. @@ -865,9 +893,7 @@ do_core_roundtrip_2(M, Core0, Outdir) -> case cmp_core(Core0, Core, M) of true -> ok; false -> error - end, - - ok. + end. undo_var_translation(Tree) -> F = fun(Node) -> @@ -920,11 +946,72 @@ diff(E, E) -> diff([H1|T1], [H2|T2]) -> [diff(H1, H2)|diff(T1, T2)]; diff(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> - L = diff(tuple_to_list(T1), tuple_to_list(T2)), - list_to_tuple(L); + case cerl:is_c_var(T1) andalso cerl:is_c_var(T2) of + true -> + diff_var(T1, T2); + false -> + case cerl:is_c_map(T1) andalso cerl:is_c_map(T2) of + true -> + diff_map(T1, T2); + false -> + diff_tuple(T1, T2) + end + end; diff(E1, E2) -> {'DIFF',E1,E2}. +diff_var(V1, V2) -> + case {cerl:var_name(V1),cerl:var_name(V2)} of + {Same,Same} -> + V1; + {Name1,Name2} -> + %% The inliner uses integers as variable names. Such integers + %% are read back as atoms. + case is_integer(Name1) andalso + list_to_atom(integer_to_list(Name1)) =:= Name2 of + true -> + V1; + _ -> + cerl:update_c_var(V1, {'DIFF',Name1,Name2}) + end + end. + +%% Annotations for maps are not preserved exactly, but that is not +%% a real problem. Workaround by not comparing all annotations when +%% comparing maps. + +diff_map(M, M) -> + M; +diff_map(M1, M2) -> + case cerl:get_ann(M1) =:= cerl:get_ann(M2) of + false -> + diff_tuple(M1, M2); + true -> + case remove_compiler_gen(M1) =:= remove_compiler_gen(M2) of + true -> + M1; + false -> + diff_tuple(M1, M2) + end + end. + +diff_tuple(T1, T2) -> + L = diff(tuple_to_list(T1), tuple_to_list(T2)), + list_to_tuple(L). + +remove_compiler_gen(M) -> + Arg0 = cerl:map_arg(M), + Arg = cerl:set_ann(Arg0, []), + Es0 = cerl:map_es(M), + Es = [remove_compiler_gen_1(Pair) || Pair <- Es0], + cerl:update_c_map(M, Arg, Es). + +remove_compiler_gen_1(Pair) -> + Op0 = cerl:map_pair_op(Pair), + Op = cerl:set_ann(Op0, []), + K = cerl:map_pair_key(Pair), + V = cerl:map_pair_val(Pair), + cerl:update_c_map_pair(Pair, Op, K, V). %% Compile to Beam assembly language (.S) and then try to %% run .S through the compiler again. |