diff options
Diffstat (limited to 'lib')
56 files changed, 1399 insertions, 1645 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/compile.erl b/lib/compiler/src/compile.erl index 019d8ba864..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). @@ -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,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; @@ -1386,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) -> @@ -1479,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/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/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index 5b1889ab06..eafab0588e 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -544,7 +544,7 @@ t_clause(Name, Type, Opts) -> pp_clause(Pre, Type, Opts) -> Types = ot_utype([Type]), - Atom = lists:duplicate(string_length(Pre), $a), + Atom = lists:duplicate(string:length(Pre), $a), Attr = {attribute,0,spec,{{list_to_atom(Atom),0},[Types]}}, L1 = erl_pp:attribute(erl_parse:new_anno(Attr), [{encoding, Opts#opts.encoding}]), @@ -566,7 +566,7 @@ format_type(Prefix, _Name, Type, Last, Opts) -> [{tt, Prefix ++ [" = "] ++ t_utype(Type, Opts) ++ Last}]. pp_type(Prefix, Type, Opts) -> - Atom = list_to_atom(lists:duplicate(string_length(Prefix), $a)), + Atom = list_to_atom(lists:duplicate(string:length(Prefix), $a)), Attr = {attribute,0,type,{Atom,ot_utype(Type),[]}}, L1 = erl_pp:attribute(erl_parse:new_anno(Attr), [{encoding, Opts#opts.encoding}]), @@ -768,21 +768,6 @@ atom(String, #opts{encoding = latin1}) -> atom(String, #opts{encoding = utf8}) -> io_lib:write_atom(list_to_atom(String)). --dialyzer({nowarn_function, string_length/1}). -string_length(Data) -> - try iolist_size(Data) - catch - _:_ -> - M = string, - F = length, - As = [Data], - try apply(M, F, As) - catch - _:_ -> - 20 - end - end. - %% <!ATTLIST author %% name CDATA #REQUIRED %% email CDATA #IMPLIED diff --git a/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile index c317411d32..e3d2ee7e3f 100644 --- a/lib/erl_docgen/priv/css/Makefile +++ b/lib/erl_docgen/priv/css/Makefile @@ -39,7 +39,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/erl_docgen-$(VSN) CSS_FILES = \ - otp_doc.css + otp_doc.css \ + highlight.css # ---------------------------------------------------- diff --git a/lib/erl_docgen/priv/css/highlight.css b/lib/erl_docgen/priv/css/highlight.css new file mode 100644 index 0000000000..d5bd1d2a9a --- /dev/null +++ b/lib/erl_docgen/priv/css/highlight.css @@ -0,0 +1,96 @@ +/* + +Atom One Light by Daniel Gamage +Original One Light Syntax theme from https://github.com/atom/one-light-syntax + +base: #fafafa +mono-1: #383a42 +mono-2: #686b77 +mono-3: #a0a1a7 +hue-1: #0184bb +hue-2: #4078f2 +hue-3: #a626a4 +hue-4: #50a14f +hue-5: #e45649 +hue-5-2: #c91243 +hue-6: #986801 +hue-6-2: #c18401 + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #383a42; + background: #fafafa; +} + +.hljs-comment, +.hljs-quote { + color: #a0a1a7; + font-style: italic; +} + +.hljs-doctag, +.hljs-keyword, +.hljs-formula { + color: #a626a4; +} + +.hljs-section, +.hljs-name, +.hljs-selector-tag, +.hljs-deletion, +.hljs-subst { + color: #e45649; +} + +.hljs-literal { + color: #0184bb; +} + +.hljs-string, +.hljs-regexp, +.hljs-addition, +.hljs-attribute, +.hljs-meta-string { + color: #50a14f; +} + +.hljs-built_in, +.hljs-class .hljs-title { + color: #c18401; +} + +.hljs-attr, +.hljs-variable, +.hljs-template-variable, +.hljs-type, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-number { + color: #986801; +} + +.hljs-symbol, +.hljs-bullet, +.hljs-link, +.hljs-meta, +.hljs-selector-id, +.hljs-title { + color: #4078f2; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-link { + text-decoration: underline; +} diff --git a/lib/erl_docgen/priv/css/otp_doc.css b/lib/erl_docgen/priv/css/otp_doc.css index 219740a557..844aad2945 100644 --- a/lib/erl_docgen/priv/css/otp_doc.css +++ b/lib/erl_docgen/priv/css/otp_doc.css @@ -1,34 +1,39 @@ /* standard OTP style sheet */ body { - background: white; - font-family: Verdana, Arial, Helvetica, sans-serif; + background: #fefefe; + color: #1a1a1a; + font-family: sans-serif; margin: 0; padding: 0; border: 0; overflow: scroll; height: 100%; max-height: 100%; + line-height: 1.2em; + font-size: 16px; } -th { font-family: Verdana, Arial, Helvetica, sans-serif } -td { font-family: Verdana, Arial, Helvetica, sans-serif } -p { font-family: Verdana, Arial, Helvetica, sans-serif } +h1, h2, h3, h4, h5, h6{ + line-height: 1.2em; +} + +p { max-width: 42em } -.header { background: #222; color: #fff } +.header { background: #222; color: #fefefe } .top { background: #efe } .otp { background: #efe } .erlang { background: #ffe } .otp2 { background: #efe } .app { background: #ffe } -a:link { color: blue; text-decoration: none } -a:active { color: blue; text-decoration: none } -a:visited { color: blue; text-decoration: none } +a:link { color: #1862ab; text-decoration: none } +a:active { color: #1c7cd6; text-decoration: none } +a:visited { color: #1b6ec2; text-decoration: none } #container { width: 100%; margin: 0; - background-color: #fff; + background-color: #fefefe; } #leftnav { @@ -41,11 +46,12 @@ a:visited { color: blue; text-decoration: none } overflow:auto; margin: 0; padding: 1px; - border-right: 1px solid red; + border-right: 1px solid #ccc; } #content { margin-left: 340px; /* set left value to WidthOfFrameDiv */ + max-width: 42em; } .frontpage @@ -61,66 +67,67 @@ a:visited { color: blue; text-decoration: none } .footer { margin: 15px; /* Magins for inner DIV inside each DIV (to provide padding) */ + text-align: center; } -span.bold_code { font-family: Courier, monospace; font-weight: bold } -span.code { font-family: Courier, monospace; font-weight: normal } +.bold_code { font-family: mono, Courier, monospace; font-weight: bold } +.code { + font-family: mono, Courier, monospace; + font-weight: normal; + background-color: #f3f3f3; +} .note, .warning, .do, .dont { - border: solid black 1px; - margin: 1em 3em; + border: 1px solid #495057; + margin: 1em 0; } .note .label { - background: #30d42a; - color: white; + background-color: #2b8a3e; + color: #fefefe; font-weight: bold; - padding: 5px 10px; + padding: 0.5em 1em; } .note .content { - background: #eafeea; - color: black; + background: #f8f9fa; line-height: 120%; - font-size: 90%; - padding: 5px 10px; + font-size: 0.9em; + padding: 0.5em 1em; } .warning .label { - background: #C00; - color: white; + background: #c92a2a; + color: #fefefe; font-weight: bold; - padding: 5px 10px; + padding: 0.5em 1em; } .warning .content { - background: #FFF0F0; - color: black; + background-color: #f8f9fa; line-height: 120%; - font-size: 90%; - padding: 5px 10px; + font-size: 0.9em; + padding: 0.5em 1em; } .do .label { - background: #30d42a; - color: white; + background-color: #2b8a3e; + color: #fefefe; font-weight: bold; - padding: 5px 10px; + padding: 0.5em 1em; } .do .content { - background: #eafeea; - color: black; + background: #f8f9fa; line-height: 120%; - font-size: 90%; - padding: 5px 10px; + font-size: 0.9em; + padding: 0.5em 1em; } .dont .label { - background: #C00; - color: white; + background: #c92a2a; + color: #fefefe; font-weight: bold; - padding: 5px 10px; + padding: 0.5em 1em; } .dont .content { - background: #FFF0F0; - color: black; + background-color: #f8f9fa; line-height: 120%; - font-size: 90%; - padding: 5px 10px; + font-size: 0.9em; + padding: 0.5em 1em; } .quote { @@ -128,19 +135,134 @@ span.code { font-family: Courier, monospace; font-weight: normal } } .example { - background-color:#eeeeff; - padding: 0px 10px; + background-color:#f1f3f5; + border: 1px solid #dee2e6; + padding: 0.5em 1em; + margin: 1em 0; + font-size: 0.7em; } .extrafrontpageinfo { color: #C00; font-weight: bold; - font-size: 120%; + font-size: 1.2em; } -pre { font-family: Courier, monospace; font-weight: normal } +pre { + font-family: mono, Courier, monospace; + font-weight: normal; + margin: 0; +} -.REFBODY { margin-left: 13mm } -.REFTYPES { margin-left: 8mm } +.exports-body, .data-types-body, .REFBODY{ + margin-left: 2em; +} +.REFTYPES { margin-left: 1.5em } footer { } + +.erlang-logo-wrapper{ + text-align: center; + margin-bottom: 1em; +} + +.main-title{ + text-align: center; +} + +.main-description{ + text-align: center; + margin: 2em 0; + font-size: 1.5em; + line-height: 1.5em; +} + +.doc-table-wrapper, .doc-image-wrapper{ + width: 100%; +} + +.doc-image-wrapper{ + text-align: center; +} + +.doc-table, .doc-image{ + min-width: 50%; + margin: 0 auto; + font-size: 0.7em; +} + +.doc-table-caption, .doc-image-caption{ + margin-top: 1em; + font-style: italic; + text-align: center; +} + +table { + border-collapse: collapse; + min-width: 50%; + margin: 1em; +} + +table, th, td { + border: 1px solid #666; +} + +th, td { + padding: 0.5em; + text-align: left; +} + +tr:hover { + background-color: #f5f5f5; +} + +tr:nth-child(even) { + background-color: #f2f2f2; +} + +th { + background-color: #777; + color: #fefefe; +} + +.section-title, .section-subtitle, .section-version{ + text-align: center; + margin: 0; +} + +.section-title{ + font-weight: bold; +} + +.section-version{ + font-size: small; +} + +.expand-collapse-items{ + font-size: small; +} + +h3>a{ + color: #1a1a1a !important; +} + +hr{ + border: 0; + border-top: 1px solid #aaa; +} + +.section-links, .panel-sections, .expand-collapse-items{ + padding: 0 1em; +} + +.section-links, .panel-sections{ + margin-top: 0; +} + +a > .code { + color: #1862ab; +} + +.func-types-title{ + font-size: 1em; +} diff --git a/lib/erl_docgen/priv/images/erlang-logo.gif b/lib/erl_docgen/priv/images/erlang-logo.gif Binary files differindex abf5f225d7..ae13168429 100644 --- a/lib/erl_docgen/priv/images/erlang-logo.gif +++ b/lib/erl_docgen/priv/images/erlang-logo.gif diff --git a/lib/erl_docgen/priv/images/erlang-logo.png b/lib/erl_docgen/priv/images/erlang-logo.png Binary files differindex 56291aac15..41bba97417 100644 --- a/lib/erl_docgen/priv/images/erlang-logo.png +++ b/lib/erl_docgen/priv/images/erlang-logo.png diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile index 06a13defca..ad6d4acb6c 100644 --- a/lib/erl_docgen/priv/js/flipmenu/Makefile +++ b/lib/erl_docgen/priv/js/flipmenu/Makefile @@ -76,6 +76,7 @@ release_spec: opt release_docs_spec: $(INSTALL_DIR) "$(RELEASE_PATH)/doc/js/flipmenu" $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELEASE_PATH)/doc/js/flipmenu" + $(INSTALL_DATA) ../highlight.js ../highlight.pack.js "$(RELEASE_PATH)/doc/js/" release_tests_spec: diff --git a/lib/erl_docgen/priv/js/flipmenu/flip_closed.gif b/lib/erl_docgen/priv/js/flipmenu/flip_closed.gif Binary files differindex 9a27c7c25d..a75107a782 100644 --- a/lib/erl_docgen/priv/js/flipmenu/flip_closed.gif +++ b/lib/erl_docgen/priv/js/flipmenu/flip_closed.gif diff --git a/lib/erl_docgen/priv/js/flipmenu/flip_open.gif b/lib/erl_docgen/priv/js/flipmenu/flip_open.gif Binary files differindex 9dda60e73a..1274637fe0 100644 --- a/lib/erl_docgen/priv/js/flipmenu/flip_open.gif +++ b/lib/erl_docgen/priv/js/flipmenu/flip_open.gif diff --git a/lib/erl_docgen/priv/js/flipmenu/flip_static.gif b/lib/erl_docgen/priv/js/flipmenu/flip_static.gif Binary files differindex 2b3ddb5382..4cc914a50a 100644 --- a/lib/erl_docgen/priv/js/flipmenu/flip_static.gif +++ b/lib/erl_docgen/priv/js/flipmenu/flip_static.gif diff --git a/lib/erl_docgen/priv/js/highlight.js b/lib/erl_docgen/priv/js/highlight.js new file mode 100644 index 0000000000..0594b42aa3 --- /dev/null +++ b/lib/erl_docgen/priv/js/highlight.js @@ -0,0 +1,39 @@ +/*globals document, window*/ +window.addEventListener("load", function () { + "use strict"; + var body = document.body, + base = window.__otpTopDocDir || "/doc/js/", + cssLink = document.createElement('link'), + script = document.createElement('script'), + intervalId, attempts = 0; + + cssLink.rel = "stylesheet"; + cssLink.href = base + "../highlight.css"; + script.src = base + "highlight.pack.js"; + + body.appendChild(cssLink); + body.appendChild(script); + + function doHighlight() { + attempts += 1; + + if (attempts > 20) { + window.clearInterval(intervalId); + return; + } + + if (!window.hljs) { + return; + } + + window.clearInterval(intervalId); + + var i, len, nodes = document.querySelectorAll('.example'); + for (i = 0, len = nodes.length; i < len; i += 1) { + window.hljs.highlightBlock(nodes[i]); + } + + } + + intervalId = window.setInterval(doHighlight, 50); +}); diff --git a/lib/erl_docgen/priv/js/highlight.pack.js b/lib/erl_docgen/priv/js/highlight.pack.js new file mode 100644 index 0000000000..073d39e644 --- /dev/null +++ b/lib/erl_docgen/priv/js/highlight.pack.js @@ -0,0 +1,2 @@ +/*! highlight.js v9.7.0 | BSD3 License | git.io/hljslicense */ +!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset<r[0].offset?e:r:"start"===r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+w.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='<span class="'+a,o=t?"":C;return i+=e+'">',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"<unnamed>")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"<br>":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="</span>",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("erlang",function(e){var r="[a-z'][a-zA-Z0-9_']*",c="("+r+":"+r+"|"+r+")",b={keyword:"after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun if let not of orelse|10 query receive rem try when xor",literal:"false true"},i=e.C("%","$"),n={cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},a={b:"fun\\s+"+r+"/\\d+"},d={b:c+"\\(",e:"\\)",rB:!0,r:0,c:[{b:c,r:0},{b:"\\(",e:"\\)",eW:!0,rE:!0,r:0}]},o={b:"{",e:"}",r:0},t={b:"\\b_([A-Z][A-Za-z0-9_]*)?",r:0},f={b:"[A-Z][a-zA-Z0-9_]*",r:0},l={b:"#"+e.UIR,r:0,rB:!0,c:[{b:"#"+e.UIR,r:0},{b:"{",e:"}",r:0}]},s={bK:"fun receive if try case",e:"end",k:b};s.c=[i,a,e.inherit(e.ASM,{cN:""}),s,d,e.QSM,n,o,t,f,l];var u=[i,a,s,d,e.QSM,n,o,t,f,l];d.c[1].c=u,o.c=u,l.c[1].c=u;var h={cN:"params",b:"\\(",e:"\\)",c:u};return{aliases:["erl"],k:b,i:"(</|\\*=|\\+=|-=|/\\*|\\*/|\\(\\*|\\*\\))",c:[{cN:"function",b:"^"+r+"\\s*\\(",e:"->",rB:!0,i:"\\(|#|//|/\\*|\\\\|:|;",c:[h,e.inherit(e.TM,{b:r})],starts:{e:";|\\.",k:b,c:u}},i,{b:"^-",e:"\\.",r:0,eE:!0,rB:!0,l:"-"+e.IR,k:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec",c:[h]},n,e.QSM,l,t,f,o,{b:/\.$/}]}});hljs.registerLanguage("erlang-repl",function(e){return{k:{built_in:"spawn spawn_link self",keyword:"after and andalso|10 band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse|10 query receive rem try when xor"},c:[{cN:"meta",b:"^[0-9]+> ",r:10},e.C("%","$"),{cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},e.ASM,e.QSM,{b:"\\?(::)?([A-Z]\\w*(::)?)+"},{b:"->"},{b:"ok"},{b:"!"},{b:"(\\b[a-z'][a-zA-Z0-9_']*:[a-z'][a-zA-Z0-9_']*)|(\\b[a-z'][a-zA-Z0-9_']*)",r:0},{b:"[A-Z][a-zA-Z0-9_']*",r:0}]}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\._]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,s,a,t]}});
\ No newline at end of file diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index edab8e1c7e..4c7df1d1a0 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -200,13 +200,12 @@ <xsl:template match="head"> <xsl:param name="local_types"/> <xsl:param name="global_types"/> - <span class="bold_code"> + <div class="bold_code func-head"> <xsl:apply-templates mode="local_type"> <xsl:with-param name="local_types" select="$local_types"/> <xsl:with-param name="global_types" select="$global_types"/> </xsl:apply-templates> - </span> - <br/> + </div> </xsl:template> <!-- The *last* <name name="..." arity=".."/> --> @@ -234,7 +233,8 @@ <!-- It is assumed there is no support for overloaded specs (there is no spec with more than one clause) --> <xsl:if test="count($clause/guard) > 0 or count($type) > 0"> - <div class="REFBODY"><p>Types:</p> + <div class="REFBODY fun-types"> + <h3 class="func-types-title">Types</h3> <xsl:choose> <xsl:when test="$output_subtypes"> @@ -327,13 +327,13 @@ <xsl:for-each select="$subtype"> <xsl:variable name="tname" select="typename"/> - <div class="REFTYPES"> - <span class="bold_code"> - <xsl:apply-templates select="string" mode="local_type"> - <xsl:with-param name="local_types" select="$local_types"/> - <xsl:with-param name="global_types" select="$global_types"/> - </xsl:apply-templates> - </span> + <div class="REFTYPES rt-1"> + <span class="bold_code bc-2"> + <xsl:apply-templates select="string" mode="local_type"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> + </span> </div> <xsl:apply-templates select="$type_desc[@variable = $tname]"/> </xsl:for-each> @@ -345,7 +345,7 @@ <xsl:param name="global_types"/> <xsl:for-each select="$local_types"> - <div class="REFTYPES"> + <div class="REFTYPES rt-2"> <xsl:call-template name="type_name"> <xsl:with-param name="mode" select="'local_type'"/> <xsl:with-param name="local_types" select="$local_types"/> @@ -366,7 +366,7 @@ <!-- Similar to <d> --> <xsl:template match="type_desc"> - <div class="REFBODY"> + <div class="REFBODY rb-1"> <xsl:apply-templates/> </div> </xsl:template> @@ -375,7 +375,7 @@ <xsl:template match="all_etypes"> <xsl:for-each select= "$i//type"> <pre> - <span class="bold_code"> + <span class="bold_code bc-3"> <xsl:apply-templates select="typedecl"/> </span><xsl:text> </xsl:text> @@ -386,15 +386,17 @@ <!-- Datatypes --> <xsl:template match="datatypes"> <h3> - <xsl:text>DATA TYPES</xsl:text> + <a name="data-types" href="#data-types"><xsl:text>Data Types</xsl:text></a> </h3> - <xsl:apply-templates/> + <div class="data-types-body"> + <xsl:apply-templates/> + </div> </xsl:template> <!-- Datatype --> <xsl:template match="datatype"> - <p><xsl:apply-templates select="name"/></p> - <xsl:apply-templates select="desc"/> + <div class="data-type-name"><xsl:apply-templates select="name"/></div> + <div class="data-type-desc"><xsl:apply-templates select="desc"/></div> </xsl:template> <!-- The "mode" attribute of apply has been used to separate the case @@ -454,7 +456,7 @@ </xsl:template> <xsl:template match="typehead"> - <span class="bold_code"> + <span class="bold_code bc-4"> <xsl:apply-templates/> </span><br/> </xsl:template> @@ -462,7 +464,7 @@ <xsl:template match="typehead" mode="local_type"> <xsl:param name="local_types"/> <xsl:param name="global_types"/> - <span class="bold_code"> + <span class="bold_code bc-5"> <xsl:apply-templates mode="local_type"> <xsl:with-param name="local_types" select="$local_types"/> <xsl:with-param name="global_types" select="$global_types"/> @@ -473,7 +475,7 @@ <!-- Not used right now --> <!-- local_defs --> <xsl:template match="local_defs"> - <div class="REFBODY"> + <div class="REFBODY rb-2"> <xsl:apply-templates> </xsl:apply-templates> </div> @@ -481,8 +483,8 @@ <!-- Not used right now --> <xsl:template match="local_def"> - <div class="REFTYPES"> - <span class="bold_code"> + <div class="REFTYPES rt-3"> + <span class="bold_code bc-6"> <xsl:apply-templates/> </span> </div> @@ -659,7 +661,7 @@ </xsl:otherwise> </xsl:choose> </head> - <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"> + <body> <div id="container"> <script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/> @@ -734,6 +736,8 @@ </div> </div> + <script type="text/javascript"><xsl:text>window.__otpTopDocDir = '</xsl:text><xsl:value-of select="$topdocdir"/><xsl:text>/js/';</xsl:text></script> + <script type="text/javascript" src="{$topdocdir}/js/highlight.js"/> </body> </html> </xsl:template> @@ -796,36 +800,42 @@ </xsl:template> - <xsl:template name="menu_top"> + <xsl:template name="erlang_logo"> <xsl:choose> <xsl:when test="string-length($logo) > 0"> - <img alt="Erlang logo" src="{$topdocdir}/{$logo}"/> + <div class="erlang-logo-wrapper"> + <a href="{$topdocdir}/index.html"><img alt="Erlang Logo" src="{$topdocdir}/{$logo}" class="erlang-logo"/></a> + </div> </xsl:when> <xsl:otherwise> - <img alt="Erlang logo" src="{$topdocdir}/erlang-logo.png"/> + <div class="erlang-logo-wrapper"> + <a href="{$topdocdir}/index.html"><img alt="Erlang Logo" src="{$topdocdir}/erlang-logo.png" class="erlang-logo"/></a> + </div> </xsl:otherwise> </xsl:choose> - <br/> - <small> + </xsl:template> + + <xsl:template name="menu_top"> + <ul class="panel-sections"> <xsl:if test="boolean(/book/parts/part)"> - <a href="users_guide.html">User's Guide</a><br/> + <li><a href="users_guide.html">User's Guide</a></li> </xsl:if> <xsl:if test="boolean(/book/applications)"> - <a href="index.html">Reference Manual</a><br/> + <li><a href="index.html">Reference Manual</a></li> </xsl:if> <xsl:if test="boolean(/book/releasenotes)"> - <a href="release_notes.html">Release Notes</a><br/> + <li><a href="release_notes.html">Release Notes</a></li> </xsl:if> <xsl:choose> <xsl:when test="string-length($pdfname) > 0"> - <a href="{$pdfdir}/{$pdfname}.pdf">PDF</a><br/> + <li><a href="{$pdfdir}/{$pdfname}.pdf">PDF</a></li> </xsl:when> <xsl:otherwise> - <a href="{$pdfdir}/{$appname}-{$appver}.pdf">PDF</a><br/> + <li><a href="{$pdfdir}/{$appname}-{$appver}.pdf">PDF</a></li> </xsl:otherwise> </xsl:choose> - <a href="{$topdocdir}/index.html">Top</a> - </small> + <li><a href="{$topdocdir}/index.html">Top</a></li> + </ul> </xsl:template> <xsl:template name="menu_middle"> @@ -841,10 +851,11 @@ </xsl:when> </xsl:choose> </small --> - <br/> - <a href="javascript:openAllFlips()">Expand All</a><br/> - <a href="javascript:closeAllFlips()">Contract All</a> + <ul class="expand-collapse-items"> + <li><a href="javascript:openAllFlips()">Expand All</a></li> + <li><a href="javascript:closeAllFlips()">Contract All</a></li> + </ul> </xsl:template> @@ -934,7 +945,7 @@ <xsl:value-of select="title"/> </a> </h3> - <div class="REFBODY"> + <div class="REFBODY rb-3"> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> </xsl:apply-templates> @@ -948,7 +959,7 @@ <h4> <xsl:value-of select="title"/> </h4> - <div class="REFBODY"> + <div class="REFBODY rb-4"> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> </xsl:apply-templates> @@ -1129,7 +1140,8 @@ <xsl:variable name="tabnum"> <xsl:number level="any" from="chapter" count="table"/> </xsl:variable> - <table border="1" cellpadding="2" cellspacing="0"> + <div class="doc-table-wrapper"> + <table class="doc-table"> <!-- tbody--> <xsl:apply-templates select="row"> <xsl:with-param name="chapnum" select="$chapnum"/> @@ -1141,6 +1153,7 @@ <xsl:with-param name="chapnum" select="$chapnum"/> <xsl:with-param name="tabnum" select="$tabnum"/> </xsl:apply-templates> + </div> </xsl:template> <xsl:template match="row"> @@ -1160,11 +1173,11 @@ <xsl:param name="chapnum"/> <xsl:param name="tabnum"/> - <em>Table + <p class="doc-table-caption">Table <xsl:value-of select="$chapnum"/>.<xsl:value-of select="$tabnum"/>:   <xsl:apply-templates/> - </em> + </p> </xsl:template> @@ -1175,12 +1188,14 @@ <xsl:number level="any" from="chapter" count="image"/> </xsl:variable> - <img alt="IMAGE MISSING" src="{@file}"/><br/> + <div class="doc-image-wrapper"> + <img alt="IMAGE MISSING" src="{@file}" class="doc-image"/> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> <xsl:with-param name="fignum" select="$fignum"/> </xsl:apply-templates> + </div> </xsl:template> @@ -1190,11 +1205,11 @@ <xsl:param name="chapnum"/> <xsl:param name="fignum"/> - <p><em>Figure + <p class="doc-image-caption">Figure <xsl:value-of select="$chapnum"/>.<xsl:value-of select="$fignum"/>:   <xsl:apply-templates/> - </em></p> + </p> </xsl:template> @@ -1244,21 +1259,17 @@ <div id="leftnav"> <div class="innertube"> - <xsl:call-template name="menu_top"/> + <xsl:call-template name="erlang_logo"/> - <p> - <strong><xsl:value-of select="/book/header/title"/></strong><br/> - <strong>User's Guide</strong><br/> - <small>Version <xsl:value-of select="$appver"/></small> - </p> + <p class="section-title"><xsl:value-of select="/book/header/title"/></p> + <p class="section-subtitle">User's Guide</p> + <p class="section-version">Version <xsl:value-of select="$appver"/></p> + + <xsl:call-template name="menu_top"/> <xsl:call-template name="menu_middle"/> - <p> - <small> - <strong>Chapters</strong> - </small> - </p> + <h3>Chapters</h3> <ul class="flipMenu" imagepath="{$topdocdir}/js/flipmenu"> <xsl:call-template name="menu.chapter"> @@ -1409,21 +1420,17 @@ <div id="leftnav"> <div class="innertube"> - <xsl:call-template name="menu_top"/> + <xsl:call-template name="erlang_logo"/> - <p> - <strong><xsl:value-of select="/book/header/title"/></strong><br/> - <strong>Reference Manual</strong><br/> - <small>Version <xsl:value-of select="$appver"/></small> - </p> + <p class="section-title"><xsl:value-of select="/book/header/title"/></p> + <p class="section-subtitle">Reference Manual</p> + <p class="section-version">Version <xsl:value-of select="$appver"/></p> + + <xsl:call-template name="menu_top"/> <xsl:call-template name="menu_middle"/> - <p> - <small> - <strong>Table of Contents</strong> - </small> - </p> + <h3>Table of Contents</h3> <ul class="flipMenu"> <xsl:call-template name="menu.ref2"> @@ -1773,8 +1780,8 @@ <!-- Module --> <xsl:template match="module"> <xsl:param name="partnum"/> - <h3>MODULE</h3> - <div class="REFBODY"> + <h3><a name="module" href="#module">Module</a></h3> + <div class="REFBODY module-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1785,8 +1792,8 @@ <!-- Modulesummary --> <xsl:template match="modulesummary"> <xsl:param name="partnum"/> - <h3>MODULE SUMMARY</h3> - <div class="REFBODY"> + <h3><a name="module-sumary" href="#module-sumary">Module Summary</a></h3> + <div class="REFBODY module-summary-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1796,8 +1803,8 @@ <!-- Lib --> <xsl:template match="lib"> <xsl:param name="partnum"/> - <h3>C LIBRARY</h3> - <div class="REFBODY"> + <h3><a name="c-library" href="#c-library">C Library</a></h3> + <div class="REFBODY c-library-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1808,8 +1815,8 @@ <!-- Libsummary --> <xsl:template match="libsummary"> <xsl:param name="partnum"/> - <h3>LIBRARY SUMMARY</h3> - <div class="REFBODY"> + <h3><a name="library-sumary" href="#library-sumary">Library Summary</a></h3> + <div class="REFBODY library-summary-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1819,8 +1826,8 @@ <!-- Com --> <xsl:template match="com"> <xsl:param name="partnum"/> - <h3>COMMAND</h3> - <div class="REFBODY"> + <h3><a name="command" href="#command">Command</a></h3> + <div class="REFBODY command-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1831,8 +1838,8 @@ <!-- Comsummary --> <xsl:template match="comsummary"> <xsl:param name="partnum"/> - <h3>COMMAND SUMMARY</h3> - <div class="REFBODY"> + <h3><a name="command-summary" href="#command-summary">Command Summary</a></h3> + <div class="REFBODY command-summary-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1842,8 +1849,8 @@ <!-- File --> <xsl:template match="file"> <xsl:param name="partnum"/> - <h3>FILE</h3> - <div class="REFBODY"> + <h3><a name="file" href="#file">File</a></h3> + <div class="REFBODY file-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1854,8 +1861,8 @@ <!-- Filesummary --> <xsl:template match="filesummary"> <xsl:param name="partnum"/> - <h3>FILE SUMMARY</h3> - <div class="REFBODY"> + <h3><a name="file-summary" href="#file-summary">File Summary</a></h3> + <div class="REFBODY file-summary-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1866,8 +1873,8 @@ <!-- App --> <xsl:template match="app"> <xsl:param name="partnum"/> - <h3>APPLICATION</h3> - <div class="REFBODY"> + <h3><a name="application" href="#application">Application</a></h3> + <div class="REFBODY application-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1878,8 +1885,8 @@ <!-- Appsummary --> <xsl:template match="appsummary"> <xsl:param name="partnum"/> - <h3>APPLICATION SUMMARY</h3> - <div class="REFBODY"> + <h3><a name="application-summary" href="#application-summary">Application Summary</a></h3> + <div class="REFBODY application-summary-body"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -1889,8 +1896,8 @@ <!-- Description --> <xsl:template match="description"> <xsl:param name="partnum"/> - <h3>DESCRIPTION</h3> - <div class="REFBODY"> + <h3><a name="description" href="#description">Description</a></h3> + <div class="REFBODY description-body"> <p> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> @@ -1903,13 +1910,13 @@ <xsl:template match="funcs"> <xsl:param name="partnum"/> - <h3> - <xsl:text>EXPORTS</xsl:text> - </h3> + <h3><a name="exports" href="#exports"><xsl:text>Exports</xsl:text></a></h3> - <xsl:apply-templates> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> + <div class="exports-body"> + <xsl:apply-templates> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> + </div> </xsl:template> @@ -1980,7 +1987,7 @@ <xsl:choose> <xsl:when test="ancestor::cref"> <a name="{substring-before(nametext, '(')}"> - <span class="bold_code"> + <span class="bold_code bc-7"> <xsl:value-of select="ret"/> <xsl:call-template name="maybe-space-after-ret"> <xsl:with-param name="s" select="ret"/> @@ -2007,15 +2014,15 @@ </xsl:variable> <xsl:choose> <xsl:when test="ancestor::datatype"> - <a name="type-{$fname}"></a><span class="bold_code"><xsl:apply-templates/></span><br/> + <a name="type-{$fname}"></a><span class="bold_code bc-8"><xsl:apply-templates/></span><br/> </xsl:when> <xsl:otherwise> - <a name="{$fname}-{$arity}"></a><span class="bold_code"><xsl:apply-templates/></span><br/> + <a name="{$fname}-{$arity}"></a><span class="bold_code fun-type"><xsl:apply-templates/></span><br/> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> - <span class="bold_code"><xsl:value-of select="."/></span> + <span class="bold_code bc-10"><xsl:value-of select="."/></span> </xsl:otherwise> </xsl:choose> @@ -2040,12 +2047,13 @@ <!-- The case where @name != 0 is taken care of in "type_name" --> <xsl:if test="string-length(@name) = 0 and string-length(@variable) = 0"> - <div class="REFBODY"><p>Types:</p> + <div class="REFBODY rb-5"> + <h3 class="func-types-title">Types</h3> - <xsl:apply-templates> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> - </div> + <xsl:apply-templates> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> + </div> </xsl:if> @@ -2055,8 +2063,8 @@ <!-- V --> <xsl:template match="v"> <xsl:param name="partnum"/> - <div class="REFTYPES"> - <span class="bold_code"> + <div class="REFTYPES rt-4"> + <span class="bold_code fun-param-type"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -2067,7 +2075,7 @@ <!-- D --> <xsl:template match="d"> <xsl:param name="partnum"/> - <div class="REFBODY"> + <div class="REFBODY rb-6"> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> @@ -2077,7 +2085,7 @@ <!-- Desc --> <xsl:template match="desc"> <xsl:param name="partnum"/> - <div class="REFBODY"> + <div class="REFBODY rb-7"> <p> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> @@ -2094,7 +2102,7 @@ <xsl:template match="input"> - <span class="bold_code"><xsl:apply-templates/></span> + <span class="bold_code bc-12"><xsl:apply-templates/></span> </xsl:template> <xsl:template match="seealso"> @@ -2113,7 +2121,7 @@ <xsl:when test="string-length($app_part) > 0"> <!-- "AppPart:ModPart#Linkpart" --> <xsl:variable name="mod_part"><xsl:value-of select="substring-after($filepart, ':')"/></xsl:variable> - <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app_part}','{$mod_part}.html#{$linkpart}');"><xsl:apply-templates/></a></span> + <span class="bold_code bc-13"><a href="javascript:erlhref('{$topdocdir}/../','{$app_part}','{$mod_part}.html#{$linkpart}');"><xsl:apply-templates/></a></span> </xsl:when> <xsl:otherwise> <!-- "Filepart#Linkpart (there is no ':' in Filepart) --> @@ -2133,7 +2141,7 @@ <xsl:variable name="app" select="key('mod2app', $filepart)"/> <xsl:choose> <xsl:when test="string-length($app) > 0"> - <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app}','{$filepart}.html#{$linkpart}');"><xsl:value-of select="$this"/></a></span> + <span class="bold_code bc-14"><a href="javascript:erlhref('{$topdocdir}/../','{$app}','{$filepart}.html#{$linkpart}');"><xsl:value-of select="$this"/></a></span> </xsl:when> <xsl:otherwise> <!-- Unknown application --> @@ -2146,11 +2154,11 @@ </xsl:when> <xsl:when test="string-length($linkpart) > 0"> <!-- Still Filepart#Linkpart (there is no ':' in Filepart --> - <span class="bold_code"><a href="{$filepart}.html#{$linkpart}"><xsl:apply-templates/></a></span> + <span class="bold_code bc-15"><a href="{$filepart}.html#{$linkpart}"><xsl:apply-templates/></a></span> </xsl:when> <xsl:otherwise> <!-- "Filepart#" (there is no ':' in Filepart --> - <span class="bold_code"><a href="{$filepart}.html"><xsl:apply-templates/></a></span> + <span class="bold_code bc-16"><a href="{$filepart}.html"><xsl:apply-templates/></a></span> </xsl:otherwise> </xsl:choose> </xsl:otherwise> @@ -2158,7 +2166,7 @@ </xsl:when> <!-- string-length($filepart) > 0 --> <xsl:when test="string-length($linkpart) > 0"> <!-- "#Linkpart" --> - <span class="bold_code"><a href="#{$linkpart}"><xsl:apply-templates/></a></span> + <span class="bold_code bc-17"><a href="#{$linkpart}"><xsl:apply-templates/></a></span> </xsl:when> <xsl:otherwise> <!-- "AppPart:Mod" or "Mod" (there is no '#') --> @@ -2168,11 +2176,11 @@ <xsl:when test="string-length($app_part) > 0"> <!-- "App:Mod" --> <xsl:variable name="mod_part"><xsl:value-of select="substring-after(@marker, ':')"/></xsl:variable> - <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app_part}','{$mod_part}.html');"><xsl:apply-templates/></a></span> + <span class="bold_code bc-18"><a href="javascript:erlhref('{$topdocdir}/../','{$app_part}','{$mod_part}.html');"><xsl:apply-templates/></a></span> </xsl:when> <xsl:otherwise> <!-- "Mod" --> - <span class="bold_code"><a href="{@marker}.html"><xsl:apply-templates/></a></span> + <span class="bold_code bc-19"><a href="{@marker}.html"><xsl:apply-templates/></a></span> </xsl:otherwise> </xsl:choose> </xsl:otherwise> @@ -2181,7 +2189,7 @@ </xsl:template> <xsl:template match="url"> - <span class="bold_code"><a href="{@href}"><xsl:apply-templates/></a></span> + <span class="bold_code bc-20"><a href="{@href}"><xsl:apply-templates/></a></span> </xsl:template> <xsl:template match="marker"> @@ -2256,21 +2264,17 @@ <div id="leftnav"> <div class="innertube"> - <xsl:call-template name="menu_top"/> + <xsl:call-template name="erlang_logo"/> - <p> - <strong><xsl:value-of select="/book/header/title"/></strong><br/> - <strong>Release Notes</strong><br/> - <small>Version <xsl:value-of select="$appver"/></small> - </p> + <p class="section-title"><xsl:value-of select="/book/header/title"/></p> + <p class="section-subtitle">Release Notes</p> + <p class="section-version">Version <xsl:value-of select="$appver"/></p> + + <xsl:call-template name="menu_top"/> <xsl:call-template name="menu_middle"/> - <p> - <small> - <strong>Chapters</strong> - </small> - </p> + <h3>Chapters</h3> <ul class="flipMenu" imagepath="{$topdocdir}/js/flipmenu"> <xsl:call-template name="menu.chapter"> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 9321750d44..a3a936322a 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -1080,9 +1080,6 @@ type(hipe_bifs, ref_get, 1, Xs, Opaques) -> strict(hipe_bifs, ref_get, 1, Xs, fun (_) -> t_immediate() end, Opaques); type(hipe_bifs, ref_set, 2, Xs, Opaques) -> strict(hipe_bifs, ref_set, 2, Xs, fun (_) -> t_nil() end, Opaques); -type(hipe_bifs, remove_refs_from, 1, Xs, Opaques) -> - strict(hipe_bifs, remove_refs_from, 1, Xs, - fun (_) -> t_atom('ok') end, Opaques); type(hipe_bifs, set_funinfo_native_address, 3, Xs, Opaques) -> strict(hipe_bifs, set_funinfo_native_address, 3, Xs, fun (_) -> t_nil() end, Opaques); @@ -2518,8 +2515,6 @@ arg_types(hipe_bifs, ref_get, 1) -> [t_hiperef()]; arg_types(hipe_bifs, ref_set, 2) -> [t_hiperef(), t_immediate()]; -arg_types(hipe_bifs, remove_refs_from, 1) -> - [t_sup([t_mfa(), t_atom('all')])]; arg_types(hipe_bifs, set_funinfo_native_address, 3) -> arg_types(hipe_bifs, set_native_address, 3); arg_types(hipe_bifs, commit_patch_load, 1) -> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 440607e99d..14d802085f 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -250,7 +250,7 @@ init({call, From}, {start, Timeout}, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - HelloVersion = dtls_record:lowest_protocol_version(SslOpts#ssl_options.versions), + HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), State1 = prepare_flight(State0#state{negotiated_version = Version}), {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}), State3 = State2#state{negotiated_version = Version, %% Requested version diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index d3ba90a226..5fb1b61489 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -66,7 +66,7 @@ client_hello(Host, Port, Cookie, ConnectionStates, CipherSuites = ssl_handshake:available_suites(UserSuites, TLSVersion), Extensions = ssl_handshake:client_hello_extensions(Host, TLSVersion, CipherSuites, - SslOpts, ConnectionStates, Renegotiation), + SslOpts, ConnectionStates, Renegotiation), Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), @@ -252,7 +252,7 @@ enc_handshake(#server_hello{} = HandshakeMsg, Version) -> {Type, <<?BYTE(DTLSMajor), ?BYTE(DTLSMinor), Rest/binary>>}; enc_handshake(HandshakeMsg, Version) -> - ssl_handshake:encode_handshake(HandshakeMsg, Version). + ssl_handshake:encode_handshake(HandshakeMsg, dtls_v1:corresponding_tls_version(Version)). bin_fragments(Bin, Size) -> bin_fragments(Bin, size(Bin), Size, 0, []). diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 049f83e49e..de6b6e400f 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -44,7 +44,7 @@ -export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, highest_protocol_version/2, is_higher/2, supported_protocol_versions/0, - is_acceptable_version/2]). + is_acceptable_version/2, hello_version/2]). -export([save_current_connection_state/2, next_epoch/2]). @@ -402,6 +402,15 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, write) -> Epoch. +-spec hello_version(dtls_version(), [dtls_version()]) -> dtls_version(). +hello_version(Version, Versions) -> + case dtls_v1:corresponding_tls_version(Version) of + TLSVersion when TLSVersion >= {3, 3} -> + Version; + _ -> + lowest_protocol_version(Versions) + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index b3d08bdfbe..de5ca3dddd 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1031,15 +1031,15 @@ validate_option(protocol, Value = dtls) -> validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). -handle_hashsigns_option(Value, {Major, Minor} = Version) when is_list(Value) - andalso Major >= 3 andalso Minor >= 3-> +handle_hashsigns_option(Value, Version) when is_list(Value) + andalso Version >= {3, 3} -> case tls_v1:signature_algs(Version, Value) of [] -> throw({error, {options, no_supported_algorithms, {signature_algs, Value}}}); _ -> Value end; -handle_hashsigns_option(_, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3-> +handle_hashsigns_option(_, Version) when Version >= {3, 3} -> handle_hashsigns_option(tls_v1:default_signature_algs(Version), Version); handle_hashsigns_option(_, _Version) -> undefined. diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index df9b9e8a63..cc77aa6bf4 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1689,7 +1689,7 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer, ssl_record:pending_connection_state(ConnectionStates0, read), TLSVersion = ssl:tls_version(Version), HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, - TLSVersion, [TLSVersion]), + TLSVersion), Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, TLSVersion), State = Connection:queue_handshake(Msg, State0), diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index cb61c82334..954b0875ce 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -64,7 +64,7 @@ ]). %% Cipher suites handling --export([available_suites/2, available_signature_algs/3, cipher_suites/2, +-export([available_suites/2, available_signature_algs/2, cipher_suites/2, select_session/11, supported_ecc/1, available_signature_algs/4]). %% Extensions handling @@ -121,8 +121,7 @@ server_hello_done() -> client_hello_extensions(Host, Version, CipherSuites, #ssl_options{signature_algs = SupportedHashSigns, - eccs = SupportedECCs, - versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) -> + eccs = SupportedECCs} = SslOpts, ConnectionStates, Renegotiation) -> {EcPointFormats, EllipticCurves} = case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of true -> @@ -136,7 +135,7 @@ client_hello_extensions(Host, Version, CipherSuites, renegotiation_info = renegotiation_info(tls_record, client, ConnectionStates, Renegotiation), srp = SRP, - signature_algs = available_signature_algs(SupportedHashSigns, Version, AllVersions), + signature_algs = available_signature_algs(SupportedHashSigns, Version), ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves, alpn = encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation), @@ -2150,16 +2149,11 @@ is_member(Suite, SupportedSuites) -> select_compression(_CompressionMetodes) -> ?NULL. -available_signature_algs(undefined, _, _) -> +available_signature_algs(undefined, _) -> undefined; -available_signature_algs(SupportedHashSigns, {Major, Minor}, AllVersions) when Major >= 3 andalso Minor >= 3 -> - case tls_record:lowest_protocol_version(AllVersions) of - {3, 3} -> - #hash_sign_algos{hash_sign_algos = SupportedHashSigns}; - _ -> - undefined - end; -available_signature_algs(_, _, _) -> +available_signature_algs(SupportedHashSigns, Version) when Version >= {3, 3} -> + #hash_sign_algos{hash_sign_algos = SupportedHashSigns}; +available_signature_algs(_, _) -> undefined. psk_secret(PSKIdentity, PSKLookup) -> @@ -2346,11 +2340,11 @@ bad_key(#'RSAPrivateKey'{}) -> bad_key(#'ECPrivateKey'{}) -> unacceptable_ecdsa_key. -available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when - (Major >= 3) andalso (Minor >= 3) -> +available_signature_algs(undefined, SupportedHashSigns, _, Version) when + Version >= {3,3} -> SupportedHashSigns; available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns, - _, {Major, Minor}) when (Major >= 3) andalso (Minor >= 3) -> + _, Version) when Version >= {3,3} -> sets:to_list(sets:intersection(sets:from_list(ClientHashSigns), sets:from_list(SupportedHashSigns))); available_signature_algs(_, _, _, _) -> diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index bda6bf0349..ce440d1e71 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -220,7 +220,7 @@ init({call, From}, {start, Timeout}, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions), + HelloVersion = tls_record:hello_version(Version, SslOpts#ssl_options.versions), Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0, V2HComp), diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 065c6dc8a7..2d6407677f 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -43,7 +43,7 @@ -export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, highest_protocol_version/2, is_higher/2, supported_protocol_versions/0, - is_acceptable_version/1, is_acceptable_version/2]). + is_acceptable_version/1, is_acceptable_version/2, hello_version/2]). %% Decoding -export([decode_cipher_text/3]). @@ -277,6 +277,7 @@ supported_protocol_versions([_|_] = Vsns) -> NewVsns end end. + %%-------------------------------------------------------------------- %% %% Description: ssl version 2 is not acceptable security risks are too big. @@ -296,6 +297,11 @@ is_acceptable_version({N,_} = Version, Versions) is_acceptable_version(_,_) -> false. +-spec hello_version(tls_version(), [tls_version()]) -> tls_version(). +hello_version(Version, _) when Version >= {3, 3} -> + Version; +hello_version(_, Versions) -> + lowest_protocol_version(Versions). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- 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/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml index 691a039e34..e70f347479 100644 --- a/lib/stdlib/doc/src/gen_fsm.xml +++ b/lib/stdlib/doc/src/gen_fsm.xml @@ -4,14 +4,14 @@ <erlref> <header> <copyright> - <year>1996</year><year>2016</year> + <year>1996-2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -29,945 +29,176 @@ <rev></rev> </header> <module>gen_fsm</module> - <modulesummary>Generic finite state machine behavior.</modulesummary> - <description> - <note> - <p> - There is a new behaviour - <seealso marker="gen_statem"><c>gen_statem</c></seealso> - that is intended to replace <c>gen_fsm</c> for new code. - <c>gen_fsm</c> will not be removed for the foreseeable future - to keep old state machine implementations running. - </p> - </note> - <p>This behavior module provides a finite state machine. - A generic finite state machine process (<c>gen_fsm</c>) implemented - using this module has a standard set of interface functions - and includes functionality for tracing and error reporting. It - also fits into an OTP supervision tree. For more information, see - <seealso marker="doc/design_principles:fsm">OTP Design Principles</seealso>. - </p> - - <p>A <c>gen_fsm</c> process assumes all specific parts to be located in a - callback module exporting a predefined set of functions. The relationship - between the behavior functions and the callback functions is as - follows:</p> - - <pre> -gen_fsm module Callback module --------------- --------------- -gen_fsm:start -gen_fsm:start_link -----> Module:init/1 - -gen_fsm:stop -----> Module:terminate/3 - -gen_fsm:send_event -----> Module:StateName/2 - -gen_fsm:send_all_state_event -----> Module:handle_event/3 - -gen_fsm:sync_send_event -----> Module:StateName/3 - -gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 - -- -----> Module:handle_info/3 - -- -----> Module:terminate/3 + <modulesummary>Deprecated and replaced by <seealso marker="gen_statem"><c>gen_statem</c></seealso> </modulesummary> -- -----> Module:code_change/4</pre> - - <p>If a callback function fails or returns a bad value, the <c>gen_fsm</c> - process terminates.</p> - - <p>A <c>gen_fsm</c> process handles system messages as described in - <seealso marker="sys"><c>sys(3)</c></seealso>. The <c>sys</c> module - can be used for debugging a <c>gen_fsm</c> process.</p> - - <p>Notice that a <c>gen_fsm</c> process does not trap exit signals - automatically, this must be explicitly initiated in the callback - module.</p> - - <p>Unless otherwise stated, all functions in this module fail if - the specified <c>gen_fsm</c> process does not exist or if bad arguments - are specified.</p> - - <p>The <c>gen_fsm</c> process can go into hibernation - (see <seealso marker="erts:erlang#hibernate/3"> - <c>erlang:hibernate/3</c></seealso>) if a callback function - specifies <c>'hibernate'</c> instead of a time-out value. This - can be useful if the server is expected to be idle for a long - time. However, use this feature with care, as hibernation - implies at least two garbage collections (when hibernating and - shortly after waking up) and is not something you want to do - between each call to a busy state machine.</p> + <description> + <p> Deprecated and replaced by <seealso marker="gen_statem"><c>gen_statem</c></seealso> </p> </description> - - <funcs> - <func> - <name>cancel_timer(Ref) -> RemainingTime | false</name> - <fsummary>Cancel an internal timer in a generic FSM.</fsummary> - <type> - <v>Ref = reference()</v> - <v>RemainingTime = integer()</v> - </type> - <desc> - <p>Cancels an internal timer referred by <c>Ref</c> in the - <c>gen_fsm</c> process that calls this function.</p> - <p><c>Ref</c> is a reference returned from - <seealso marker="#send_event_after/2"> - <c>send_event_after/2</c></seealso> or - <seealso marker="#start_timer/2"><c>start_timer/2</c></seealso>.</p> - <p>If the timer has already timed out, but the event not yet - been delivered, it is cancelled as if it had <em>not</em> - timed out, so there is no false timer event after - returning from this function.</p> - <p>Returns the remaining time in milliseconds until the timer would - have expired if <c>Ref</c> referred to an active timer, otherwise - <c>false</c>.</p> - </desc> - </func> - - <func> - <name>enter_loop(Module, Options, StateName, StateData)</name> - <name>enter_loop(Module, Options, StateName, StateData, FsmName)</name> - <name>enter_loop(Module, Options, StateName, StateData, Timeout)</name> - <name>enter_loop(Module, Options, StateName, StateData, FsmName, Timeout)</name> - <fsummary>Enter the <c>gen_fsm</c> receive loop.</fsummary> - <type> - <v>Module = atom()</v> - <v>Options = [Option]</v> - <v> Option = {debug,Dbgs}</v> - <v> Dbgs = [Dbg]</v> - <v> Dbg = trace | log | statistics</v> - <v> | {log_to_file,FileName} | {install,{Func,FuncState}}</v> - <v>StateName = atom()</v> - <v>StateData = term()</v> - <v>FsmName = {local,Name} | {global,GlobalName}</v> - <v> | {via,Module,ViaName}</v> - <v> Name = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Timeout = int() | infinity</v> - </type> - <desc> - <p>Makes an existing process into a <c>gen_fsm</c> process. - Does not return, - instead the calling process enters the <c>gen_fsm</c> receive - loop and becomes a <c>gen_fsm</c> process. The process <em>must</em> - have been started using one of the start functions in - <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>. The user is - responsible for any initialization of the process, including - registering a name for it.</p> - <p>This function is useful when a more complex initialization - procedure is needed than the <c>gen_fsm</c> behavior provides.</p> - <p><c>Module</c>, <c>Options</c>, and <c>FsmName</c> have - the same meanings as when calling - <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>. - However, if <c>FsmName</c> is specified, the process must have - been registered accordingly <em>before</em> this function is - called.</p> - <p><c>StateName</c>, <c>StateData</c>, and <c>Timeout</c> have - the same meanings as in the return value of - <seealso marker="#Moduleinit"><c>Module:init/1</c></seealso>. - The callback module <c>Module</c> does not need to - export an <c>init/1</c> function.</p> - <p>The function fails if the calling process was not started by a - <c>proc_lib</c> start function, or if it is not registered - according to <c>FsmName</c>.</p> - </desc> - </func> - - <func> - <name>reply(Caller, Reply) -> Result</name> - <fsummary>Send a reply to a caller.</fsummary> - <type> - <v>Caller - see below</v> - <v>Reply = term()</v> - <v>Result = term()</v> - </type> - <desc> - <p>This function can be used by a <c>gen_fsm</c> process to - explicitly send a reply to a client process that called - <seealso marker="#sync_send_event/2"> - <c>sync_send_event/2,3</c></seealso> or - <seealso marker="#sync_send_all_state_event/2"> - <c>sync_send_all_state_event/2,3</c></seealso> - when the reply cannot be defined in the return value of - <seealso marker="#Module:StateName/3"> - <c>Module:StateName/3</c></seealso> or - <seealso marker="#Module:handle_sync_event/4"> - <c>Module:handle_sync_event/4</c></seealso>.</p> - <p><c>Caller</c> must be the <c>From</c> argument provided to - the callback function. <c>Reply</c> is any term - given back to the client as the return value of - <c>sync_send_event/2,3</c> or - <c>sync_send_all_state_event/2,3</c>.</p> - <p>Return value <c>Result</c> is not further defined, and - is always to be ignored.</p> - </desc> - </func> - - <func> - <name>send_all_state_event(FsmRef, Event) -> ok</name> - <fsummary>Send an event asynchronously to a generic FSM.</fsummary> - <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName}</v> - <v> | {via,Module,ViaName} | pid()</v> - <v> Name = Node = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Event = term()</v> - </type> - <desc> - <p>Sends an event asynchronously to the <c>FsmRef</c> of the - <c>gen_fsm</c> process and returns <c>ok</c> immediately. - The <c>gen_fsm</c> process calls - <seealso marker="#Module:handle_event/3"> - <c>Module:handle_event/3</c></seealso> to handle the event.</p> - <p>For a description of the arguments, see - <seealso marker="#send_event/2"><c>send_event/2</c></seealso>.</p> - <p>The difference between <c>send_event/2</c> and - <c>send_all_state_event/2</c> is which callback function is - used to handle the event. This function is useful when - sending events that are handled the same way in every state, - as only one <c>handle_event</c> clause is needed to handle - the event instead of one clause in each state name function.</p> - </desc> - </func> - - <func> - <name>send_event(FsmRef, Event) -> ok</name> - <fsummary>Send an event asynchronously to a generic FSM.</fsummary> - <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName}</v> - <v> | {via,Module,ViaName} | pid()</v> - <v> Name = Node = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Event = term()</v> - </type> - <desc> - <p>Sends an event asynchronously to the <c>FsmRef</c> of the - <c>gen_fsm</c> process - and returns <c>ok</c> immediately. The <c>gen_fsm</c> process calls - <seealso marker="#Module:StateName/2"> - <c>Module:StateName/2</c></seealso> to handle the event, where - <c>StateName</c> is the name of the current state of - the <c>gen_fsm</c> process.</p> - <p><c>FsmRef</c> can be any of the following:</p> - <list type="bulleted"> - <item>The pid</item> - <item><c>Name</c>, if the <c>gen_fsm</c> process is locally - registered</item> - <item><c>{Name,Node}</c>, if the <c>gen_fsm</c> process is locally - registered at another node</item> - <item><c>{global,GlobalName}</c>, if the <c>gen_fsm</c> process is - globally registered</item> - <item><c>{via,Module,ViaName}</c>, if the <c>gen_fsm</c> process is - registered through an alternative process registry</item> - </list> - <p><c>Event</c> is any term that is passed as one of - the arguments to <c>Module:StateName/2</c>.</p> - </desc> - </func> - - <func> - <name>send_event_after(Time, Event) -> Ref</name> - <fsummary>Send a delayed event internally in a generic FSM.</fsummary> - <type> - <v>Time = integer()</v> - <v>Event = term()</v> - <v>Ref = reference()</v> - </type> - <desc> - <p>Sends a delayed event internally in the <c>gen_fsm</c> process - that calls this function after <c>Time</c> milliseconds. - Returns immediately a - reference that can be used to cancel the delayed send using - <seealso marker="#cancel_timer/1"><c>cancel_timer/1</c></seealso>.</p> - <p>The <c>gen_fsm</c> process calls - <seealso marker="#Module:StateName/2"> - <c>Module:StateName/2</c></seealso> to handle - the event, where <c>StateName</c> is the name of the current - state of the <c>gen_fsm</c> process at the time the delayed event is - delivered.</p> - <p><c>Event</c> is any term that is passed as one of - the arguments to <c>Module:StateName/2</c>.</p> - </desc> - </func> - - <func> - <name>start(Module, Args, Options) -> Result</name> - <name>start(FsmName, Module, Args, Options) -> Result</name> - <fsummary>Create a standalone <c>gen_fsm</c> process.</fsummary> - <type> - <v>FsmName = {local,Name} | {global,GlobalName}</v> - <v> | {via,Module,ViaName}</v> - <v> Name = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Module = atom()</v> - <v>Args = term()</v> - <v>Options = [Option]</v> - <v> Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v> - <v> Dbgs = [Dbg]</v> - <v> Dbg = trace | log | statistics</v> - <v> | {log_to_file,FileName} | {install,{Func,FuncState}}</v> - <v> SOpts = [term()]</v> - <v>Result = {ok,Pid} | ignore | {error,Error}</v> - <v> Pid = pid()</v> - <v> Error = {already_started,Pid} | term()</v> - </type> - <desc> - <p>Creates a standalone <c>gen_fsm</c> process, that is, a process that - is not part of a supervision tree and thus has no supervisor.</p> - <p>For a description of arguments and return values, see - <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.</p> - </desc> - </func> - - <func> - <name>start_link(Module, Args, Options) -> Result</name> - <name>start_link(FsmName, Module, Args, Options) -> Result</name> - <fsummary>Create a <c>gen_fsm</c> process in a supervision tree. - </fsummary> - <type> - <v>FsmName = {local,Name} | {global,GlobalName}</v> - <v> | {via,Module,ViaName}</v> - <v> Name = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Module = atom()</v> - <v>Args = term()</v> - <v>Options = [Option]</v> - <v> Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v> - <v> Dbgs = [Dbg]</v> - <v> Dbg = trace | log | statistics</v> - <v> | {log_to_file,FileName} | {install,{Func,FuncState}}</v> - <v> SOpts = [SOpt]</v> - <v> SOpt - see erlang:spawn_opt/2,3,4,5</v> - <v>Result = {ok,Pid} | ignore | {error,Error}</v> - <v> Pid = pid()</v> - <v> Error = {already_started,Pid} | term()</v> - </type> - <desc> - <p>Creates a <c>gen_fsm</c> process as part of a supervision tree. - The function is to be called, directly or indirectly, by - the supervisor. For example, it ensures that - the <c>gen_fsm</c> process is linked to the supervisor.</p> - <p>The <c>gen_fsm</c> process calls - <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> to - initialize. To ensure a synchronized startup procedure, - <c>start_link/3,4</c> does not return until - <c>Module:init/1</c> has returned.</p> - <list type="bulleted"> - <item> - <p>If <c>FsmName={local,Name}</c>, the <c>gen_fsm</c> process is - registered locally as <c>Name</c> using <c>register/2</c>.</p> - </item> - <item> - <p>If <c>FsmName={global,GlobalName}</c>, the <c>gen_fsm</c> process - is registered globally as <c>GlobalName</c> using - <seealso marker="kernel:global#register_name/2"> - <c>global:register_name/2</c></seealso>.</p> - </item> - <item> - <p>If <c>FsmName={via,Module,ViaName}</c>, the <c>gen_fsm</c> - process registers with the registry represented by <c>Module</c>. - The <c>Module</c> callback is to export the functions - <c>register_name/2</c>, <c>unregister_name/1</c>, - <c>whereis_name/1</c>, and <c>send/2</c>, which are to behave - like the corresponding functions in - <seealso marker="kernel:global"><c>global</c></seealso>. - Thus, <c>{via,global,GlobalName}</c> is a valid reference.</p> - </item> - </list> - <p>If no name is provided, the <c>gen_fsm</c> process is not - registered.</p> - <p><c>Module</c> is the name of the callback module.</p> - <p><c>Args</c> is any term that is passed as - the argument to <c>Module:init/1</c>.</p> - <p>If option <c>{timeout,Time}</c> is present, the <c>gen_fsm</c> - process is allowed to spend <c>Time</c> milliseconds initializing - or it terminates and the start function returns - <c>{error,timeout}</c>.</p> - <p>If option <c>{debug,Dbgs}</c> is present, the corresponding - <c>sys</c> function is called for each item in <c>Dbgs</c>; see - <seealso marker="sys"><c>sys(3)</c></seealso>.</p> - <p>If option <c>{spawn_opt,SOpts}</c> is present, <c>SOpts</c> is - passed as option list to the <c>spawn_opt</c> BIF that is used to - spawn the <c>gen_fsm</c> process; see - <seealso marker="erts:erlang#spawn_opt/2"> - <c>spawn_opt/2</c></seealso>.</p> - <note> - <p>Using spawn option <c>monitor</c> is not - allowed, it causes the function to fail with reason - <c>badarg</c>.</p> - </note> - <p>If the <c>gen_fsm</c> process is successfully created and - initialized, the function returns <c>{ok,Pid}</c>, where <c>Pid</c> - is the pid of the <c>gen_fsm</c> process. If a process with the - specified <c>FsmName</c> exists already, the function returns - <c>{error,{already_started,Pid}}</c>, where <c>Pid</c> is - the pid of that process.</p> - <p>If <c>Module:init/1</c> fails with <c>Reason</c>, - the function returns <c>{error,Reason}</c>. If - <c>Module:init/1</c> returns <c>{stop,Reason}</c> or - <c>ignore</c>, the process is terminated and the function - returns <c>{error,Reason}</c> or <c>ignore</c>, respectively.</p> - </desc> - </func> - - <func> - <name>start_timer(Time, Msg) -> Ref</name> - <fsummary>Send a time-out event internally in a generic FSM.</fsummary> - <type> - <v>Time = integer()</v> - <v>Msg = term()</v> - <v>Ref = reference()</v> - </type> - <desc> - <p>Sends a time-out event internally in the <c>gen_fsm</c> - process that calls this function after <c>Time</c> milliseconds. - Returns immediately a - reference that can be used to cancel the timer using - <seealso marker="#cancel_timer/1"><c>cancel_timer/1</c></seealso>.</p> - <p>The <c>gen_fsm</c> process calls - <seealso marker="#Module:StateName/2"> - <c>Module:StateName/2</c></seealso> to handle - the event, where <c>StateName</c> is the name of the current - state of the <c>gen_fsm</c> process at the time the time-out - message is delivered.</p> - <p><c>Msg</c> is any term that is passed in the - time-out message, <c>{timeout, Ref, Msg}</c>, as one of - the arguments to <c>Module:StateName/2</c>.</p> - </desc> - </func> - - <func> - <name>stop(FsmRef) -> ok</name> - <name>stop(FsmRef, Reason, Timeout) -> ok</name> - <fsummary>Synchronously stop a generic FSM.</fsummary> - <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName}</v> - <v> | {via,Module,ViaName} | pid()</v> - <v> Node = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Reason = term()</v> - <v>Timeout = int()>0 | infinity</v> - </type> - <desc> - <p>Orders a generic finite state machine to exit with the specified - <c>Reason</c> and waits for it to terminate. The <c>gen_fsm</c> - process calls <seealso marker="#Module:terminate/3"> - <c>Module:terminate/3</c></seealso> before exiting.</p> - <p>The function returns <c>ok</c> if the generic finite state machine - terminates with the expected reason. Any other reason than - <c>normal</c>, <c>shutdown</c>, or <c>{shutdown,Term}</c> causes an - error report to be issued using - <seealso marker="kernel:error_logger#format/2"> - <c>error_logger:format/2</c></seealso>. - The default <c>Reason</c> is <c>normal</c>.</p> - <p><c>Timeout</c> is an integer greater than zero that - specifies how many milliseconds to wait for the generic FSM - to terminate, or the atom <c>infinity</c> to wait - indefinitely. The default value is <c>infinity</c>. If the - generic finite state machine has not terminated within the specified - time, a <c>timeout</c> exception is raised.</p> - <p>If the process does not exist, a <c>noproc</c> exception - is raised.</p> - </desc> - </func> - - <func> - <name>sync_send_all_state_event(FsmRef, Event) -> Reply</name> - <name>sync_send_all_state_event(FsmRef, Event, Timeout) -> Reply</name> - <fsummary>Send an event synchronously to a generic FSM.</fsummary> - <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName}</v> - <v> | {via,Module,ViaName} | pid()</v> - <v> Name = Node = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Event = term()</v> - <v>Timeout = int()>0 | infinity</v> - <v>Reply = term()</v> - </type> - <desc> - <p>Sends an event to the <c>FsmRef</c> of the <c>gen_fsm</c> - process and waits until a reply arrives or a time-out occurs. - The <c>gen_fsm</c> process calls - <seealso marker="#Module:handle_sync_event/4"> - <c>Module:handle_sync_event/4</c></seealso> to handle the event.</p> - <p>For a description of <c>FsmRef</c> and <c>Event</c>, see - <seealso marker="#send_event/2">send_event/2</seealso>. - For a description of <c>Timeout</c> and <c>Reply</c>, see - <seealso marker="#sync_send_event/3"> - <c>sync_send_event/3</c></seealso>.</p> - <p>For a discussion about the difference between - <c>sync_send_event</c> and <c>sync_send_all_state_event</c>, see - <seealso marker="#send_all_state_event/2"> - <c>send_all_state_event/2</c></seealso>.</p> - </desc> - </func> - - <func> - <name>sync_send_event(FsmRef, Event) -> Reply</name> - <name>sync_send_event(FsmRef, Event, Timeout) -> Reply</name> - <fsummary>Send an event synchronously to a generic FSM.</fsummary> - <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName}</v> - <v> | {via,Module,ViaName} | pid()</v> - <v> Name = Node = atom()</v> - <v> GlobalName = ViaName = term()</v> - <v>Event = term()</v> - <v>Timeout = int()>0 | infinity</v> - <v>Reply = term()</v> - </type> - <desc> - <p>Sends an event to the <c>FsmRef</c> of the <c>gen_fsm</c> - process and waits until a reply arrives or a time-out occurs. - <c>The gen_fsm</c> process calls - <seealso marker="#Module:StateName/3"> - <c>Module:StateName/3</c></seealso> to handle the event, where - <c>StateName</c> is the name of the current state of - the <c>gen_fsm</c> process.</p> - <p>For a description of <c>FsmRef</c> and <c>Event</c>, see - <seealso marker="#send_event/2"><c>send_event/2</c></seealso>.</p> - <p><c>Timeout</c> is an integer greater than zero that - specifies how many milliseconds to wait for a reply, or - the atom <c>infinity</c> to wait indefinitely. Defaults - to 5000. If no reply is received within the specified time, - the function call fails.</p> - <p>Return value <c>Reply</c> is defined in the return value - of <c>Module:StateName/3</c>.</p> - </desc> - </func> - </funcs> - - <section> - <title>Callback Functions</title> - <p>The following functions are to be exported from a <c>gen_fsm</c> - callback module.</p> - - <p><em>state name</em> denotes a state of the state machine.</p> - - <p><em>state data</em> denotes the internal state of the Erlang process - that implements the state machine.</p> - </section> - - <funcs> - <func> - <name>Module:code_change(OldVsn, StateName, StateData, Extra) -> {ok, NextStateName, NewStateData}</name> - <fsummary>Update the internal state data during upgrade/downgrade. - </fsummary> - <type> - <v>OldVsn = Vsn | {down, Vsn}</v> - <v> Vsn = term()</v> - <v>StateName = NextStateName = atom()</v> - <v>StateData = NewStateData = term()</v> - <v>Extra = term()</v> - </type> - <desc> - <note> - <p>This callback is optional, so callback modules need not export it. - If a release upgrade/downgrade with <c>Change={advanced,Extra}</c> - specified in the <c>appup</c> file is made when <c>code_change/4</c> - isn't implemented the process will crash with an <c>undef</c> exit - reason.</p> - </note> - <p>This function is called by a <c>gen_fsm</c> process when it is to - update its internal state data during a release upgrade/downgrade, - that is, when instruction <c>{update,Module,Change,...}</c>, - where <c>Change={advanced,Extra}</c>, is given in - the <c>appup</c> file; see section - <seealso marker="doc/design_principles:release_handling#instr"> - Release Handling Instructions</seealso> in OTP Design Principles.</p> - <p>For an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and for a downgrade, - <c>OldVsn</c> is <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the - <c>vsn</c> attribute(s) of the old version of the callback module - <c>Module</c>. If no such attribute is defined, the version is - the checksum of the Beam file.</p> - <p><c>StateName</c> is the current state name and <c>StateData</c> the - internal state data of the <c>gen_fsm</c> process.</p> - <p><c>Extra</c> is passed "as is" from the <c>{advanced,Extra}</c> - part of the update instruction.</p> - <p>The function is to return the new current state name and - updated internal data.</p> - </desc> - </func> - - <func> - <name>Module:format_status(Opt, [PDict, StateData]) -> Status</name> - <fsummary>Optional function for providing a term describing the - current <c>gen_fsm</c> process status.</fsummary> - <type> - <v>Opt = normal | terminate</v> - <v>PDict = [{Key, Value}]</v> - <v>StateData = term()</v> - <v>Status = term()</v> - </type> - <desc> - <note> - <p>This callback is optional, so callback modules need not - export it. The <c>gen_fsm</c> module provides a default - implementation of this function that returns the callback - module state data.</p> - </note> - <p>This function is called by a <c>gen_fsm</c> process in the - following situations:</p> - <list type="bulleted"> - <item>One of <seealso marker="sys#get_status/1"> - <c>sys:get_status/1,2</c></seealso> - is invoked to get the <c>gen_fsm</c> status. <c>Opt</c> is set to - the atom <c>normal</c> for this case.</item> - <item>The <c>gen_fsm</c> process terminates abnormally and logs an - error. <c>Opt</c> is set to the atom <c>terminate</c> for - this case.</item> - </list> - <p>This function is useful for changing the form and - appearance of the <c>gen_fsm</c> status for these cases. A callback - module wishing to change the <c>sys:get_status/1,2</c> - return value as well as how its status appears in - termination error logs, exports an instance - of <c>format_status/2</c> that returns a term describing the - current status of the <c>gen_fsm</c> process.</p> - <p><c>PDict</c> is the current value of the process dictionary of the - <c>gen_fsm</c> process.</p> - <p><c>StateData</c> is the internal state data of the - <c>gen_fsm</c> process.</p> - <p>The function is to return <c>Status</c>, a term that - change the details of the current state and status of - the <c>gen_fsm</c> process. There are no restrictions on the - form <c>Status</c> can take, but for - the <c>sys:get_status/1,2</c> case (when <c>Opt</c> - is <c>normal</c>), the recommended form for - the <c>Status</c> value is <c>[{data, [{"StateData", - Term}]}]</c>, where <c>Term</c> provides relevant details of - the <c>gen_fsm</c> state data. Following this recommendation is not - required, but it makes the callback module status - consistent with the rest of the <c>sys:get_status/1,2</c> - return value.</p> - <p>One use for this function is to return compact alternative - state data representations to avoid that large state terms - are printed in log files.</p> - </desc> - </func> - - <func> - <name>Module:handle_event(Event, StateName, StateData) -> Result</name> - <fsummary>Handle an asynchronous event.</fsummary> - <type> - <v>Event = term()</v> - <v>StateName = atom()</v> - <v>StateData = term()</v> - <v>Result = {next_state,NextStateName,NewStateData}</v> - <v> | {next_state,NextStateName,NewStateData,Timeout}</v> - <v> | {next_state,NextStateName,NewStateData,hibernate}</v> - <v> | {stop,Reason,NewStateData}</v> - <v> NextStateName = atom()</v> - <v> NewStateData = term()</v> - <v> Timeout = int()>0 | infinity</v> - <v> Reason = term()</v> - </type> - <desc> - <p>Whenever a <c>gen_fsm</c> process receives an event sent using - <seealso marker="#send_all_state_event/2"> - <c>send_all_state_event/2</c></seealso>, - this function is called to handle the event.</p> - <p><c>StateName</c> is the current state name of the <c>gen_fsm</c> - process.</p> - <p>For a description of the other arguments and possible return values, - see <seealso marker="#Module:StateName/2"> - <c>Module:StateName/2</c></seealso>.</p> - </desc> - </func> - - <func> - <name>Module:handle_info(Info, StateName, StateData) -> Result</name> - <fsummary>Handle an incoming message.</fsummary> - <type> - <v>Info = term()</v> - <v>StateName = atom()</v> - <v>StateData = term()</v> - <v>Result = {next_state,NextStateName,NewStateData}</v> - <v> | {next_state,NextStateName,NewStateData,Timeout}</v> - <v> | {next_state,NextStateName,NewStateData,hibernate}</v> - <v> | {stop,Reason,NewStateData}</v> - <v> NextStateName = atom()</v> - <v> NewStateData = term()</v> - <v> Timeout = int()>0 | infinity</v> - <v> Reason = normal | term()</v> - </type> - <desc> - <note> - <p>This callback is optional, so callback modules need not - export it. The <c>gen_fsm</c> module provides a default - implementation of this function that logs about the unexpected - <c>Info</c> message, drops it and returns - <c>{next_state, StateName, StateData}</c>.</p> - </note> - <p>This function is called by a <c>gen_fsm</c> process when it receives - any other message than a synchronous or asynchronous event (or a - system message).</p> - <p><c>Info</c> is the received message.</p> - <p>For a description of the other arguments and possible return values, - see <seealso marker="#Module:StateName/2"> - <c>Module:StateName/2</c></seealso>.</p> - </desc> - </func> - - <func> - <name>Module:handle_sync_event(Event, From, StateName, StateData) -> Result</name> - <fsummary>Handle a synchronous event.</fsummary> - <type> - <v>Event = term()</v> - <v>From = {pid(),Tag}</v> - <v>StateName = atom()</v> - <v>StateData = term()</v> - <v>Result = {reply,Reply,NextStateName,NewStateData}</v> - <v> | {reply,Reply,NextStateName,NewStateData,Timeout}</v> - <v> | {reply,Reply,NextStateName,NewStateData,hibernate}</v> - <v> | {next_state,NextStateName,NewStateData}</v> - <v> | {next_state,NextStateName,NewStateData,Timeout}</v> - <v> | {next_state,NextStateName,NewStateData,hibernate}</v> - <v> | {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}</v> - <v> Reply = term()</v> - <v> NextStateName = atom()</v> - <v> NewStateData = term()</v> - <v> Timeout = int()>0 | infinity</v> - <v> Reason = term()</v> - </type> - <desc> - <p>Whenever a <c>gen_fsm</c> process receives an event sent using - <seealso marker="#sync_send_all_state_event/2"> - <c>sync_send_all_state_event/2,3</c></seealso>, - this function is called to handle the event.</p> - <p><c>StateName</c> is the current state name of the <c>gen_fsm</c> - process.</p> - <p>For a description of the other arguments and possible return values, - see <seealso marker="#Module:StateName/3"> - <c>Module:StateName/3</c></seealso>.</p> - </desc> - </func> - - <func> - <name>Module:init(Args) -> Result</name> - <fsummary>Initialize process and internal state name and state data. - </fsummary> - <type> - <v>Args = term()</v> - <v>Result = {ok,StateName,StateData} | {ok,StateName,StateData,Timeout}</v> - <v> | {ok,StateName,StateData,hibernate}</v> - <v> | {stop,Reason} | ignore</v> - <v> StateName = atom()</v> - <v> StateData = term()</v> - <v> Timeout = int()>0 | infinity</v> - <v> Reason = term()</v> - </type> - <desc> - <marker id="Moduleinit"></marker> - <p>Whenever a <c>gen_fsm</c> process is started using - <seealso marker="#start/3"><c>start/3,4</c></seealso> or - <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>, - this function is called by the new process to initialize.</p> - <p><c>Args</c> is the <c>Args</c> argument provided to the start - function.</p> - <p>If initialization is successful, the function is to return - <c>{ok,StateName,StateData}</c>, - <c>{ok,StateName,StateData,Timeout}</c>, or - <c>{ok,StateName,StateData,hibernate}</c>, where <c>StateName</c> - is the initial state name and <c>StateData</c> the initial - state data of the <c>gen_fsm</c> process.</p> - <p>If an integer time-out value is provided, a time-out occurs - unless an event or a message is received within <c>Timeout</c> - milliseconds. A time-out is represented by the atom - <c>timeout</c> and is to be handled by the - <seealso marker="#Module:StateName/2"> - <c>Module:StateName/2</c></seealso> callback functions. The atom - <c>infinity</c> can be used to wait indefinitely, this is - the default value.</p> - <p>If <c>hibernate</c> is specified instead of a time-out value, the - process goes into hibernation when waiting for the next message - to arrive (by calling <seealso marker="proc_lib#hibernate/3"> - <c>proc_lib:hibernate/3</c></seealso>).</p> - <p>If the initialization fails, the function returns - <c>{stop,Reason}</c>, where <c>Reason</c> is any term, - or <c>ignore</c>.</p> - </desc> - </func> - - <func> - <name>Module:StateName(Event, StateData) -> Result</name> - <fsummary>Handle an asynchronous event.</fsummary> - <type> - <v>Event = timeout | term()</v> - <v>StateData = term()</v> - <v>Result = {next_state,NextStateName,NewStateData} </v> - <v> | {next_state,NextStateName,NewStateData,Timeout}</v> - <v> | {next_state,NextStateName,NewStateData,hibernate}</v> - <v> | {stop,Reason,NewStateData}</v> - <v> NextStateName = atom()</v> - <v> NewStateData = term()</v> - <v> Timeout = int()>0 | infinity</v> - <v> Reason = term()</v> - </type> - <desc> - <p>There is to be one instance of this function for each - possible state name. Whenever a <c>gen_fsm</c> process receives - an event sent using - <seealso marker="#send_event/2"><c>send_event/2</c></seealso>, - the instance of this function with the same name as - the current state name <c>StateName</c> is called to handle - the event. It is also called if a time-out occurs.</p> - <p><c>Event</c> is either the atom <c>timeout</c>, if a time-out - has occurred, or the <c>Event</c> argument provided to - <c>send_event/2</c>.</p> - <p><c>StateData</c> is the state data of the <c>gen_fsm</c> process.</p> - <p>If the function returns - <c>{next_state,NextStateName,NewStateData}</c>, - <c>{next_state,NextStateName,NewStateData,Timeout}</c>, or - <c>{next_state,NextStateName,NewStateData,hibernate}</c>, the - <c>gen_fsm</c> process continues executing with the current state - name set to <c>NextStateName</c> and with the possibly - updated state data <c>NewStateData</c>. For a description of - <c>Timeout</c> and <c>hibernate</c>, see - <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.</p> - <p>If the function returns <c>{stop,Reason,NewStateData}</c>, - the <c>gen_fsm</c> process calls - <c>Module:terminate(Reason,StateName,NewStateData)</c> and - terminates.</p> - </desc> - </func> - - <func> - <name>Module:StateName(Event, From, StateData) -> Result</name> - <fsummary>Handle a synchronous event.</fsummary> - <type> - <v>Event = term()</v> - <v>From = {pid(),Tag}</v> - <v>StateData = term()</v> - <v>Result = {reply,Reply,NextStateName,NewStateData}</v> - <v> | {reply,Reply,NextStateName,NewStateData,Timeout}</v> - <v> | {reply,Reply,NextStateName,NewStateData,hibernate}</v> - <v> | {next_state,NextStateName,NewStateData}</v> - <v> | {next_state,NextStateName,NewStateData,Timeout}</v> - <v> | {next_state,NextStateName,NewStateData,hibernate}</v> - <v> | {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}</v> - <v> Reply = term()</v> - <v> NextStateName = atom()</v> - <v> NewStateData = term()</v> - <v> Timeout = int()>0 | infinity</v> - <v> Reason = normal | term()</v> - </type> - <desc> - <p>There is to be one instance of this function for each - possible state name. Whenever a <c>gen_fsm</c> process receives an - event sent using <seealso marker="#sync_send_event/2"> - <c>sync_send_event/2,3</c></seealso>, - the instance of this function with the same name as - the current state name <c>StateName</c> is called to handle - the event.</p> - <p><c>Event</c> is the <c>Event</c> argument provided to - <c>sync_send_event/2,3</c>.</p> - <p><c>From</c> is a tuple <c>{Pid,Tag}</c> where <c>Pid</c> is - the pid of the process that called <c>sync_send_event/2,3</c> - and <c>Tag</c> is a unique tag.</p> - <p><c>StateData</c> is the state data of the <c>gen_fsm</c> process.</p> - <list type="bulleted"> - <item> - <p>If <c>{reply,Reply,NextStateName,NewStateData}</c>, - <c>{reply,Reply,NextStateName,NewStateData,Timeout}</c>, or - <c>{reply,Reply,NextStateName,NewStateData,hibernate}</c> is - returned, <c>Reply</c> is given back to <c>From</c> as the return - value of <c>sync_send_event/2,3</c>. The <c>gen_fsm</c> process - then continues executing with the current state name set to - <c>NextStateName</c> and with the possibly updated state data - <c>NewStateData</c>. For a description of <c>Timeout</c> and - <c>hibernate</c>, see - <seealso marker="#Module:init/1"> - <c>Module:init/1</c></seealso>.</p> - </item> - <item> - <p>If <c>{next_state,NextStateName,NewStateData}</c>, - <c>{next_state,NextStateName,NewStateData,Timeout}</c>, or - <c>{next_state,NextStateName,NewStateData,hibernate}</c> is - returned, the <c>gen_fsm</c> process continues executing in - <c>NextStateName</c> with <c>NewStateData</c>. - Any reply to <c>From</c> must be specified explicitly using - <seealso marker="#reply/2"><c>reply/2</c></seealso>.</p> - </item> - <item> - <p>If the function returns - <c>{stop,Reason,Reply,NewStateData}</c>, <c>Reply</c> is - given back to <c>From</c>. If the function returns - <c>{stop,Reason,NewStateData}</c>, any reply to <c>From</c> - must be specified explicitly using <c>reply/2</c>. - The <c>gen_fsm</c> process then calls - <c>Module:terminate(Reason,StateName,NewStateData)</c> and - terminates.</p> - </item> - </list> - </desc> - </func> - - <func> - <name>Module:terminate(Reason, StateName, StateData)</name> - <fsummary>Clean up before termination.</fsummary> - <type> - <v>Reason = normal | shutdown | {shutdown,term()} | term()</v> - <v>StateName = atom()</v> - <v>StateData = term()</v> - </type> - <desc> - <note> - <p>This callback is optional, so callback modules need not - export it. The <c>gen_fsm</c> module provides a default - implementation without cleanup.</p> - </note> - <p>This function is called by a <c>gen_fsm</c> process when it is about - to terminate. It is to be the opposite of - <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> - and do any necessary cleaning up. When it returns, the <c>gen_fsm</c> - process terminates with <c>Reason</c>. The return value is ignored. - </p> - <p><c>Reason</c> is a term denoting the stop reason, - <c>StateName</c> is the current state name, and - <c>StateData</c> is the state data of the <c>gen_fsm</c> process.</p> - <p><c>Reason</c> depends on why the <c>gen_fsm</c> process is - terminating. If - it is because another callback function has returned a stop - tuple <c>{stop,..}</c>, <c>Reason</c> has the value - specified in that tuple. If it is because of a failure, - <c>Reason</c> is the error reason.</p> - <p>If the <c>gen_fsm</c> process is part of a supervision tree and is - ordered by its supervisor to terminate, this function is called - with <c>Reason=shutdown</c> if the following conditions apply:</p> - <list type="bulleted"> - <item> - <p>The <c>gen_fsm</c> process has been set to trap exit signals.</p> - </item> - <item> - <p>The shutdown strategy as defined in the child specification of - the supervisor is an integer time-out value, not - <c>brutal_kill</c>.</p> - </item> - </list> - <p>Even if the <c>gen_fsm</c> process is <em>not</em> part of a - supervision tree, - this function is called if it receives an <c>'EXIT'</c> - message from its parent. <c>Reason</c> is the same as in - the <c>'EXIT'</c> message.</p> - <p>Otherwise, the <c>gen_fsm</c> process terminates immediately.</p> - <p>Notice that for any other reason than <c>normal</c>, - <c>shutdown</c>, or <c>{shutdown,Term}</c> the <c>gen_fsm</c> process - is assumed to terminate because of an error and an error report is - issued using <seealso marker="kernel:error_logger#format/2"> - <c>error_logger:format/2</c></seealso>.</p> - </desc> - </func> - </funcs> - <section> - <title>See Also</title> - <p><seealso marker="gen_event"><c>gen_event(3)</c></seealso>, - <seealso marker="gen_server"><c>gen_server(3)</c></seealso>, - <seealso marker="gen_statem"><c>gen_statem(3)</c></seealso>, - <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>, - <seealso marker="supervisor"><c>supervisor(3)</c></seealso>, - <seealso marker="sys"><c>sys(3)</c></seealso></p> + <marker id="Migration to gen_statem"/> + <title>Migration to gen_statem</title> + + <p>Here follows a simple example of turning a gen_fsm into + a <seealso marker="gen_statem"><c>gen_statem</c></seealso>. The example comes + from the previous Users Guide for <c>gen_fsm</c> </p> + + <code type="erl"> +-module(code_lock). +-define(NAME, code_lock). +%-define(BEFORE_REWRITE, true). + +-ifdef(BEFORE_REWRITE). +-behaviour(gen_fsm). +-else. +-behaviour(gen_statem). +-endif. + +-export([start_link/1, button/1, stop/0]). + +-ifdef(BEFORE_REWRITE). +-export([init/1, locked/2, open/2, handle_sync_event/4, handle_event/3, + handle_info/3, terminate/3, code_change/4]). +-else. +-export([init/1, callback_mode/0, locked/3, open/3, terminate/3, code_change/4]). +%% Add callback__mode/0 +%% Change arity of the state functions +%% Remove handle_info/3 +-endif. + +-ifdef(BEFORE_REWRITE). +start_link(Code) -> + gen_fsm:start_link({local, ?NAME}, ?MODULE, Code, []). +-else. +start_link(Code) -> + gen_statem:start_link({local,?NAME}, ?MODULE, Code, []). +-endif. + +-ifdef(BEFORE_REWRITE). +button(Digit) -> + gen_fsm:send_event(?NAME, {button, Digit}). +-else. +button(Digit) -> + gen_statem:cast(?NAME, {button,Digit}). + %% send_event is asynchronous and becomes a cast +-endif. + +-ifdef(BEFORE_REWRITE). +stop() -> + gen_fsm:sync_send_all_state_event(?NAME, stop). +-else. +stop() -> + gen_statem:call(?NAME, stop). + %% sync_send is synchronous and becomes call + %% all_state is handled by callback code in gen_statem +-endif. + +init(Code) -> + do_lock(), + Data = #{code => Code, remaining => Code}, + {ok, locked, Data}. + +-ifdef(BEFORE_REWRITE). +-else. +callback_mode() -> + state_functions. +%% state_functions mode is the mode most similar to +%% gen_fsm. There is also handle_event mode which is +%% a fairly different concept. +-endif. + +-ifdef(BEFORE_REWRITE). +locked({button, Digit}, Data0) -> + case analyze_lock(Digit, Data0) of + {open = StateName, Data} -> + {next_state, StateName, Data, 10000}; + {StateName, Data} -> + {next_state, StateName, Data} + end. +-else. +locked(cast, {button,Digit}, Data0) -> + case analyze_lock(Digit, Data0) of + {open = StateName, Data} -> + {next_state, StateName, Data, 10000}; + {StateName, Data} -> + {next_state, StateName, Data} + end; +locked({call, From}, Msg, Data) -> + handle_call(From, Msg, Data); +locked({info, Msg}, StateName, Data) -> + handle_info(Msg, StateName, Data). +%% Arity differs +%% All state events are dispatched to handle_call and handle_info help +%% functions. If you want to handle a call or cast event specifically +%% for this state you would add a special clause for it above. +-endif. + +-ifdef(BEFORE_REWRITE). +open(timeout, State) -> + do_lock(), + {next_state, locked, State}; +open({button,_}, Data) -> + {next_state, locked, Data}. +-else. +open(timeout, _, Data) -> + do_lock(), + {next_state, locked, Data}; +open(cast, {button,_}, Data) -> + {next_state, locked, Data}; +open({call, From}, Msg, Data) -> + handle_call(From, Msg, Data); +open(info, Msg, Data) -> + handle_info(Msg, open, Data). +%% Arity differs +%% All state events are dispatched to handle_call and handle_info help +%% functions. If you want to handle a call or cast event specifically +%% for this state you would add a special clause for it above. +-endif. + +-ifdef(BEFORE_REWRITE). +handle_sync_event(stop, _From, _StateName, Data) -> + {stop, normal, ok, Data}. + +handle_event(Event, StateName, Data) -> + {stop, {shutdown, {unexpected, Event, StateName}}, Data}. + +handle_info(Info, StateName, Data) -> + {stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}. +-else. +-endif. + +terminate(_Reason, State, _Data) -> + State =/= locked andalso do_lock(), + ok. +code_change(_Vsn, State, Data, _Extra) -> + {ok, State, Data}. + +%% Internal functions +-ifdef(BEFORE_REWRITE). +-else. +handle_call(From, stop, Data) -> + {stop_and_reply, normal, {reply, From, ok}, Data}. + +handle_info(Info, StateName, Data) -> + {stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}. +%% These are internal functions for handling all state events +%% and not behaviour callbacks as in gen_fsm +-endif. + +analyze_lock(Digit, #{code := Code, remaining := Remaining} = Data) -> + case Remaining of + [Digit] -> + do_unlock(), + {open, Data#{remaining := Code}}; + [Digit|Rest] -> % Incomplete + {locked, Data#{remaining := Rest}}; + _Wrong -> + {locked, Data#{remaining := Code}} + end. + +do_lock() -> + io:format("Lock~n", []). +do_unlock() -> + io:format("Unlock~n", []). + </code> </section> </erlref> diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index 4540449792..0bcbbc2805 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -814,7 +814,6 @@ gen_server:abcast -----> Module:handle_cast/2 <section> <title>See Also</title> <p><seealso marker="gen_event"><c>gen_event(3)</c></seealso>, - <seealso marker="gen_fsm"><c>gen_fsm(3)</c></seealso>, <seealso marker="gen_statem"><c>gen_statem(3)</c></seealso>, <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>, <seealso marker="supervisor"><c>supervisor(3)</c></seealso>, diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 5eb13db1aa..18089a8191 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -62,8 +62,8 @@ </p> </note> <p> - The <c>gen_statem</c> behavior is intended to replace - <seealso marker="gen_fsm"><c>gen_fsm</c></seealso> for new code. + The <c>gen_statem</c> behavior replaces + <seealso marker="gen_fsm"><c>gen_fsm</c> </seealso> in Erlang/OTP 20.0. It has the same features and adds some really useful: </p> <list type="bulleted"> @@ -78,8 +78,10 @@ <p> The callback model(s) for <c>gen_statem</c> differs from the one for <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>, - but it is still fairly easy to rewrite - from <c>gen_fsm</c> to <c>gen_statem</c>. + but it is still fairly easy to + <seealso marker="gen_fsm#Migration to gen_statem"> + rewrite from + </seealso> <c>gen_fsm</c> to <c>gen_statem</c>. </p> <p> A generic state machine process (<c>gen_statem</c>) implemented @@ -945,7 +947,6 @@ handle_event(_, _, State, Data) -> <seealso marker="#state callback">state callback</seealso> return value <c>{next_state,NextState,NewData,Timeout}</c> allowed like for <c>gen_fsm</c>'s - <seealso marker="gen_fsm#Module:StateName/2"><c>Module:StateName/2</c></seealso>. </p> </item> <tag><c>timeout</c></tag> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index 11a64c7f8a..64fcf4379f 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2016</year> + <year>1996</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -265,6 +265,8 @@ ok <p>Writes data with the standard syntax. This is used to output Erlang terms. Atoms are printed within quotes if they contain embedded non-printable characters. + Atom characters > 255 are escaped unless the + Unicode translation modifier (<c>t</c>) is used. Floats are printed accurately as the shortest, correctly rounded string.</p> </item> @@ -567,8 +569,6 @@ Prompt> <input><Characters beyond latin1 range not printable in this medium&g <item> <p>Similar to <c>s</c>, but the resulting string is converted into an atom.</p> - <p>The Unicode translation modifier is not allowed (atoms - cannot contain characters beyond the <c>latin1</c> range).</p> </item> <tag><c>c</c></tag> <item> diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 5ae400da62..bc1d77ac83 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -356,7 +356,8 @@ <func> <name name="write" arity="1"/> - <name name="write" arity="2"/> + <name name="write" arity="2" clause_i="1"/> + <name name="write" arity="2" clause_i="2"/> <fsummary>Write a term.</fsummary> <desc> <p>Returns a character list that represents <c><anno>Term</anno></c>. diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index e64b2ce18a..7939a0ef61 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -36,7 +36,7 @@ the <seealso marker="doc/design_principles:des_princ"> OTP Design Principles</seealso>. Specifically, the functions in this module are used by the OTP standard behaviors (for example, - <c>gen_server</c>, <c>gen_fsm</c>, and <c>gen_statem</c>) + <c>gen_server</c> and <c>gen_statem</c>) when starting new processes. The functions can also be used to start <em>special processes</em>, user-defined processes that comply to the OTP design principles. For an example, diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index bb06d3645e..a42cfdd567 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -36,7 +36,6 @@ process can either be another supervisor or a worker process. Worker processes are normally implemented using one of the <seealso marker="gen_event"><c>gen_event</c></seealso>, - <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>, <seealso marker="gen_server"><c>gen_server</c></seealso>, or <seealso marker="gen_statem"><c>gen_statem</c></seealso> behaviors. A supervisor implemented using this module has @@ -237,8 +236,8 @@ child_spec() = #{id => child_id(), % mandatory <p><c>modules</c> is used by the release handler during code replacement to determine which processes are using a certain module. As a rule of thumb, if the child process is a - <c>supervisor</c>, <c>gen_server</c>, - <c>gen_statem</c>, or <c>gen_fsm</c>, + <c>supervisor</c>, <c>gen_server</c> or, + <c>gen_statem</c>, this is to be a list with one element <c>[Module]</c>, where <c>Module</c> is the callback module. If the child process is an event manager (<c>gen_event</c>) with a @@ -706,7 +705,6 @@ child_spec() = #{id => child_id(), % mandatory <section> <title>See Also</title> <p><seealso marker="gen_event"><c>gen_event(3)</c></seealso>, - <seealso marker="gen_fsm"><c>gen_fsm(3)</c></seealso>, <seealso marker="gen_statem"><c>gen_statem(3)</c></seealso>, <seealso marker="gen_server"><c>gen_server(3)</c></seealso>, <seealso marker="sys"><c>sys(3)</c></seealso></p> diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index 45171f814d..78840aaaf3 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -168,12 +168,6 @@ </item> <item> <p>For a - <seealso marker="gen_fsm"><c>gen_fsm</c></seealso> - process, <c><anno>State</anno></c> is the tuple - <c>{CurrentStateName, CurrentStateData}</c>.</p> - </item> - <item> - <p>For a <seealso marker="gen_statem"><c>gen_statem</c></seealso> process, <c><anno>State</anno></c> is the tuple <c>{CurrentState,CurrentData}</c>.</p> @@ -222,7 +216,7 @@ <p>Function <c>system_get_state/1</c> is primarily useful for user-defined behaviors and modules that implement OTP <seealso marker="#special_process">special processes</seealso>. - The <c>gen_server</c>, <c>gen_fsm</c>, + The <c>gen_server</c>, <c>gen_statem</c>, and <c>gen_event</c> OTP behavior modules export this function, so callback modules for those behaviors need not to supply their own.</p> @@ -246,11 +240,6 @@ process returns the state of the callback module.</p> </item> <item> - <p>A <seealso marker="gen_fsm"><c>gen_fsm</c></seealso> - process returns information, such as its current - state name and state data.</p> - </item> - <item> <p>A <seealso marker="gen_statem"><c>gen_statem</c></seealso> process returns information, such as its current state name and state data.</p> @@ -262,14 +251,12 @@ </item> </list> <p>Callback modules for <c>gen_server</c>, - <c>gen_fsm</c>, <c>gen_statem</c>, and <c>gen_event</c> + <c>gen_statem</c>, and <c>gen_event</c> can also change the value of <c><anno>Misc</anno></c> by exporting a function <c>format_status/2</c>, which contributes module-specific information. For details, see <seealso marker="gen_server#Module:format_status/2"> <c>gen_server:format_status/2</c></seealso>, - <seealso marker="gen_fsm#Module:format_status/2"> - <c>gen_fsm:format_status/2</c></seealso>, <seealso marker="gen_statem#Module:format_status/2"> <c>gen_statem:format_status/2</c></seealso>, and <seealso marker="gen_event#Module:format_status/2"> @@ -373,13 +360,6 @@ is a new instance of that state.</p> </item> <item> - <p>For a <seealso marker="gen_fsm"><c>gen_fsm</c></seealso> process, - <c><anno>State</anno></c> is the tuple <c>{CurrentStateName, - CurrentStateData}</c>, and <c><anno>NewState</anno></c> is a - similar tuple, which can contain - a new state name, new state data, or both.</p> - </item> - <item> <p>For a <seealso marker="gen_statem"><c>gen_statem</c></seealso> process, <c><anno>State</anno></c> is the tuple <c>{CurrentState,CurrentData}</c>, @@ -422,7 +402,7 @@ return its <c><anno>State</anno></c> argument.</p> <p>If a <c><anno>StateFun</anno></c> function crashes or throws an exception, the original state of the process is unchanged for - <c>gen_server</c>, <c>gen_fsm</c>, and <c>gen_statem</c> processes. + <c>gen_server</c>, and <c>gen_statem</c> processes. For <c>gen_event</c> processes, a crashing or failing <c><anno>StateFun</anno></c> function means that only the state of the particular event handler it was @@ -462,7 +442,7 @@ user-defined behaviors and modules that implement OTP <seealso marker="#special_process">special processes</seealso>. The OTP behavior modules <c>gen_server</c>, - <c>gen_fsm</c>, <c>gen_statem</c>, and <c>gen_event</c> + <c>gen_statem</c>, and <c>gen_event</c> export this function, so callback modules for those behaviors need not to supply their own.</p> </desc> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index 11b84f552a..6af2fa9fa3 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -64,8 +64,8 @@ source files was switched to UTF-8.</p></item> <item><p>In Erlang/OTP 20.0, atoms and function can contain - Unicode characters. Module names are still restricted to - the ISO-Latin-1 range.</p> + Unicode characters. Module names, application names, and node + names are still restricted to the ISO Latin-1 range.</p> <p>Support was added for normalizations forms in <c>unicode</c> and the <c>string</c> module now handles utf8-encoded binaries.</p></item> @@ -352,10 +352,11 @@ <p>Having the source code in UTF-8 also allows you to write string literals, function names, and atoms containing Unicode characters with code points > 255. - Module names are still restricted to the ISO Latin-1 range. - Binary literals, where you use type + Module names, application names, and node names are still restricted + to the ISO Latin-1 range. Binary literals, where you use type <c>/utf8</c>, can also be expressed using Unicode characters > 255. - Having module names using characters other than 7-bit ASCII can cause + Having module names or application names using characters other than + 7-bit ASCII can cause trouble on operating systems with inconsistent file naming schemes, and can hurt portability, so it is not recommended.</p> <p>EEP 40 suggests that the language is also to allow for Unicode @@ -451,8 +452,8 @@ external_charlist() = maybe_improper_list(char() | external_unicode_binary() | marker="stdlib:epp#encoding"><c>epp(3)</c></seealso> module. As from Erlang/OTP R16, strings and comments can be written using Unicode. As from Erlang/OTP 20, also atoms and functions can be - written using Unicode. Modules names must still be named using - characters from the ISO Latin-1 character set. (These + written using Unicode. Modules, applications, and nodes must still be + named using characters from the ISO Latin-1 character set. (These restrictions in the language are independent of the encoding of the source file.)</p> @@ -780,7 +781,7 @@ Eshell V5.10.1 (abort with ^G) filenames or directory names. If the file system content is listed, you also get Unicode lists as return value. The support lies in the Kernel and STDLIB modules, which is why - most applications (that does not explicitly require the filenames + most applications (that do not explicitly require the filenames to be in the ISO Latin-1 range) benefit from the Unicode support without change.</p> diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 0864cfeff6..a7d53af7bc 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/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 78b7a0e751..7c40058dd8 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -3883,6 +3883,10 @@ extract_sequence(4, [$t, $p | Fmt], Need) -> extract_sequence(5, [$p|Fmt], Need); extract_sequence(4, [$t, $P | Fmt], Need) -> extract_sequence(5, [$P|Fmt], Need); +extract_sequence(4, [$t, $w | Fmt], Need) -> + extract_sequence(5, [$w|Fmt], Need); +extract_sequence(4, [$t, $W | Fmt], Need) -> + extract_sequence(5, [$W|Fmt], Need); extract_sequence(4, [$t, C | _Fmt], _Need) -> {error,"invalid control ~t" ++ [C]}; extract_sequence(4, [$l, $p | Fmt], Need) -> diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index 39a8fd42fe..d413da3ea1 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -124,6 +124,26 @@ system_replace_state/2, format_status/2]). +-deprecated({start, 3, next_major_release}). +-deprecated({start, 4, next_major_release}). +-deprecated({start_link, 3, next_major_release}). +-deprecated({start_link, 4, next_major_release}). +-deprecated({stop, 1, next_major_release}). +-deprecated({stop, 3, next_major_release}). +-deprecated({send_event, 2, next_major_release}). +-deprecated({sync_send_event, 2, next_major_release}). +-deprecated({sync_send_event, 3, next_major_release}). +-deprecated({send_all_state_event, 2, next_major_release}). +-deprecated({sync_send_all_state_event, 2, next_major_release}). +-deprecated({sync_send_all_state_event, 3, next_major_release}). +-deprecated({reply, 2, next_major_release}). +-deprecated({start_timer, 2, next_major_release}). +-deprecated({send_event_after, 2, next_major_release}). +-deprecated({cancel_timer, 1, next_major_release}). +-deprecated({enter_loop, 4, next_major_release}). +-deprecated({enter_loop, 5, next_major_release}). +-deprecated({enter_loop, 6, next_major_release}). + -import(error_logger, [format/2]). %%% --------------------------------------------------- diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 28e5007e5a..5ed2f4d888 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -268,47 +268,61 @@ write(Term, D, false) -> -spec write(Term, Depth) -> chars() when Term :: term(), + Depth :: depth(); + (Term, Options) -> chars() when + Term :: term(), + Options :: [Option], + Option :: {'depth', Depth} + | {'encoding', 'latin1' | 'utf8' | 'unicode'}, Depth :: depth(). -write(_Term, 0) -> "..."; -write(Term, _D) when is_integer(Term) -> integer_to_list(Term); -write(Term, _D) when is_float(Term) -> io_lib_format:fwrite_g(Term); -write(Atom, _D) when is_atom(Atom) -> write_atom(Atom); -write(Term, _D) when is_port(Term) -> write_port(Term); -write(Term, _D) when is_pid(Term) -> pid_to_list(Term); -write(Term, _D) when is_reference(Term) -> write_ref(Term); -write(<<_/bitstring>>=Term, D) -> write_binary(Term, D); -write([], _D) -> "[]"; -write({}, _D) -> "{}"; -write([H|T], D) -> +write(Term, Options) when is_list(Options) -> + Depth = get_option(depth, Options, -1), + Encoding = get_option(encoding, Options, epp:default_encoding()), + write1(Term, Depth, Encoding); +write(Term, Depth) -> + write1(Term, Depth, latin1). + +write1(_Term, 0, _E) -> "..."; +write1(Term, _D, _E) when is_integer(Term) -> integer_to_list(Term); +write1(Term, _D, _E) when is_float(Term) -> io_lib_format:fwrite_g(Term); +write1(Atom, _D, latin1) when is_atom(Atom) -> write_atom_as_latin1(Atom); +write1(Atom, _D, _E) when is_atom(Atom) -> write_atom(Atom); +write1(Term, _D, _E) when is_port(Term) -> write_port(Term); +write1(Term, _D, _E) when is_pid(Term) -> pid_to_list(Term); +write1(Term, _D, _E) when is_reference(Term) -> write_ref(Term); +write1(<<_/bitstring>>=Term, D, _E) -> write_binary(Term, D); +write1([], _D, _E) -> "[]"; +write1({}, _D, _E) -> "{}"; +write1([H|T], D, E) -> if D =:= 1 -> "[...]"; true -> - [$[,[write(H, D-1)|write_tail(T, D-1, $|)],$]] + [$[,[write1(H, D-1, E)|write_tail(T, D-1, E, $|)],$]] end; -write(F, _D) when is_function(F) -> +write1(F, _D, _E) when is_function(F) -> erlang:fun_to_list(F); -write(Term, D) when is_map(Term) -> - write_map(Term, D); -write(T, D) when is_tuple(T) -> +write1(Term, D, E) when is_map(Term) -> + write_map(Term, D, E); +write1(T, D, E) when is_tuple(T) -> if D =:= 1 -> "{...}"; true -> [${, - [write(element(1, T), D-1)| - write_tail(tl(tuple_to_list(T)), D-1, $,)], + [write1(element(1, T), D-1, E)| + write_tail(tl(tuple_to_list(T)), D-1, E, $,)], $}] end. %% write_tail(List, Depth, CharacterBeforeDots) %% Test the terminating case first as this looks better with depth. -write_tail([], _D, _S) -> ""; -write_tail(_, 1, S) -> [S | "..."]; -write_tail([H|T], D, S) -> - [$,,write(H, D-1)|write_tail(T, D-1, S)]; -write_tail(Other, D, S) -> - [S,write(Other, D-1)]. +write_tail([], _D, _E, _S) -> ""; +write_tail(_, 1, _E, S) -> [S | "..."]; +write_tail([H|T], D, E, S) -> + [$,,write1(H, D-1, E)|write_tail(T, D-1, E, S)]; +write_tail(Other, D, E, S) -> + [S,write1(Other, D-1, E)]. write_port(Port) -> erlang:port_to_list(Port). @@ -316,17 +330,17 @@ write_port(Port) -> write_ref(Ref) -> erlang:ref_to_list(Ref). -write_map(Map, D) when is_integer(D) -> - [$#,${,write_map_body(maps:to_list(Map), D),$}]. +write_map(Map, D, E) when is_integer(D) -> + [$#,${,write_map_body(maps:to_list(Map), D, E),$}]. -write_map_body(_, 0) -> "..."; -write_map_body([],_) -> []; -write_map_body([{K,V}],D) -> write_map_assoc(K,V,D); -write_map_body([{K,V}|KVs], D) -> - [write_map_assoc(K,V,D),$, | write_map_body(KVs,D-1)]. +write_map_body(_, 0, _E) -> "..."; +write_map_body([], _, _E) -> []; +write_map_body([{K,V}], D, E) -> write_map_assoc(K, V, D, E); +write_map_body([{K,V}|KVs], D, E) -> + [write_map_assoc(K, V, D, E),$, | write_map_body(KVs, D-1, E)]. -write_map_assoc(K,V,D) -> - [write(K,D - 1),"=>",write(V,D-1)]. +write_map_assoc(K, V, D, E) -> + [write1(K, D - 1, E),"=>",write1(V, D-1, E)]. write_binary(B, D) when is_integer(D) -> [$<,$<,write_binary_body(B, D),$>,$>]. @@ -344,6 +358,13 @@ write_binary_body(B, _D) -> <<X:L>> = B, [integer_to_list(X),$:,integer_to_list(L)]. +get_option(Key, TupleList, Default) -> + case lists:keyfind(Key, 1, TupleList) of + false -> Default; + {Key, Value} -> Value; + _ -> Default + end. + %%% There are two functions to write Unicode atoms: %%% - they both escape control characters < 160; %%% - write_atom() never escapes characters >= 160; diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index 3113767614..14d925bacf 100644 --- a/lib/stdlib/src/io_lib_format.erl +++ b/lib/stdlib/src/io_lib_format.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -257,12 +257,12 @@ indentation([], I) -> I. %% This is the main dispatch function for the various formatting commands. %% Field widths and precisions have already been calculated. -control($w, [A], F, Adj, P, Pad, _Enc, _Str, _I) -> - term(io_lib:write(A, -1), F, Adj, P, Pad); +control($w, [A], F, Adj, P, Pad, Enc, _Str, _I) -> + term(io_lib:write(A, [{depth,-1}, {encoding, Enc}]), F, Adj, P, Pad); control($p, [A], F, Adj, P, Pad, Enc, Str, I) -> print(A, -1, F, Adj, P, Pad, Enc, Str, I); -control($W, [A,Depth], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(Depth) -> - term(io_lib:write(A, Depth), F, Adj, P, Pad); +control($W, [A,Depth], F, Adj, P, Pad, Enc, _Str, _I) when is_integer(Depth) -> + term(io_lib:write(A, [{depth,Depth}, {encoding, Enc}]), F, Adj, P, Pad); control($P, [A,Depth], F, Adj, P, Pad, Enc, Str, I) when is_integer(Depth) -> print(A, Depth, F, Adj, P, Pad, Enc, Str, I); control($s, [A], F, Adj, P, Pad, latin1, _Str, _I) when is_atom(A) -> diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 6a8f8f728e..983e8d4566 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -159,8 +159,8 @@ fread_field([$t|Format], F, Sup, _Unic) -> fread_field(Format, F, Sup, Unic) -> {Format,F,Sup,Unic}. -%% fread1(Format, FieldWidth, Suppress, Line, N, Results, AllFormat) -%% fread1(Format, FieldWidth, Suppress, Line, N, Results) +%% fread1(Format, FieldWidth, Suppress, Unicode, Line, N, Results, AllFormat) +%% fread1(Format, FieldWidth, Suppress, Unicode, Line, N, Results) %% The main dispatch function for the formatting commands. Done in two %% stages so format commands that need no input can always be processed. @@ -231,9 +231,8 @@ fread1([$s|Format], none, Sup, U, Line0, N0, Res) -> fread1([$s|Format], F, Sup, U, Line0, N, Res) -> {Line,Cs} = fread_chars(Line0, F, U), fread_string(Cs, Sup, U, Format, Line, N+F, Res); -%% XXX:PaN Atoms still only latin1... -fread1([$a|Format], none, Sup, false, Line0, N0, Res) -> - {Line,N,Cs} = fread_string_cs(Line0, N0, false), +fread1([$a|Format], none, Sup, U, Line0, N0, Res) -> + {Line,N,Cs} = fread_string_cs(Line0, N0, U), fread_atom(Cs, Sup, Format, Line, N, Res); fread1([$a|Format], F, Sup, false, Line0, N, Res) -> {Line,Cs} = fread_chars(Line0, F, false), diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index d89ff4a624..42094e3088 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -55,6 +55,55 @@ obsolete_1(erlang, now, 0) -> obsolete_1(calendar, local_time_to_universal_time, 1) -> {deprecated, {calendar, local_time_to_universal_time_dst, 1}}; +%% *** STDLIB added in OTP 20 *** + +obsolete_1(gen_fsm, start, 3) -> + {deprecated, {gen_statem, start, 3}}; +obsolete_1(gen_fsm, start, 4) -> + {deprecated, {gen_statem, start, 4}}; + +obsolete_1(gen_fsm, start_link, 3) -> + {deprecated, {gen_statem, start, 3}}; +obsolete_1(gen_fsm, start_link, 4) -> + {deprecated, {gen_statem, start, 4}}; + +obsolete_1(gen_fsm, stop, 1) -> + {deprecated, {gen_statem, stop, 1}}; +obsolete_1(gen_fsm, stop, 3) -> + {deprecated, {gen_statem, stop, 3}}; + +obsolete_1(gen_fsm, enter_loop, 4) -> + {deprecated, {gen_statem, enter_loop, 4}}; +obsolete_1(gen_fsm, enter_loop, 5) -> + {deprecated, {gen_statem, enter_loop, 5}}; +obsolete_1(gen_fsm, enter_loop, 6) -> + {deprecated, {gen_statem, enter_loop, 6}}; + +obsolete_1(gen_fsm, reply, 2) -> + {deprecated, {gen_statem, reply, 2}}; + +obsolete_1(gen_fsm, send_event, 2) -> + {deprecated, {gen_statem, cast, 1}}; +obsolete_1(gen_fsm, send_all_state_event, 2) -> + {deprecated, {gen_statem, cast, 1}}; + +obsolete_1(gen_fsm, sync_send_event, 2) -> + {deprecated, {gen_statem, call, 2}}; +obsolete_1(gen_fsm, sync_send_event, 3) -> + {deprecated, {gen_statem, call, 3}}; + +obsolete_1(gen_fsm, sync_send_all_state_event, 2) -> + {deprecated, {gen_statem, call, 2}}; +obsolete_1(gen_fsm, sync_send_all_state_event, 3) -> + {deprecated, {gen_statem, call, 3}}; + +obsolete_1(gen_fsm, start_timer, 2) -> + {deprecated, {erlang, start_timer, 2}}; +obsolete_1(gen_fsm, cancel_timer, 1) -> + {deprecated, {erlang, cancel_timer, 1}}; +obsolete_1(gen_fsm, send_event_after, 2) -> + {deprecated, {erlang, send_after, 2}}; + %% *** CRYPTO added in OTP 20 *** obsolete_1(crypto, rand_uniform, 2) -> diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl index ab9731180f..7a8a5e6d4a 100644 --- a/lib/stdlib/src/rand.erl +++ b/lib/stdlib/src/rand.erl @@ -265,7 +265,7 @@ seed_s(Alg0, S0 = {_, _, _}) -> %%% uniform/0, uniform/1, uniform_s/1, uniform_s/2 are all %%% uniformly distributed random numbers. -%% uniform/0: returns a random float X where 0.0 < X < 1.0, +%% uniform/0: returns a random float X where 0.0 =< X < 1.0, %% updating the state in the process dictionary. -spec uniform() -> X :: float(). @@ -285,7 +285,7 @@ uniform(N) -> X. %% uniform_s/1: given a state, uniform_s/1 -%% returns a random float X where 0.0 < X < 1.0, +%% returns a random float X where 0.0 =< X < 1.0, %% and a new state. -spec uniform_s(State :: state()) -> {X :: float(), NewState :: state()}. @@ -742,20 +742,20 @@ exrop_uniform(Range, {Alg, R}) -> MaxMinusRange = ?BIT(58) - Range, ?uniform_range(Range, Alg, R1, V, MaxMinusRange, I). -%% Split a 116 bit constant into two '1'++58 bit words, -%% the top '1' marks the top of the word +%% Split a 116 bit constant into two 58 bit words, +%% a top '1' marks the end of the low word. -define( JUMP_116(Jump), - [?BIT(58) bor ?MASK(58, (Jump)),?BIT(58) bor ((Jump) bsr 58)]). + [?BIT(58) bor ?MASK(58, (Jump)),(Jump) bsr 58]). %% exrop_jump({Alg,S}) -> [J|Js] = ?JUMP_116(16#9863200f83fcd4a11293241fcb12a), {Alg, exrop_jump(S, 0, 0, J, Js)}. %% -dialyzer({no_improper_lists, exrop_jump/5}). -exrop_jump(_S, S0, S1, 1, []) -> % End of jump constant +exrop_jump(_S, S0, S1, 0, []) -> % End of jump constant [S0|S1]; -exrop_jump(S, S0, S1, 1, [J|Js]) -> % End of the word +exrop_jump(S, S0, S1, 1, [J|Js]) -> % End of word exrop_jump(S, S0, S1, J, Js); exrop_jump([S__0|S__1] = _S, S0, S1, J, Js) -> case ?MASK(1, J) of diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index c8900d74e8..d56f27953f 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; diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index b2754e47ba..ef3f0be5d7 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -2348,4 +2348,28 @@ otp_14285(_Config) -> L1 = [S || C <- Chars, S <- io_lib:write_atom(list_to_atom([C])), not is_latin1(S)], L1 = lists:seq(256, 512), + + latin1_fmt("~w", ['кирилли́ческий атом']), + latin1_fmt("~w", ['\x{10FFFF}']), + "'кирилли́ческий атом'" = fmt("~tw", ['кирилли́ческий атом']), + [$',16#10FFFF,$'] = fmt("~tw", ['\x{10FFFF}']), + + latin1_fmt("~W", ['кирилли́ческий атом', 13]), + latin1_fmt("~W", ['\x{10FFFF}', 13]), + "'кирилли́ческий атом'" = fmt("~tW", ['кирилли́ческий атом', 13]), + [$',16#10FFFF,$'] = fmt("~tW", ['\x{10FFFF}', 13]), + + {ok, [an_atom],[]} = io_lib:fread("~a", "an_atom"), + {ok, [an_atom],[]} = io_lib:fread("~ta", "an_atom"), + Str = "\"ab" ++ [1089] ++ "cd\"", + {ok, ["\"ab"], [1089]++"cd\""} = io_lib:fread("~s", Str), + {ok, ['\"ab'], [1089]++"cd\""} = io_lib:fread("~a", Str), + {ok,[Str], []} = io_lib:fread("~ts", Str), + {ok,[Atom],[]} = io_lib:fread("~ta", Str), + Str = atom_to_list(Atom), + ok. + +latin1_fmt(Fmt, Args) -> + L = fmt(Fmt, Args), + true = lists:all(fun is_latin1/1, L). diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl index 36bc283aec..2ccd89a59f 100644 --- a/lib/stdlib/test/rand_SUITE.erl +++ b/lib/stdlib/test/rand_SUITE.erl @@ -324,8 +324,9 @@ basic_stats_normal(Config) when is_list(Config) -> ct:timetrap({minutes, 6 * length(IntendedMeanVariancePairs)}), %% valgrind needs a lot of time lists:foreach( fun ({IntendedMean, IntendedVariance}) -> - io:format("Testing normal(~.2f, ~.2f)~n", - [float(IntendedMean), float(IntendedVariance)]), + ct:pal( + "Testing normal(~.2f, ~.2f)~n", + [float(IntendedMean), float(IntendedVariance)]), [basic_normal_1(?LOOP, IntendedMean, IntendedVariance, rand:seed_s(Alg), 0, 0) || Alg <- algs()] @@ -485,12 +486,12 @@ do_measure(_Config) -> {int, rand:uniform_s(Range, State)} end) || Algo <- Algos], %% - ct:pal("~nRNG uniform integer 2^(N-1) performance~n",[]), - RangeTwoPowFun = fun (State) -> quart_range(State) bsl 1 end, + ct:pal("~nRNG uniform integer half range performance~n",[]), + HalfRangeFun = fun (State) -> half_range(State) end, TMark2 = measure_1( random, - RangeTwoPowFun, + HalfRangeFun, undefined, fun (Range, State) -> {int, random:uniform_s(Range, State)} @@ -498,18 +499,18 @@ do_measure(_Config) -> _ = [measure_1( Algo, - RangeTwoPowFun, + HalfRangeFun, TMark2, fun (Range, State) -> {int, rand:uniform_s(Range, State)} end) || Algo <- Algos], %% - ct:pal("~nRNG uniform integer 3*2^(N-2)+1 performance~n",[]), - RangeLargeFun = fun (State) -> 3 * quart_range(State) + 1 end, + ct:pal("~nRNG uniform integer half range + 1 performance~n",[]), + HalfRangePlus1Fun = fun (State) -> half_range(State) + 1 end, TMark3 = measure_1( random, - RangeLargeFun, + HalfRangePlus1Fun, undefined, fun (Range, State) -> {int, random:uniform_s(Range, State)} @@ -517,17 +518,18 @@ do_measure(_Config) -> _ = [measure_1( Algo, - RangeLargeFun, + HalfRangePlus1Fun, TMark3, fun (Range, State) -> {int, rand:uniform_s(Range, State)} end) || Algo <- Algos], %% - ct:pal("~nRNG uniform integer 2^128 performance~n",[]), + ct:pal("~nRNG uniform integer full range - 1 performance~n",[]), + FullRangeMinus1Fun = fun (State) -> (half_range(State) bsl 1) - 1 end, TMark4 = measure_1( random, - fun (_) -> 1 bsl 128 end, + FullRangeMinus1Fun, undefined, fun (Range, State) -> {int, random:uniform_s(Range, State)} @@ -535,17 +537,18 @@ do_measure(_Config) -> _ = [measure_1( Algo, - fun (_) -> 1 bsl 128 end, + FullRangeMinus1Fun, TMark4, fun (Range, State) -> {int, rand:uniform_s(Range, State)} end) || Algo <- Algos], %% - ct:pal("~nRNG uniform integer 2^128 + 1 performance~n",[]), + ct:pal("~nRNG uniform integer full range performance~n",[]), + FullRangeFun = fun (State) -> half_range(State) bsl 1 end, TMark5 = measure_1( random, - fun (_) -> (1 bsl 128) + 1 end, + FullRangeFun, undefined, fun (Range, State) -> {int, random:uniform_s(Range, State)} @@ -553,16 +556,73 @@ do_measure(_Config) -> _ = [measure_1( Algo, - fun (_) -> (1 bsl 128) + 1 end, + FullRangeFun, TMark5, fun (Range, State) -> {int, rand:uniform_s(Range, State)} end) || Algo <- Algos], %% - ct:pal("~nRNG uniform float performance~n",[]), + ct:pal("~nRNG uniform integer full range + 1 performance~n",[]), + FullRangePlus1Fun = fun (State) -> (half_range(State) bsl 1) + 1 end, TMark6 = measure_1( random, + FullRangePlus1Fun, + undefined, + fun (Range, State) -> + {int, random:uniform_s(Range, State)} + end), + _ = + [measure_1( + Algo, + FullRangePlus1Fun, + TMark6, + fun (Range, State) -> + {int, rand:uniform_s(Range, State)} + end) || Algo <- Algos], + %% + ct:pal("~nRNG uniform integer double range performance~n",[]), + DoubleRangeFun = fun (State) -> half_range(State) bsl 2 end, + TMark7 = + measure_1( + random, + DoubleRangeFun, + undefined, + fun (Range, State) -> + {int, random:uniform_s(Range, State)} + end), + _ = + [measure_1( + Algo, + DoubleRangeFun, + TMark7, + fun (Range, State) -> + {int, rand:uniform_s(Range, State)} + end) || Algo <- Algos], + %% + ct:pal("~nRNG uniform integer double range + 1 performance~n",[]), + DoubleRangePlus1Fun = fun (State) -> (half_range(State) bsl 2) + 1 end, + TMark8 = + measure_1( + random, + DoubleRangePlus1Fun, + undefined, + fun (Range, State) -> + {int, random:uniform_s(Range, State)} + end), + _ = + [measure_1( + Algo, + DoubleRangePlus1Fun, + TMark8, + fun (Range, State) -> + {int, rand:uniform_s(Range, State)} + end) || Algo <- Algos], + %% + ct:pal("~nRNG uniform float performance~n",[]), + TMark9 = + measure_1( + random, fun (_) -> 0 end, undefined, fun (_, State) -> @@ -572,7 +632,7 @@ do_measure(_Config) -> [measure_1( Algo, fun (_) -> 0 end, - TMark6, + TMark9, fun (_, State) -> {uniform, rand:uniform_s(State)} end) || Algo <- Algos], @@ -582,7 +642,7 @@ do_measure(_Config) -> _ = [measure_1( Algo, fun (_) -> 0 end, - TMark6, + TMark9, fun (_, State) -> {normal, rand:normal_s(State)} end) || Algo <- Algos], @@ -1043,7 +1103,7 @@ range({#{max:=Max}, _}) -> Max; %% Old incorrect range range({_, _, _}) -> 51. % random -quart_range({#{bits:=Bits}, _}) -> 1 bsl (Bits - 2); -quart_range({#{max:=Max}, _}) -> (Max bsr 2) + 1; -quart_range({#{}, _}) -> 1 bsl 62; % crypto -quart_range({_, _, _}) -> 1 bsl 49. % random +half_range({#{bits:=Bits}, _}) -> 1 bsl (Bits - 1); +half_range({#{max:=Max}, _}) -> (Max bsr 1) + 1; +half_range({#{}, _}) -> 1 bsl 63; % crypto +half_range({_, _, _}) -> 1 bsl 50. % random |