aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/doc/src/ct.xml3
-rw-r--r--lib/common_test/doc/src/ct_run.xml8
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml12
-rw-r--r--lib/common_test/src/ct_logs.erl37
-rw-r--r--lib/common_test/src/ct_run.erl18
-rw-r--r--lib/common_test/test/Makefile3
-rw-r--r--lib/common_test/test/ct_keep_logs_SUITE.erl186
-rw-r--r--lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl32
-rw-r--r--lib/common_test/test/ct_test_support.erl3
-rw-r--r--lib/compiler/doc/src/compile.xml25
-rw-r--r--lib/compiler/src/compile.erl89
-rw-r--r--lib/compiler/src/erl_bifs.erl3
-rw-r--r--lib/compiler/src/v3_core.erl12
-rw-r--r--lib/compiler/src/v3_kernel.erl2
-rw-r--r--lib/compiler/test/compilation_SUITE.erl2
-rw-r--r--lib/compiler/test/compile_SUITE.erl44
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl86
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl11
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl298
-rw-r--r--lib/dialyzer/src/typer.erl36
-rw-r--r--lib/edoc/src/edoc_layout.erl19
-rw-r--r--lib/erl_docgen/priv/css/Makefile3
-rw-r--r--lib/erl_docgen/priv/css/highlight.css96
-rw-r--r--lib/erl_docgen/priv/css/otp_doc.css220
-rw-r--r--lib/erl_docgen/priv/images/erlang-logo.gifbin11588 -> 3342 bytes
-rw-r--r--lib/erl_docgen/priv/images/erlang-logo.pngbin14795 -> 5837 bytes
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/Makefile1
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/flip_closed.gifbin82 -> 79 bytes
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/flip_open.gifbin86 -> 85 bytes
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/flip_static.gifbin109 -> 132 bytes
-rw-r--r--lib/erl_docgen/priv/js/highlight.js39
-rw-r--r--lib/erl_docgen/priv/js/highlight.pack.js2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl264
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl5
-rw-r--r--lib/hipe/rtl/Makefile8
-rw-r--r--lib/ssl/src/dtls_connection.erl2
-rw-r--r--lib/ssl/src/dtls_handshake.erl4
-rw-r--r--lib/ssl/src/dtls_record.erl11
-rw-r--r--lib/ssl/src/ssl.erl6
-rw-r--r--lib/ssl/src/ssl_connection.erl2
-rw-r--r--lib/ssl/src/ssl_handshake.erl26
-rw-r--r--lib/ssl/src/tls_connection.erl2
-rw-r--r--lib/ssl/src/tls_record.erl8
-rw-r--r--lib/stdlib/doc/src/beam_lib.xml46
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml1109
-rw-r--r--lib/stdlib/doc/src/gen_server.xml1
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml11
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml2
-rw-r--r--lib/stdlib/doc/src/sets.xml2
-rw-r--r--lib/stdlib/doc/src/supervisor.xml6
-rw-r--r--lib/stdlib/doc/src/sys.xml28
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml17
-rw-r--r--lib/stdlib/src/Makefile1
-rw-r--r--lib/stdlib/src/beam_lib.erl105
-rw-r--r--lib/stdlib/src/erl_abstract_code.erl28
-rw-r--r--lib/stdlib/src/erl_internal.erl4
-rw-r--r--lib/stdlib/src/gen_fsm.erl20
-rw-r--r--lib/stdlib/src/otp_internal.erl49
-rw-r--r--lib/stdlib/src/rand.erl14
-rw-r--r--lib/stdlib/src/shell.erl8
-rw-r--r--lib/stdlib/src/stdlib.app.src1
-rw-r--r--lib/stdlib/test/beam_lib_SUITE.erl40
-rw-r--r--lib/stdlib/test/rand_SUITE.erl106
-rw-r--r--lib/tools/emacs/erlang.el442
64 files changed, 1725 insertions, 1943 deletions
diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index ea9f956271..cd1e51a9a9 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -1190,7 +1190,7 @@
Opts.</fsummary>
<type>
<v>Opts = [OptTuples]</v>
- <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
+ <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {keep_logs,KeepSpec} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
<v>TestDirs = [string()] | string()</v>
<v>Suites = [string()] | [atom()] | string() | atom()</v>
<v>Cases = [atom()] | atom()</v>
@@ -1226,6 +1226,7 @@
<v>VLevels = VLevel | [{Category, VLevel}]</v>
<v>VLevel = integer()</v>
<v>Category = atom()</v>
+ <v>KeepSpec = all | pos_integer()</v>
<v>CTHs = [CTHModule | {CTHModule, CTHInitArgs}]</v>
<v>CTHModule = atom()</v>
<v>CTHInitArgs = term()</v>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 9e6229f1dd..af30550719 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -125,6 +125,7 @@
[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
[-no_esc_chars]
+ [-keep_logs all | NLogs]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
[-exit_status ignore_config]
@@ -164,6 +165,7 @@
[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
[-no_esc_chars]
+ [-keep_logs all | NLogs]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
[-exit_status ignore_config]</pre>
@@ -189,13 +191,15 @@
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-basic_html]
- [-no_esc_chars]</pre>
+ [-no_esc_chars]
+ [-keep_logs all | NLogs]</pre>
</section>
<section>
<title>Refresh HTML Index Files</title>
<pre>
- ct_run -refresh_logs [-logdir LogDir] [-basic_html]</pre>
+ ct_run -refresh_logs [-logdir LogDir] [-basic_html]
+ [-keep_logs all | NLogs]</pre>
</section>
<section>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 76e306c4ed..aea4d05867 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -1353,6 +1353,18 @@
only works when <c>Common Test</c> is running. Printouts with <c>ct:pal/2</c>
are however always displayed on screen.</p></note>
</section>
+
+ <section>
+ <marker id="delete_old_logs"></marker>
+ <title>Delete Old Logs</title>
+ <p><c>Common Test</c> can automatically delete old log. This
+ is specified with the <c>keep_logs</c> option. The default
+ value for this option is <c>all</c>, which means that no
+ logs are deleted. If the value is set to an
+ integer, <c>N</c>, <c>Common Test</c> deletes
+ all <c>ct_run.&lt;timestamp&gt;</c> directories, except
+ the <c>N</c> newest.</p>
+ </section>
</section>
<section>
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 09ad709da5..544b019b64 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -41,6 +41,7 @@
-export([xhtml/2, locate_priv_file/1, make_relative/1]).
-export([insert_javascript/1]).
-export([uri/1]).
+-export([parse_keep_logs/1]).
%% Logging stuff directly from testcase
-export([tc_log/3, tc_log/4, tc_log/5, tc_log/6,
@@ -1946,7 +1947,11 @@ make_all_runs_index(When) ->
end,
Dirs = filelib:wildcard(logdir_prefix()++"*.*"),
- DirsSorted = (catch sort_all_runs(Dirs)),
+ DirsSorted0 = (catch sort_all_runs(Dirs)),
+ DirsSorted =
+ if When == start -> DirsSorted0;
+ true -> maybe_delete_old_dirs(DirsSorted0)
+ end,
LogCacheInfo = get_cache_data(UseCache),
@@ -2064,6 +2069,36 @@ sort_ct_runs(Dirs) ->
{DateHH1,MM1,SS1} =< {DateHH2,MM2,SS2}
end, Dirs).
+parse_keep_logs([Str="all"]) ->
+ parse_keep_logs(list_to_atom(Str));
+parse_keep_logs([NStr]) ->
+ parse_keep_logs(list_to_integer(NStr));
+parse_keep_logs(all) ->
+ all;
+parse_keep_logs(N) when is_integer(N), N>0 ->
+ N.
+
+maybe_delete_old_dirs(Sorted) ->
+ {Keep,Delete} =
+ case application:get_env(common_test, keep_logs) of
+ {ok,MaxN} when is_integer(MaxN), length(Sorted)>MaxN ->
+ lists:split(MaxN,Sorted);
+ _ ->
+ {Sorted,[]}
+ end,
+ delete_old_dirs(Delete),
+ Keep.
+
+delete_old_dirs([]) ->
+ ok;
+delete_old_dirs(Dirs) ->
+ io:put_chars("\n Removing old test directories:\n"),
+ [begin
+ io:put_chars(" " ++ Dir ++ "\n"),
+ rm_dir(Dir)
+ end|| Dir <- Dirs],
+ ok.
+
dir_diff_all_runs(Dirs, LogCache) ->
case LogCache#log_cache.all_runs of
[] ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index cac176de3a..ba27303c81 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -363,6 +363,12 @@ script_start1(Parent, Args) ->
_ ->
application:set_env(common_test, disable_log_cache, true)
end,
+ %% log_cleanup - used by ct_logs
+ KeepLogs = get_start_opt(keep_logs,
+ fun ct_logs:parse_keep_logs/1,
+ all,
+ Args),
+ application:set_env(common_test, keep_logs, KeepLogs),
Opts = #opts{label = Label, profile = Profile,
vts = Vts, shell = Shell,
@@ -970,6 +976,12 @@ run_test1(StartOpts) when is_list(StartOpts) ->
stop_trace(Tracing),
exit(Res);
RefreshDir ->
+ %% log_cleanup - used by ct_logs
+ KeepLogs = get_start_opt(keep_logs,
+ fun ct_logs:parse_keep_logs/1,
+ all,
+ StartOpts),
+ application:set_env(common_test, keep_logs, KeepLogs),
ok = refresh_logs(?abs(RefreshDir)),
exit(done)
end.
@@ -1131,6 +1143,12 @@ run_test2(StartOpts) ->
DisableCacheBool ->
application:set_env(common_test, disable_log_cache, DisableCacheBool)
end,
+ %% log_cleanup - used by ct_logs
+ KeepLogs = get_start_opt(keep_logs,
+ fun ct_logs:parse_keep_logs/1,
+ all,
+ StartOpts),
+ application:set_env(common_test, keep_logs, KeepLogs),
%% stepped execution
Step = get_start_opt(step, value, StartOpts),
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 2f0fc2e05a..b8fb678dbd 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -71,7 +71,8 @@ MODULES= \
test_server_test_lib \
ct_release_test_SUITE \
ct_log_SUITE \
- ct_SUITE
+ ct_SUITE \
+ ct_keep_logs_SUITE
ERL_FILES= $(MODULES:%=%.erl)
HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_keep_logs_SUITE.erl b/lib/common_test/test/ct_keep_logs_SUITE.erl
new file mode 100644
index 0000000000..34bfaaa97a
--- /dev/null
+++ b/lib/common_test/test/ct_keep_logs_SUITE.erl
@@ -0,0 +1,186 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2016. 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.
+%% 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
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_keep_logs_SUITE
+%%%
+%%% Description:
+%%% Test the 'keep_logs' option
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_keep_logs_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+init_per_suite(Config0) ->
+ ct_test_support:init_per_suite(Config0).
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ keep_logs,
+ refresh_logs
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%% Test the keep_logs option with normal common_test runs
+keep_logs(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "keep_logs_SUITE"),
+ Opts0 = ct_test_support:get_opts(Config),
+ Opts = [{suite,Suite},{label,keep_logs} | Opts0],
+
+ LogDir=?config(logdir,Opts),
+ KeepLogsDir = filename:join(LogDir,unique_name("keep_logs-")),
+ ok = file:make_dir(KeepLogsDir),
+ Opts1 = lists:keyreplace(logdir,1,Opts,{logdir,KeepLogsDir}),
+ ct:log("New LogDir = ~s", [KeepLogsDir]),
+
+ %% Create 6 ct_run.* log directories
+ [ok = ct_test_support:run(Opts1, Config) || _ <- lists:seq(1,3)],
+
+ %% Verify the number of directories
+ WC = filename:join(KeepLogsDir,"ct_run.ct@*"),
+ L1 = filelib:wildcard(WC),
+ 6 = length(L1),
+
+ %% Keep all logs
+ {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,all}|Opts1], Config),
+ L2 = filelib:wildcard(WC),
+ 7 = length(L2),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,all}|Opts1], Config),
+ L3 = filelib:wildcard(WC),
+ 8 = length(L3),
+
+ %% N<length of list
+ {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,7}|Opts1], Config),
+ L4 = filelib:wildcard(WC),
+ 7 = length(L4),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,6}|Opts1], Config),
+ L5 = filelib:wildcard(WC),
+ 6 = length(L5),
+
+ %% N>length of list
+ {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,10}|Opts1], Config),
+ L6 = filelib:wildcard(WC),
+ 7 = length(L6),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,10}|Opts1], Config),
+ L7 = filelib:wildcard(WC),
+ 8 = length(L7),
+
+ %% N==length of list
+ {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,8}|Opts1], Config),
+ L8 = filelib:wildcard(WC),
+ 8 = length(L8),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,8}|Opts1], Config),
+ L9 = filelib:wildcard(WC),
+ 8 = length(L9),
+
+ %% N==length of list + current run
+ {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,9}|Opts1], Config),
+ L10 = filelib:wildcard(WC),
+ 9 = length(L10),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,10}|Opts1], Config),
+ L11 = filelib:wildcard(WC),
+ 10 = length(L11).
+
+%% Test the keep_logs option togwther with the refresh_logs option
+refresh_logs(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "keep_logs_SUITE"),
+ Opts0 = ct_test_support:get_opts(Config),
+ LogDir=?config(logdir,Opts0),
+ KeepLogsDir = filename:join(LogDir,unique_name("refresh_logs-")),
+ ok = file:make_dir(KeepLogsDir),
+ Opts1 = lists:keyreplace(logdir,1,Opts0,{logdir,KeepLogsDir}),
+ ct:log("New LogDir = ~s", [KeepLogsDir]),
+
+ %% Create 6 ct_run.* log directories
+ SuiteOpts = [{suite,Suite},{label,refresh_logs} | Opts1],
+ [ok = ct_test_support:run(SuiteOpts, Config) || _ <- lists:seq(1,3)],
+
+ %% Verify the number of directories
+ WC = filename:join(KeepLogsDir,"ct_run.ct@*"),
+ L1 = filelib:wildcard(WC),
+ 6 = length(L1),
+
+ RefreshOpts = [{refresh_logs,KeepLogsDir},{label,refresh_logs} | Opts1],
+
+ %% Keep all logs (note that refresh_logs option prevents the
+ %% creation of a new log directory for the current run)
+ done = ct_test_support:run_ct_run_test([{keep_logs,all}|RefreshOpts], Config),
+ L2 = filelib:wildcard(WC),
+ 6 = length(L2),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,all}|RefreshOpts],Config),
+ L3 = filelib:wildcard(WC),
+ 6 = length(L3),
+
+ %% N<length of list
+ done = ct_test_support:run_ct_run_test([{keep_logs,5}|RefreshOpts], Config),
+ L5 = filelib:wildcard(WC),
+ 5 = length(L5),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,4}|RefreshOpts], Config),
+ L6 = filelib:wildcard(WC),
+ 4 = length(L6),
+
+ %% N>length of list
+ done = ct_test_support:run_ct_run_test([{keep_logs,5}|RefreshOpts], Config),
+ L7 = filelib:wildcard(WC),
+ 4 = length(L7),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,5}|RefreshOpts], Config),
+ L8 = filelib:wildcard(WC),
+ 4 = length(L8),
+
+ %% N==length of list
+ done = ct_test_support:run_ct_run_test([{keep_logs,4}|RefreshOpts], Config),
+ L9 = filelib:wildcard(WC),
+ 4 = length(L9),
+ 0 = ct_test_support:run_ct_script_start([{keep_logs,4}|RefreshOpts], Config),
+ L10 = filelib:wildcard(WC),
+ 4 = length(L10),
+
+ ok.
+%%%-----------------------------------------------------------------
+%%% Internal
+unique_name(Prefix) ->
+ I = erlang:unique_integer([positive]),
+ Prefix ++ integer_to_list(I).
diff --git a/lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl b/lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl
new file mode 100644
index 0000000000..2bccae564b
--- /dev/null
+++ b/lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2016. 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.
+%% 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
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(keep_logs_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [].
+all() ->
+ [test_case].
+
+test_case(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 05a452b99d..06d591871f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -351,6 +351,9 @@ check_result(CtRunTestResult,ExitStatus,Opts)
catch _:_ ->
{error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}
end;
+check_result(done,0,_Opts) ->
+ %% refresh_logs return
+ ok;
check_result(CtRunTestResult,ExitStatus,_Opts) ->
{error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}.
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/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index d60f73d421..b1f1db6fa3 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -108,11 +108,14 @@ is_pure(erlang, list_to_binary, 1) -> true;
is_pure(erlang, list_to_float, 1) -> true;
is_pure(erlang, list_to_integer, 1) -> true;
is_pure(erlang, list_to_pid, 1) -> true;
+is_pure(erlang, list_to_port, 1) -> true;
+is_pure(erlang, list_to_ref, 1) -> true;
is_pure(erlang, list_to_tuple, 1) -> true;
is_pure(erlang, max, 2) -> true;
is_pure(erlang, min, 2) -> true;
is_pure(erlang, phash, 2) -> false;
is_pure(erlang, pid_to_list, 1) -> true;
+is_pure(erlang, port_to_list, 1) -> true;
is_pure(erlang, round, 1) -> true;
is_pure(erlang, setelement, 3) -> true;
is_pure(erlang, size, 1) -> true;
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 8dea7ec03a..fcfc1a4076 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -184,11 +184,8 @@ form({function,_,_,_,_}=F0, Module, Opts) ->
form({attribute,_,module,Mod}, Module, _Opts) ->
true = is_atom(Mod),
Module#imodule{name=Mod};
-form({attribute,_,file,{File,_Line}}, Module, _Opts) ->
- Module#imodule{file=File};
-form({attribute,_,compile,_}, Module, _Opts) ->
- %% Ignore compilation options.
- Module;
+form({attribute,_,file,{File,_Line}}=F, #imodule{attrs=As}=Module, _Opts) ->
+ Module#imodule{file=File, attrs=[attribute(F)|As]};
form({attribute,_,import,_}, Module, _Opts) ->
%% Ignore. We have no futher use for imports.
Module;
@@ -201,9 +198,8 @@ form(_, Module, _Opts) ->
%% Ignore uninteresting forms such as 'eof'.
Module.
-attribute(Attribute) ->
- Fun = fun(A) -> [erl_anno:location(A)] end,
- {attribute,Line,Name,Val0} = erl_parse:map_anno(Fun, Attribute),
+attribute({attribute,A,Name,Val0}) ->
+ Line = [erl_anno:location(A)],
Val = if
is_list(Val0) -> Val0;
true -> [Val0]
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 4b5d7d919c..7a0a63d286 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -148,6 +148,8 @@ include_attribute(opaque) -> false;
include_attribute(export_type) -> false;
include_attribute(record) -> false;
include_attribute(optional_callbacks) -> false;
+include_attribute(file) -> false;
+include_attribute(compile) -> false;
include_attribute(_) -> true.
function({#c_var{name={F,Arity}=FA},Body}, St0) ->
diff --git a/lib/compiler/test/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
index abf5f225d7..ae13168429 100644
--- a/lib/erl_docgen/priv/images/erlang-logo.gif
+++ b/lib/erl_docgen/priv/images/erlang-logo.gif
Binary files differ
diff --git a/lib/erl_docgen/priv/images/erlang-logo.png b/lib/erl_docgen/priv/images/erlang-logo.png
index 56291aac15..41bba97417 100644
--- a/lib/erl_docgen/priv/images/erlang-logo.png
+++ b/lib/erl_docgen/priv/images/erlang-logo.png
Binary files differ
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
index 9a27c7c25d..a75107a782 100644
--- a/lib/erl_docgen/priv/js/flipmenu/flip_closed.gif
+++ b/lib/erl_docgen/priv/js/flipmenu/flip_closed.gif
Binary files differ
diff --git a/lib/erl_docgen/priv/js/flipmenu/flip_open.gif b/lib/erl_docgen/priv/js/flipmenu/flip_open.gif
index 9dda60e73a..1274637fe0 100644
--- a/lib/erl_docgen/priv/js/flipmenu/flip_open.gif
+++ b/lib/erl_docgen/priv/js/flipmenu/flip_open.gif
Binary files differ
diff --git a/lib/erl_docgen/priv/js/flipmenu/flip_static.gif b/lib/erl_docgen/priv/js/flipmenu/flip_static.gif
index 2b3ddb5382..4cc914a50a 100644
--- a/lib/erl_docgen/priv/js/flipmenu/flip_static.gif
+++ b/lib/erl_docgen/priv/js/flipmenu/flip_static.gif
Binary files differ
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={"&":"&amp;","<":"&lt;",">":"&gt;"};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"/>:
&#160;
<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"/>:
&#160;
<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/hipe/rtl/Makefile b/lib/hipe/rtl/Makefile
index b4cdf8b1f2..357f1f6950 100644
--- a/lib/hipe/rtl/Makefile
+++ b/lib/hipe/rtl/Makefile
@@ -118,10 +118,12 @@ else
TYPE_STR=
endif
-ifeq ($(FLAVOR),smp)
-FLAVOR_STR=.smp
-else
+FLAVOR=$(DEFAULT_FLAVOR)
+
+ifeq ($(FLAVOR),plain)
FLAVOR_STR=
+else
+FLAVOR_STR=.smp
endif
ifeq ($(XCOMP),yes)
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>&nbsp;Option = {debug,Dbgs}</v>
- <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
- <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
- <v>StateName = atom()</v>
- <v>StateData = term()</v>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
- <v>&nbsp;&nbsp;| {via,Module,ViaName}</v>
- <v>&nbsp;Name = atom()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName}</v>
- <v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Module = atom()</v>
- <v>Args = term()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
- <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
- <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
- <v>&nbsp;&nbsp;SOpts = [term()]</v>
- <v>Result = {ok,Pid} | ignore | {error,Error}</v>
- <v>&nbsp;Pid = pid()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName}</v>
- <v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Module = atom()</v>
- <v>Args = term()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
- <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
- <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
- <v>&nbsp;&nbsp;SOpts = [SOpt]</v>
- <v>&nbsp;&nbsp;&nbsp;SOpt - see erlang:spawn_opt/2,3,4,5</v>
- <v>Result = {ok,Pid} | ignore | {error,Error}</v>
- <v>&nbsp;Pid = pid()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Node = atom()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;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>&nbsp;&nbsp;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>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,NewStateData}</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,NewStateData}</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}</v>
- <v>&nbsp;Reply = term()</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {ok,StateName,StateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason} | ignore</v>
- <v>&nbsp;StateName = atom()</v>
- <v>&nbsp;StateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,NewStateData}</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;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>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}</v>
- <v>&nbsp;Reply = term()</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;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/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/sets.xml b/lib/stdlib/doc/src/sets.xml
index 44dc104645..4dc5d68151 100644
--- a/lib/stdlib/doc/src/sets.xml
+++ b/lib/stdlib/doc/src/sets.xml
@@ -40,7 +40,7 @@
<p>This module provides the same interface as the
<seealso marker="ordsets"><c>ordsets(3)</c></seealso> module
- but with a defined representation. One difference is
+ but with an undefined representation. One difference is
that while this module considers two elements as different if they
do not match (<c>=:=</c>), <c>ordsets</c> considers two elements as
different if and only if they do not compare equal (<c>==</c>).</p>
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 &gt; 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 &gt; 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_internal.erl b/lib/stdlib/src/erl_internal.erl
index 006e7946af..9a1b17fdb7 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -331,6 +331,8 @@ bif(list_to_float, 1) -> true;
bif(list_to_integer, 1) -> true;
bif(list_to_integer, 2) -> true;
bif(list_to_pid, 1) -> true;
+bif(list_to_port, 1) -> true;
+bif(list_to_ref, 1) -> true;
bif(list_to_tuple, 1) -> true;
bif(load_module, 2) -> true;
bif(make_ref, 0) -> true;
@@ -348,6 +350,7 @@ bif(nodes, 1) -> true;
bif(now, 0) -> true;
bif(open_port, 2) -> true;
bif(pid_to_list, 1) -> true;
+bif(port_to_list, 1) -> true;
bif(port_close, 1) -> true;
bif(port_command, 2) -> true;
bif(port_command, 3) -> true;
@@ -361,6 +364,7 @@ bif(process_info, 2) -> true;
bif(processes, 0) -> true;
bif(purge_module, 1) -> true;
bif(put, 2) -> true;
+bif(ref_to_list, 1) -> true;
bif(register, 2) -> true;
bif(registered, 0) -> true;
bif(round, 1) -> true;
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/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/shell.erl b/lib/stdlib/src/shell.erl
index 394f4f2fa4..961f5f8a30 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -349,16 +349,10 @@ default_prompt(N) ->
%% Don't bother flattening the list irrespective of what the
%% I/O-protocol states.
case is_alive() of
- true -> io_lib:format(<<"(~ts)~w> ">>, [node_string(), N]);
+ true -> io_lib:format(<<"(~s)~w> ">>, [node(), N]);
false -> io_lib:format(<<"~w> ">>, [N])
end.
-node_string() ->
- case encoding() of
- latin1 -> io_lib:write_atom_as_latin1(node());
- _ -> io_lib:write_atom(node())
- end.
-
%% expand_hist(Expressions, CommandNumber)
%% Preprocess the expression list replacing all history list commands
%% with their expansions.
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/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
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 59b20c552e..2d86eb44d4 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -97,18 +97,6 @@ Erlang mode menu."
:type '(restricted-sexp :match-alternatives (stringp 'nil))
:safe (lambda (val) (or (eq nil val) (stringp val))))
-(defconst erlang-xemacs-p (string-match "Lucid\\|XEmacs" emacs-version)
- "Non-nil when running under XEmacs or Lucid Emacs.")
-
-(defvar erlang-xemacs-popup-menu '("Erlang Mode Commands" . nil)
- "Common popup menu for all buffers in Erlang mode.
-
-This variable is destructively modified every time the Erlang menu
-is modified. The effect is that all changes take effect in all
-buffers in Erlang mode, just like under GNU Emacs.
-
-Never EVER set this variable!")
-
(defvar erlang-menu-items '(erlang-menu-base-items
erlang-menu-skel-items
erlang-menu-shell-items
@@ -128,17 +116,14 @@ variable.")
(defvar erlang-menu-base-items
'(("Indent"
(("Indent Line" erlang-indent-command)
- ("Indent Region " erlang-indent-region
- (if erlang-xemacs-p (mark) mark-active))
+ ("Indent Region " erlang-indent-region mark-active)
("Indent Clause" erlang-indent-clause)
("Indent Function" erlang-indent-function)
("Indent Buffer" erlang-indent-current-buffer)))
("Edit"
(("Fill Comment" erlang-fill-paragraph)
- ("Comment Region" comment-region
- (if erlang-xemacs-p (mark) mark-active))
- ("Uncomment Region" erlang-uncomment-region
- (if erlang-xemacs-p (mark) mark-active))
+ ("Comment Region" comment-region mark-active)
+ ("Uncomment Region" uncomment-region mark-active)
nil
("Beginning of Function" erlang-beginning-of-function)
("End of Function" erlang-end-of-function)
@@ -576,8 +561,7 @@ This is an elisp list of options. Each option can be either:
- a string")
(eval-and-compile
- (defvar erlang-regexp-modern-p
- (if (> emacs-major-version 21) t nil)
+ (defvar erlang-regexp-modern-p t
"Non-nil when this version of Emacs uses a modern version of regexp.
Supporting \_< and \_> This is determined by checking the version of Emacs used."))
@@ -836,6 +820,8 @@ resulting regexp is surrounded by \\_< and \\_>."
"list_to_float"
"list_to_integer"
"list_to_pid"
+ "list_to_port"
+ "list_to_ref"
"list_to_tuple"
"load_module"
"make_ref"
@@ -854,12 +840,14 @@ resulting regexp is surrounded by \\_< and \\_>."
"port_command"
"port_connect"
"port_control"
+ "port_to_list"
"pre_loaded"
"process_flag"
"process_info"
"processes"
"purge_module"
"put"
+ "ref_to_list"
"register"
"registered"
"round"
@@ -967,14 +955,12 @@ resulting regexp is surrounded by \\_< and \\_>."
"port_get_data"
"port_info"
"port_set_data"
- "port_to_list"
"ports"
"posixtime_to_universaltime"
"prepare_loading"
"process_display"
"raise"
"read_timer"
- "ref_to_list"
"resume_process"
"send"
"send_after"
@@ -1053,8 +1039,7 @@ behaviour.")
(define-key map "\M-q" 'erlang-fill-paragraph)
(unless (boundp 'beginning-of-defun-function)
(define-key map "\M-\C-a" 'erlang-beginning-of-function)
- (define-key map "\M-\C-e" 'erlang-end-of-function)
- (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
+ (define-key map "\M-\C-e" 'erlang-end-of-function))
(define-key map "\M-\t" 'erlang-complete-tag)
(define-key map "\C-c\M-\t" 'tempo-complete-tag)
(define-key map "\M-+" 'erlang-find-next-tag)
@@ -1069,7 +1054,7 @@ behaviour.")
(define-key map "\C-c\C-l" 'erlang-compile-display)
(define-key map "\C-c\C-s" 'erlang-show-syntactic-information)
(define-key map "\C-c\C-q" 'erlang-indent-function)
- (define-key map "\C-c\C-u" 'erlang-uncomment-region)
+ (define-key map "\C-c\C-u" 'uncomment-region)
(define-key map "\C-c\C-y" 'erlang-clone-arguments)
(define-key map "\C-c\C-a" 'erlang-align-arrows)
(define-key map "\C-c\C-z" 'erlang-shell-display)
@@ -1112,12 +1097,7 @@ behaviour.")
"Font lock keyword highlighting a function header.")
(defface erlang-font-lock-exported-function-name-face
- (if (featurep 'xemacs)
- (progn
- (require 'font-lock)
- `((t (:foreground ,(face-foreground 'font-lock-function-name-face))
- (:background ,(face-background 'font-lock-function-name-face)))))
- '((default (:inherit font-lock-function-name-face))))
+ '((default (:inherit font-lock-function-name-face)))
"Face used for highlighting exported functions."
:group 'erlang)
@@ -1329,11 +1309,7 @@ Example:
The difference between this and the standard Erlang Mode
syntax table is that `_' is treated as part of words by
-this syntax table.
-
-Unfortunately, XEmacs hasn't got support for a special Font
-Lock syntax table. The effect is that `apply' in the atom
-`foo_apply' will be highlighted as a bif.")
+this syntax table.")
(defvar erlang-replace-etags-tags-completion-table nil
"Internal flag used by advice `erlang-replace-tags-table'.
@@ -1353,18 +1329,9 @@ replaced by `erlang-etags-tags-completion-table'.")
(defvar next-error-last-buffer))
(eval-when-compile
- (if (or (featurep 'bytecomp)
- (featurep 'byte-compile))
- (progn
- (cond ((string-match "Lucid\\|XEmacs" emacs-version)
- (put 'comment-indent-hook 'byte-obsolete-variable nil)
- ;; Do not warn for unused variables
- ;; when compiling under XEmacs.
- (setq byte-compile-warnings
- '(free-vars unresolved callargs redefine))))
- (require 'comint)
- (require 'tempo)
- (require 'compile))))
+ (require 'comint)
+ (require 'tempo)
+ (require 'compile))
(defun erlang-version ()
@@ -1446,8 +1413,7 @@ Other commands:
(erlang-tags-init)
(erlang-font-lock-init)
(erlang-skel-init)
- (when (fboundp 'tempo-use-tag-list)
- (tempo-use-tag-list 'erlang-tempo-tags))
+ (tempo-use-tag-list 'erlang-tempo-tags)
(when (and (fboundp 'add-function) (fboundp 'erldoc-eldoc-function))
(or eldoc-documentation-function
(setq-local eldoc-documentation-function #'ignore))
@@ -1506,8 +1472,7 @@ Other commands:
;; delsel/pending-del mode. Also, set up text properties for bit
;; syntax handling.
(mapc #'(lambda (cmd)
- (put cmd 'delete-selection t) ;for delsel (Emacs)
- (put cmd 'pending-delete t)) ;for pending-del (XEmacs)
+ (put cmd 'delete-selection t)) ;for delsel (Emacs)
'(erlang-electric-semicolon
erlang-electric-comma
erlang-electric-gt))
@@ -1545,8 +1510,6 @@ Other commands:
(make-local-variable 'indent-region-function)
(setq indent-region-function 'erlang-indent-region)
(set (make-local-variable 'comment-indent-function) 'erlang-comment-indent)
- (if (<= emacs-major-version 18)
- (set (make-local-variable 'comment-indent-hook) 'erlang-comment-indent))
(set (make-local-variable 'parse-sexp-ignore-comments) t)
(set (make-local-variable 'dabbrev-case-fold-search) nil)
(set (make-local-variable 'imenu-prev-index-position-function)
@@ -1753,7 +1716,7 @@ menu configuration is changed."
(defun erlang-menu-install (name items keymap &optional popup)
- "Install a menu in Emacs or XEmacs based on an abstract description.
+ "Install a menu in Emacs based on an abstract description.
NAME is the name of the menu.
@@ -1764,32 +1727,15 @@ same same form as ITEMS. The third optional element is an expression
which is evaluated every time the menu is displayed. Should the
expression evaluate to nil the menu item is ghosted.
-KEYMAP is the keymap to add to menu to. (When using XEmacs, the menu
-will only be visible when this menu is the global, the local, or an
-activate minor mode keymap.)
-
-If POPUP is non-nil, the menu is bound to the XEmacs `mode-popup-menu'
-variable, i.e. it will popup when pressing the right mouse button.
+KEYMAP is the keymap to add to menu to.
Please see the variable `erlang-menu-base-items'."
- (cond (erlang-xemacs-p
- (let ((menu (erlang-menu-xemacs name items keymap)))
- ;; We add the menu to the global menubar.
- ;;(funcall (symbol-function 'set-buffer-menubar)
- ;; (symbol-value 'current-menubar))
- (funcall (symbol-function 'add-submenu) nil menu)
- (setcdr erlang-xemacs-popup-menu (cdr menu))
- (if (and popup (boundp 'mode-popup-menu))
- (funcall (symbol-function 'set)
- 'mode-popup-menu erlang-xemacs-popup-menu))))
- ((>= emacs-major-version 19)
- (define-key keymap (vector 'menu-bar (intern name))
- (erlang-menu-make-keymap name items)))
- (t nil)))
+ (define-key keymap (vector 'menu-bar (intern name))
+ (erlang-menu-make-keymap name items)))
(defun erlang-menu-make-keymap (name items)
- "Build a menu for Emacs 19."
+ "Build a menu."
(let ((menumap (funcall (symbol-function 'make-sparse-keymap)
name))
(count 0)
@@ -1827,54 +1773,6 @@ Please see the variable `erlang-menu-base-items'."
(setq items (cdr items)))
(cons name menumap)))
-
-(defun erlang-menu-xemacs (name items &optional keymap)
- "Build a menu for XEmacs."
- (let ((res '())
- first second third entry)
- (while items
- ;; Replace any occurrence of atoms by their value.
- (while (and items (atom (car items)) (not (null (car items))))
- (if (and (boundp (car items))
- (listp (symbol-value (car items))))
- (setq items (append (reverse (symbol-value (car items)))
- (cdr items)))
- (setq items (cdr items))))
- (setq first (car-safe (car items)))
- (setq second (car-safe (cdr-safe (car items))))
- (setq third (car-safe (cdr-safe (cdr-safe (car items)))))
- (cond ((null first)
- (setq res (cons "------" res)))
- ((symbolp second)
- (setq res (cons (vector first second (or third t)) res)))
- ((and (consp second) (eq (car second) 'lambda))
- (setq res (cons (vector first (list 'call-interactively second)
- (or third t)) res)))
- (t
- (setq res (cons (cons first
- (cdr (erlang-menu-xemacs
- first second)))
- res))))
- (setq items (cdr items)))
- (setq res (reverse res))
- ;; When adding a menu to a minor-mode keymap under Emacs,
- ;; it disappears when the mode is disabled. The expression
- ;; generated below imitates this behaviour.
- ;; (This could be expressed much clearer using backquotes,
- ;; but I don't want to pull in every package.)
- (if keymap
- (let ((expr (list 'or
- (list 'eq keymap 'global-map)
- (list 'eq keymap (list 'current-local-map))
- (list 'symbol-value
- (list 'car-safe
- (list 'rassq
- keymap
- 'minor-mode-map-alist))))))
- (setq res (cons ':included (cons expr res)))))
- (cons name res)))
-
-
(defun erlang-menu-substitute (items alist)
"Substitute functions in menu described by ITEMS.
@@ -2278,7 +2176,7 @@ mode with the command `M-x erlang-mode RET'.")))
;; Skeleton code:
;; This code is based on the package `tempo' which is part of modern
-;; Emacsen. (GNU Emacs 19.25 (?) and XEmacs 19.14.)
+;; Emacsen.
(defvar erlang-skel)
(defun erlang-skel-init ()
@@ -2289,36 +2187,33 @@ all skeletons.
The skeleton routines are based on the `tempo' package. Should this
package not be present, this function does nothing."
(interactive)
- (condition-case nil
- (require 'tempo)
- (error t))
- (if (featurep 'tempo)
- (let ((skel erlang-skel)
- (menu '()))
- (while skel
- (cond ((null (car skel))
- (setq menu (cons nil menu)))
- (t
- (funcall (symbol-function 'tempo-define-template)
- (concat "erlang-" (nth 1 (car skel)))
- ;; The tempo template used contains an `include'
- ;; function call only, hence changes to the
- ;; variables describing the templates take effect
- ;; immdiately.
- (list (list 'erlang-skel-include (nth 2 (car skel))))
- (nth 1 (car skel))
- (car (car skel))
- 'erlang-tempo-tags)
- (setq menu (cons (erlang-skel-make-menu-item
- (car skel)) menu))))
- (setq skel (cdr skel)))
- (setq erlang-menu-skel-items
- (list nil (list "Skeletons" (nreverse menu))))
- (setq erlang-menu-items
- (erlang-menu-add-above 'erlang-menu-skel-items
- 'erlang-menu-version-items
- erlang-menu-items))
- (erlang-menu-init))))
+ (require 'tempo)
+ (let ((skel erlang-skel)
+ (menu '()))
+ (while skel
+ (cond ((null (car skel))
+ (setq menu (cons nil menu)))
+ (t
+ (funcall (symbol-function 'tempo-define-template)
+ (concat "erlang-" (nth 1 (car skel)))
+ ;; The tempo template used contains an `include'
+ ;; function call only, hence changes to the
+ ;; variables describing the templates take effect
+ ;; immediately.
+ (list (list 'erlang-skel-include (nth 2 (car skel))))
+ (nth 1 (car skel))
+ (car (car skel))
+ 'erlang-tempo-tags)
+ (setq menu (cons (erlang-skel-make-menu-item
+ (car skel)) menu))))
+ (setq skel (cdr skel)))
+ (setq erlang-menu-skel-items
+ (list nil (list "Skeletons" (nreverse menu))))
+ (setq erlang-menu-items
+ (erlang-menu-add-above 'erlang-menu-skel-items
+ 'erlang-menu-version-items
+ erlang-menu-items))
+ (erlang-menu-init)))
(defun erlang-skel-make-menu-item (skel)
(let ((func (intern (concat "tempo-template-erlang-" (nth 1 skel)))))
@@ -3263,11 +3158,8 @@ With argument, do this that many times."
(interactive)
(push-mark (point))
(erlang-end-of-clause 1)
- ;; Sets the region. In Emacs 19 and XEmacs, we want to activate
- ;; the region.
- (condition-case nil
- (push-mark (point) nil t)
- (error (push-mark (point))))
+ ;; Sets the region.
+ (push-mark (point) nil t)
(erlang-beginning-of-clause 1)
;; The above function deactivates the mark.
(if (boundp 'deactivate-mark)
@@ -3375,11 +3267,8 @@ With negative argument go towards the beginning of the buffer."
(interactive)
(push-mark (point))
(erlang-end-of-function 1)
- ;; Sets the region. In Emacs 19 and XEmacs, we want to activate
- ;; the region.
- (condition-case nil
- (push-mark (point) nil t)
- (error (push-mark (point))))
+ ;; Sets the region.
+ (push-mark (point) nil t)
(erlang-beginning-of-function 1)
;; The above function deactivates the mark.
(if (boundp 'deactivate-mark)
@@ -3462,13 +3351,6 @@ and initial `%':s."
(fill-prefix comment-fill-prefix))
(fill-paragraph justify))))))
-
-(defun erlang-uncomment-region (beg end)
- "Uncomment all commented lines in the region."
- (interactive "r")
- (uncomment-region beg end))
-
-
(defun erlang-generate-new-clause ()
"Create additional Erlang clause header.
@@ -3917,9 +3799,9 @@ of arguments could be found, otherwise nil."
(if (and (stringp str)
(not (string-match (eval-when-compile
(concat "\\`" erlang-atom-regexp "\\'")) str)))
- (progn (if (fboundp 'replace-regexp-in-string)
- (setq str (replace-regexp-in-string "'" "\\'" str t t )))
- (concat "'" str "'"))
+ (progn
+ (setq str (replace-regexp-in-string "'" "\\'" str t t ))
+ (concat "'" str "'"))
str)))
@@ -4314,9 +4196,7 @@ context, nil is returned."
(let* ((lim (or lim (save-excursion
(erlang-beginning-of-clause)
(point))))
- (state (if (fboundp 'syntax-ppss) ; post Emacs 21.3
- (funcall (symbol-function 'syntax-ppss))
- (parse-partial-sexp lim (point)))))
+ (state (funcall (symbol-function 'syntax-ppss))))
(cond
((eq (nth 3 state) ?') 'atom)
((nth 3 state) 'string)
@@ -4426,14 +4306,8 @@ This function is designed to be a member of a criteria list."
;;; Erlang tags support which is aware of erlang modules.
-;; Not yet implemented under XEmacs. (Hint: The Emacs 19 etags
-;; package works under XEmacs.)
-
(eval-when-compile
- (if (or (featurep 'bytecomp)
- (featurep 'byte-compile))
- (progn
- (require 'etags))))
+ (require 'etags))
;; Variables:
@@ -4477,28 +4351,16 @@ After calling this function, the tags functions are aware of
Erlang modules. Tags can be entered on the for `module:tag' as well
as on the old form `tag'.
-In the completion list, `module:tag' and `module:' shows up.
-
-This function only works under Emacs 18 and Emacs 19. Currently, It
-is not implemented under XEmacs. (Hint: The Emacs 19 etags module
-works under XEmacs.)"
+In the completion list, `module:tag' and `module:' shows up."
(interactive)
- (cond ((= emacs-major-version 18)
- (require 'tags)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t))
- (t
- (require 'etags)
- (set (make-local-variable 'find-tag-default-function)
- 'erlang-find-tag-for-completion)
- (if (>= emacs-major-version 25)
- (add-hook 'xref-backend-functions
- #'erlang-etags--xref-backend nil t)
- ;; Test on a function available in the Emacs 19 version
- ;; of tags but not in the XEmacs version.
- (when (fboundp 'find-tag-noselect)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t))))))
+ (require 'etags)
+ (set (make-local-variable 'find-tag-default-function)
+ 'erlang-find-tag-for-completion)
+ (if (>= emacs-major-version 25)
+ (add-hook 'xref-backend-functions
+ #'erlang-etags--xref-backend nil t)
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t)))
@@ -4635,8 +4497,7 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(while (null file)
(or erlang-tags-file-list
(save-excursion
- (if (and (featurep 'etags)
- (funcall
+ (if (and (funcall
(symbol-function 'visit-tags-table-buffer) 'same)
(funcall
(symbol-function 'visit-tags-table-buffer) t))
@@ -4655,35 +4516,12 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(find-file-noselect file)))))
((string-match ":" modtagname)
- (if (boundp 'find-tag-tag-order)
- ;; Method one: Add module-recognising functions to the
- ;; list of order functions. However, the tags system
- ;; from Emacs 18, and derives thereof (read: XEmacs)
- ;; hasn't got this feature.
- (progn
- (erlang-tags-install-module-check)
- (unwind-protect
- (funcall (symbol-function 'find-tag)
- modtagname next-p regexp-p)
- (erlang-tags-remove-module-check)))
- ;; Method two: Call the tags system until a file matching
- ;; the module is found. This could result in that many
- ;; files are read. (e.g. The tag "foo:file" will take a
- ;; while to process.)
- (let* ((modname (substring modtagname 0 (match-beginning 0)))
- (tagname (substring modtagname (match-end 0) nil))
- (last-tag tagname)
- file)
- (while
- (progn
- (funcall (symbol-function 'find-tag) tagname next-p regexp-p)
- (setq next-p t)
- ;; Determine the module form the file name. (The
- ;; alternative, to check `-module', would make this
- ;; code useless for non-Erlang programs.)
- (setq file (erlang-get-module-from-file-name buffer-file-name))
- (not (and (stringp file)
- (string= modname file))))))))
+ (progn
+ (erlang-tags-install-module-check)
+ (unwind-protect
+ (funcall (symbol-function 'find-tag)
+ modtagname next-p regexp-p)
+ (erlang-tags-remove-module-check))))
(t
(funcall (symbol-function 'find-tag) modtagname next-p regexp-p)))
(current-buffer))) ; Return the new buffer.
@@ -4713,9 +4551,7 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(prompt (if default
(format "%s(default %s) " prompt default)
prompt))
- (spec (if (featurep 'etags)
- (completing-read prompt 'erlang-tags-complete-tag)
- (read-string prompt))))
+ (spec (completing-read prompt 'erlang-tags-complete-tag)))
(list (if (equal spec "")
(or default (error "There is no default tag"))
spec)))))
@@ -4732,23 +4568,12 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
"Install our own tag search functions."
;; Make sure our functions are installed in TAGS files loaded
;; into Emacs while searching.
- (cond
- ((>= emacs-major-version 20)
- (setq erlang-tags-orig-format-functions
- (symbol-value 'tags-table-format-functions))
- (funcall (symbol-function 'set) 'tags-table-format-functions
- (cons 'erlang-tags-recognize-tags-table
- erlang-tags-orig-format-functions))
- (setq erlang-tags-buffer-list '())
- )
- (t
- (setq erlang-tags-orig-format-hooks
- (symbol-value 'tags-table-format-hooks))
- (funcall (symbol-function 'set) 'tags-table-format-hooks
- (cons 'erlang-tags-recognize-tags-table
- erlang-tags-orig-format-hooks))
- (setq erlang-tags-buffer-list '())
- ))
+ (setq erlang-tags-orig-format-functions
+ (symbol-value 'tags-table-format-functions))
+ (funcall (symbol-function 'set) 'tags-table-format-functions
+ (cons 'erlang-tags-recognize-tags-table
+ erlang-tags-orig-format-functions))
+ (setq erlang-tags-buffer-list '())
;; Install our functions in the TAGS files already resident.
(save-excursion
@@ -4810,17 +4635,9 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(defun erlang-tags-remove-module-check ()
"Remove our own tags search functions."
- (cond
- ((>= emacs-major-version 20)
- (funcall (symbol-function 'set)
- 'tags-table-format-functions
- erlang-tags-orig-format-functions)
- )
- (t
- (funcall (symbol-function 'set)
- 'tags-table-format-hooks
- erlang-tags-orig-format-hooks)
- ))
+ (funcall (symbol-function 'set)
+ 'tags-table-format-functions
+ erlang-tags-orig-format-functions)
;; Remove our functions from the TAGS files. (Note that
;; `tags-table-computed-list' need not be the same list as when
@@ -4888,21 +4705,17 @@ for a tag on the form `module:tag'."
;;; completion-table' containing all normal tags plus tags on the form
;;; `module:tag' and `module:'.
-(when (and (locate-library "etags")
- (require 'etags)
- (fboundp 'etags-tags-completion-table)
- (fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
- (if (fboundp 'advice-add)
- ;; Emacs 24.4+
- (advice-add 'etags-tags-completion-table :around
- #'erlang-etags-tags-completion-table-advice)
- ;; Emacs 23.1-24.3
- (defadvice etags-tags-completion-table (around
- erlang-replace-tags-table
- activate)
- (if erlang-replace-etags-tags-completion-table
- (setq ad-return-value (erlang-etags-tags-completion-table))
- ad-do-it))))
+(if (fboundp 'advice-add)
+ ;; Emacs 24.4+
+ (advice-add 'etags-tags-completion-table :around
+ #'erlang-etags-tags-completion-table-advice)
+ ;; Emacs 23.1-24.3
+ (defadvice etags-tags-completion-table (around
+ erlang-replace-tags-table
+ activate)
+ (if erlang-replace-etags-tags-completion-table
+ (setq ad-return-value (erlang-etags-tags-completion-table))
+ ad-do-it)))
(defun erlang-etags-tags-completion-table-advice (oldfun)
(if erlang-replace-etags-tags-completion-table
@@ -4916,28 +4729,9 @@ Completes to the set of names listed in the current tags table.
Should the Erlang tags system be installed this command knows
about Erlang modules."
(interactive)
- (condition-case nil
- (require 'etags)
- (error nil))
- (cond ((and (fboundp 'etags-tags-completion-table)
- (fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
- (let ((erlang-replace-etags-tags-completion-table t))
- (complete-tag)))
- ((and erlang-tags-installed
- (fboundp 'complete-tag)
- (fboundp 'tags-complete-tag)) ; Emacs 19-22
- (let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag)))
- (fset 'tags-complete-tag
- (symbol-function 'erlang-tags-complete-tag))
- (unwind-protect
- (complete-tag)
- (fset 'tags-complete-tag orig-tags-complete-tag))))
- ((fboundp 'complete-tag) ; Emacs 19
- (complete-tag))
- ((fboundp 'tag-complete-symbol) ; XEmacs
- (funcall (symbol-function 'tag-complete-symbol)))
- (t
- (error "This version of Emacs can't complete tags"))))
+ (require 'etags)
+ (let ((erlang-replace-etags-tags-completion-table t))
+ (complete-tag)))
(defun erlang-find-tag-for-completion ()
@@ -5435,13 +5229,11 @@ The following special commands are available:
'inferior-erlang-strip-delete nil t)
(add-hook 'comint-output-filter-functions
'inferior-erlang-strip-ctrl-m nil t)
- ;; Some older versions of comint don't have an input ring.
- (if (fboundp 'comint-read-input-ring)
- (progn
- (setq comint-input-ring-file-name erlang-input-ring-file-name)
- (comint-read-input-ring t)
- (make-local-variable 'kill-buffer-hook)
- (add-hook 'kill-buffer-hook 'comint-write-input-ring)))
+
+ (setq comint-input-ring-file-name erlang-input-ring-file-name)
+ (comint-read-input-ring t)
+ (make-local-variable 'kill-buffer-hook)
+ (add-hook 'kill-buffer-hook 'comint-write-input-ring)
;; At least in Emacs 21, we need to be in `compilation-minor-mode'
;; for `next-error' to work. We can avoid it clobbering the shell
;; keys thus.
@@ -5559,10 +5351,7 @@ editing control characters:
\\{erlang-shell-mode-map}"
(interactive
(when current-prefix-arg
- (list (if (fboundp 'read-shell-command)
- ;; `read-shell-command' is a new function in Emacs 23.
- (read-shell-command "Erlang command: ")
- (read-string "Erlang command: ")))))
+ (list ((read-shell-command "Erlang command: ")))))
(require 'comint)
(let (cmd opts)
(if command
@@ -5590,10 +5379,8 @@ editing control characters:
(setq inferior-erlang-process
(get-buffer-process inferior-erlang-buffer))
- (if (> 21 emacs-major-version) ; funcalls to avoid compiler warnings
- (funcall (symbol-function 'set-process-query-on-exit-flag)
- inferior-erlang-process nil)
- (funcall (symbol-function 'process-kill-without-query) inferior-erlang-process))
+ (funcall (symbol-function 'set-process-query-on-exit-flag)
+ inferior-erlang-process nil)
(if erlang-inferior-shell-split-window
(switch-to-buffer-other-window inferior-erlang-buffer)
(switch-to-buffer inferior-erlang-buffer))
@@ -5661,7 +5448,7 @@ frame will become deselected before the next command."
(defun inferior-erlang-window (&optional all-frames)
"Return the window containing the inferior Erlang, or nil."
(and (inferior-erlang-running-p)
- (if (and all-frames (>= emacs-major-version 19))
+ (if all-frames
(get-buffer-window inferior-erlang-buffer t)
(get-buffer-window inferior-erlang-buffer))))
@@ -5733,10 +5520,7 @@ Return the position after the newly inserted command."
;; has been sorted out in Emacs 21. -- fx
(let ((comint-eol-on-send nil)
(comint-input-filter (if hist comint-input-filter 'ignore)))
- (if (and (not erlang-xemacs-p)
- (>= emacs-major-version 22))
- (comint-send-input nil t)
- (comint-send-input)))
+ (comint-send-input nil t))
;; Adjust all windows whose points are incorrect.
(if (null comint-process-echoes)
(walk-windows
@@ -6120,8 +5904,7 @@ Tab characters are counted by their visual width."
Simplified version of a combination `defalias' and `make-obsolete',
it assumes that NEWDEF is loaded."
(defalias sym (symbol-function newdef))
- (if (fboundp 'make-obsolete)
- (make-obsolete sym newdef "long ago")))
+ (make-obsolete sym newdef "long ago"))
(erlang-obsolete 'calculate-erlang-indent 'erlang-calculate-indent)
@@ -6141,9 +5924,8 @@ it assumes that NEWDEF is loaded."
(defconst erlang-unload-hook
(list (lambda ()
- (when (featurep 'advice)
- (ad-unadvise 'Man-notify-when-ready)
- (ad-unadvise 'set-visited-file-name)))))
+ (ad-unadvise 'Man-notify-when-ready)
+ (ad-unadvise 'set-visited-file-name))))
;; The end...