aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile4
-rw-r--r--lib/asn1/src/asn1ct_check.erl41
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EnumExt.asn12
-rw-r--r--lib/asn1/test/testEnumExt.erl4
-rw-r--r--lib/common_test/src/ct_util.erl6
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl61
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl138
-rw-r--r--lib/common_test/test/ct_telnet_SUITE.erl29
-rw-r--r--lib/common_test/test/ct_test_support.erl52
-rw-r--r--lib/compiler/doc/src/compile.xml5
-rw-r--r--lib/compiler/src/beam_bsm.erl1
-rw-r--r--lib/compiler/src/beam_disasm.erl6
-rw-r--r--lib/compiler/src/beam_validator.erl5
-rw-r--r--lib/compiler/src/compile.erl7
-rw-r--r--lib/compiler/src/core_lib.erl4
-rw-r--r--lib/compiler/src/sys_core_fold.erl143
-rw-r--r--lib/compiler/src/v3_codegen.erl138
-rw-r--r--lib/compiler/src/v3_core.erl510
-rw-r--r--lib/compiler/src/v3_kernel.erl105
-rw-r--r--lib/compiler/src/v3_kernel.hrl4
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl17
-rw-r--r--lib/compiler/src/v3_life.erl14
-rw-r--r--lib/compiler/test/Makefile2
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl15
-rw-r--r--lib/compiler/test/compile_SUITE.erl96
-rw-r--r--lib/compiler/test/compile_SUITE_data/small.erl48
-rw-r--r--lib/compiler/test/compile_SUITE_data/small_maps.erl16
-rw-r--r--lib/compiler/test/core_SUITE.erl5
-rw-r--r--lib/compiler/test/core_SUITE_data/eval_case.core34
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl36
-rw-r--r--lib/compiler/test/map_SUITE.erl507
-rw-r--r--lib/compiler/test/warnings_SUITE.erl9
-rw-r--r--lib/crypto/c_src/crypto.c87
-rw-r--r--lib/crypto/c_src/crypto_callback.c22
-rw-r--r--lib/crypto/doc/src/crypto.xml8
-rw-r--r--lib/debugger/src/dbg_ieval.erl84
-rw-r--r--lib/debugger/src/dbg_iload.erl70
-rw-r--r--lib/debugger/src/dbg_wx_filedialog_win.erl19
-rw-r--r--lib/debugger/src/dbg_wx_mon.erl4
-rw-r--r--lib/debugger/src/dbg_wx_mon_win.erl4
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl4
-rw-r--r--lib/debugger/src/dbg_wx_view.erl4
-rw-r--r--lib/debugger/src/int.erl5
-rw-r--r--lib/debugger/test/Makefile1
-rw-r--r--lib/debugger/test/erl_eval_SUITE.erl1
-rw-r--r--lib/debugger/test/map_SUITE.erl1002
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml2
-rw-r--r--lib/dialyzer/src/dialyzer.app.src8
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl17
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl6
-rw-r--r--lib/dialyzer/test/Makefile1
-rw-r--r--lib/dialyzer/test/dialyzer_SUITE.erl73
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings5
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl95
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl9
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl23
-rw-r--r--lib/edoc/src/edoc_tags.erl2
-rw-r--r--lib/gs/doc/src/gs.xml4
-rw-r--r--lib/inets/test/httpd_SUITE.erl145
-rw-r--r--lib/jinterface/doc/src/jinterface_users_guide.xml4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java293
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java3
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java20
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/java_files1
-rw-r--r--lib/jinterface/test/jinterface_SUITE.erl18
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Makefile.src3
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Maps.java108
-rw-r--r--lib/kernel/doc/src/application.xml17
-rw-r--r--lib/kernel/doc/src/file.xml4
-rw-r--r--lib/kernel/src/application.erl24
-rw-r--r--lib/kernel/src/application_controller.erl48
-rw-r--r--lib/kernel/test/application_SUITE.erl49
-rw-r--r--lib/mnesia/test/mnesia_frag_test.erl4
-rw-r--r--lib/observer/src/cdv_bin_cb.erl24
-rw-r--r--lib/observer/src/cdv_html_wx.erl26
-rw-r--r--lib/observer/src/cdv_term_cb.erl21
-rw-r--r--lib/observer/src/observer_html_lib.erl77
-rw-r--r--lib/observer/src/observer_lib.erl12
-rw-r--r--lib/observer/src/observer_procinfo.erl8
-rw-r--r--lib/observer/src/observer_wx.erl4
-rw-r--r--lib/orber/src/orber_interceptors.erl11
-rw-r--r--lib/parsetools/src/yecc.erl4
-rw-r--r--lib/public_key/doc/src/public_key.xml2
-rw-r--r--lib/public_key/include/public_key.hrl22
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl4
-rw-r--r--lib/public_key/src/pubkey_pem.erl8
-rw-r--r--lib/public_key/src/pubkey_ssh.erl7
-rw-r--r--lib/public_key/src/public_key.erl23
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl35
-rw-r--r--lib/ssh/doc/src/ssh.xml5
-rw-r--r--lib/ssh/src/ssh.erl6
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl2
-rw-r--r--lib/ssh/src/ssh_file.erl26
-rw-r--r--lib/ssh/src/ssh_message.erl30
-rw-r--r--lib/ssh/src/ssh_sftpd.erl2
-rw-r--r--lib/ssl/src/ssl.erl60
-rw-r--r--lib/ssl/src/ssl_internal.hrl10
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl33
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl19
-rw-r--r--lib/stdlib/doc/src/ets.xml6
-rw-r--r--lib/stdlib/doc/src/lists.xml8
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml44
-rw-r--r--lib/stdlib/src/edlin_expand.erl8
-rw-r--r--lib/stdlib/src/erl_eval.erl30
-rw-r--r--lib/stdlib/src/erl_expand_records.erl14
-rw-r--r--lib/stdlib/src/erl_lint.erl21
-rw-r--r--lib/stdlib/src/erl_pp.erl19
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl2
-rw-r--r--lib/stdlib/src/lists.erl15
-rw-r--r--lib/stdlib/src/maps.erl2
-rw-r--r--lib/stdlib/src/re.erl3
-rw-r--r--lib/stdlib/src/supervisor.erl5
-rw-r--r--lib/stdlib/src/supervisor_bridge.erl11
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl11
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl19
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl21
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl19
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl23
-rw-r--r--lib/stdlib/test/ets_SUITE.erl18
-rw-r--r--lib/stdlib/test/expand_test.erl6
-rw-r--r--lib/stdlib/test/expand_test1.erl4
-rw-r--r--lib/stdlib/test/lists_SUITE.erl12
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl34
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl265
-rw-r--r--lib/test_server/src/configure.in27
-rw-r--r--lib/test_server/src/test_server.erl37
-rw-r--r--lib/test_server/src/test_server_ctrl.erl29
-rw-r--r--lib/test_server/src/test_server_sup.erl65
-rw-r--r--lib/test_server/src/ts.erl92
-rw-r--r--lib/tools/src/cover.erl167
-rw-r--r--lib/tools/test/cover_SUITE.erl89
-rw-r--r--lib/wx/api_gen/Makefile4
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.c_src36
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.erl46
-rw-r--r--lib/wx/api_gen/wx_gen.erl10
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl11
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl28
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp12
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp36
-rw-r--r--lib/wx/c_src/wxe_callback_impl.cpp27
-rw-r--r--lib/wx/c_src/wxe_callback_impl.h18
-rw-r--r--lib/wx/c_src/wxe_events.h31
-rw-r--r--lib/wx/src/gen/wxBufferedDC.erl4
-rw-r--r--lib/wx/src/gen/wxBufferedPaintDC.erl4
-rw-r--r--lib/wx/src/gen/wxClientDC.erl4
-rw-r--r--lib/wx/src/gen/wxEvtHandler.erl48
-rw-r--r--lib/wx/src/gen/wxGridCellBoolEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellChoiceEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellFloatEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellNumberEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellTextEditor.erl4
-rw-r--r--lib/wx/src/gen/wxMemoryDC.erl4
-rw-r--r--lib/wx/src/gen/wxMirrorDC.erl4
-rw-r--r--lib/wx/src/gen/wxPaintDC.erl4
-rw-r--r--lib/wx/src/gen/wxPostScriptDC.erl4
-rw-r--r--lib/wx/src/gen/wxScreenDC.erl4
-rw-r--r--lib/wx/src/gen/wxWindowDC.erl4
-rw-r--r--lib/wx/src/wxe.hrl5
-rw-r--r--lib/wx/src/wxe_server.erl192
-rw-r--r--lib/wx/src/wxe_util.erl33
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl21
-rw-r--r--lib/wx/test/wx_event_SUITE.erl12
164 files changed, 5036 insertions, 1568 deletions
diff --git a/lib/Makefile b/lib/Makefile
index 5128241563..6301c882b2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -25,12 +25,12 @@ ERTS_APPLICATIONS = stdlib sasl kernel compiler
# Then these have to be build
ERLANG_APPLICATIONS = tools test_server common_test runtime_tools \
- inets xmerl edoc erl_docgen
+ inets xmerl edoc erl_docgen parsetools
# These are only build if -a is given to otp_build or make is used directly
ALL_ERLANG_APPLICATIONS = snmp otp_mibs erl_interface asn1 jinterface \
wx debugger reltool gs \
- ic mnesia crypto orber os_mon parsetools syntax_tools \
+ ic mnesia crypto orber os_mon syntax_tools \
public_key ssl observer odbc diameter \
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco webtool \
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index f94f3b56bc..df034c96ad 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -2642,16 +2642,19 @@ normalize_objectdescriptor(Value) ->
normalize_real(Value) ->
Value.
-normalize_enumerated(S, Id, {Base,Ext}) ->
+normalize_enumerated(S, Id0, NNL) ->
+ {Id,_} = lookup_enum_value(S, Id0, NNL),
+ Id.
+
+lookup_enum_value(S, Id, {Base,Ext}) ->
%% Extensible ENUMERATED.
- normalize_enumerated(S, Id, Base++Ext);
-normalize_enumerated(S, #'Externalvaluereference'{value=Id},
- NamedNumberList) ->
- normalize_enumerated(S, Id, NamedNumberList);
-normalize_enumerated(S, Id, NamedNumberList) when is_atom(Id) ->
- case lists:keymember(Id, 1, NamedNumberList) of
- true ->
- Id;
+ lookup_enum_value(S, Id, Base++Ext);
+lookup_enum_value(S, #'Externalvaluereference'{value=Id}, NNL) ->
+ lookup_enum_value(S, Id, NNL);
+lookup_enum_value(S, Id, NNL) when is_atom(Id) ->
+ case lists:keyfind(Id, 1, NNL) of
+ {_,_}=Ret ->
+ Ret;
false ->
throw(asn1_error(S, S#state.value, {undefined,Id}))
end.
@@ -3786,8 +3789,9 @@ resolv_value(S,Val) ->
resolv_value1(S,Id).
resolv_value1(S, ERef = #'Externalvaluereference'{value=Name}) ->
- case catch resolve_namednumber(S,S#state.type,Name) of
- V when is_integer(V) -> V;
+ case catch resolve_namednumber(S, S#state.type, Name) of
+ V when is_integer(V) ->
+ V;
_ ->
case get_referenced_type(S,ERef) of
{Err,_Reason} when Err == error; Err == 'EXIT' ->
@@ -3840,21 +3844,20 @@ resolve_value_from_object(S,Object,FieldName) ->
end.
-
resolve_namednumber(S,#typedef{typespec=Type},Name) ->
case Type#type.def of
{'ENUMERATED',NameList} ->
- NamedNumberList=check_enumerated(S,NameList,Type#type.constraint),
- N = normalize_enumerated(S,Name,NamedNumberList),
- {value,{_,V}} = lists:keysearch(N,1,NamedNumberList),
- V;
+ resolve_namednumber_1(S, Name, NameList, Type);
{'INTEGER',NameList} ->
- NamedNumberList = check_enumerated(S,NameList,Type#type.constraint),
- {value,{_,V}} = lists:keysearch(Name,1,NamedNumberList),
- V;
+ resolve_namednumber_1(S, Name, NameList, Type);
_ ->
not_enumerated
end.
+
+resolve_namednumber_1(S, Name, NameList, Type) ->
+ NamedNumberList = check_enumerated(S, NameList, Type#type.constraint),
+ {_,N} = lookup_enum_value(S, Name, NamedNumberList),
+ N.
check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) ->
{RefMod,CTDef} = get_referenced_type(S,Type#type.def),
diff --git a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
index 8dc5f3d7e1..74fa97e7aa 100644
--- a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
@@ -18,6 +18,8 @@ Ext1 ::= ENUMERATED {
magenta(9)
}
+SubExt1 ::= Ext1 ( blue | orange | black )
+
Noext ::= ENUMERATED {
blue(0),
red(1),
diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl
index c66adaf949..878518be11 100644
--- a/lib/asn1/test/testEnumExt.erl
+++ b/lib/asn1/test/testEnumExt.erl
@@ -59,6 +59,10 @@ main(ber) ->
common(ber).
common(Erule) ->
+ roundtrip('SubExt1', blue),
+ roundtrip('SubExt1', orange),
+ roundtrip('SubExt1', black),
+
roundtrip('Seq', {'Seq',blue,42}),
roundtrip('Seq', {'Seq',red,42}),
roundtrip('Seq', {'Seq',green,42}),
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 1d851f8d2f..ac9857487d 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -382,13 +382,17 @@ loop(Mode,TestData,StartDir) ->
TestData1 =
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
- case Fun(Val) of
+ try Fun(Val) of
'$delete' ->
return(From,deleted),
lists:keydelete(Key,1,TestData);
NewVal ->
return(From,NewVal),
[{Key,NewVal}|lists:keydelete(Key,1,TestData)]
+ catch
+ _:Error ->
+ return(From,{error,Error}),
+ TestData
end;
_ ->
case lists:member(create,Opts) of
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index 28f0494d20..194e7d42ae 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -66,7 +66,7 @@ all() ->
[cfg_error, lib_error, no_compile, timetrap_end_conf,
timetrap_normal, timetrap_extended, timetrap_parallel,
timetrap_fun, timetrap_fun_group, misc_errors,
- config_restored].
+ config_restored, config_func_errors].
groups() ->
[].
@@ -310,6 +310,25 @@ config_restored(Config) when is_list(Config) ->
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
+%%%
+config_func_errors(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "error/test/config_func_error_1_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite}],
+ Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(config_func_errors,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(config_func_errors),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -323,8 +342,6 @@ setup(Test, Config) ->
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
- %reformat(Events, _EH) ->
- % Events.
%%%-----------------------------------------------------------------
%%% TEST EVENTS
@@ -1498,4 +1515,42 @@ test_events(config_restored) ->
{?eh,tc_done,{config_restored_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
+ ];
+
+test_events(config_func_errors) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{config_func_error_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{config_func_error_1_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_iptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_iptc,'_'}},
+ {?eh,test_stats,{0,1,{0,0}}},
+
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_eptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_eptc,'_'}},
+ {?eh,test_stats,{0,2,{0,0}}},
+
+ [{?eh,tc_start,{config_func_error_1_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{init_per_group,g1,[]},ok}},
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_iptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_iptc,'_'}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{config_func_error_1_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{end_per_group,g1,[]},ok}}],
+
+ [{?eh,tc_start,{config_func_error_1_SUITE,{init_per_group,g2,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{init_per_group,g2,[]},ok}},
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_eptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_eptc,'_'}},
+ {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{config_func_error_1_SUITE,{end_per_group,g2,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{end_per_group,g2,[]},ok}}],
+
+ {?eh,tc_start,{config_func_error_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{config_func_error_1_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl
new file mode 100644
index 0000000000..f1025213dc
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl
@@ -0,0 +1,138 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(config_func_error_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,5}}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+bad_proc(Config) ->
+ ct:pal("Bye bye from ~p", [self()]),
+ %% this call will either generate an exit immediately
+ %% or return a fun to be executed here
+ ErrorFun = ct_test_support:random_error(Config),
+ ct:log("Calling error fun now...", []),
+ ErrorFun(),
+ ct:sleep(10000),
+ ok.
+
+init_per_testcase(exit_in_iptc, Config) ->
+ spawn_link(?MODULE, bad_proc, [Config]),
+ ct:sleep(10000),
+ Config;
+init_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(exit_in_eptc, Config) ->
+ spawn_link(?MODULE, bad_proc, [Config]),
+ ct:sleep(10000),
+ ok;
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%%--------------------------------------------------------------------
+groups() ->
+ [{g1, [], [exit_in_iptc]},
+ {g2, [], [exit_in_eptc]}].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [exit_in_iptc,
+ exit_in_eptc,
+ {group, g1},
+ {group, g2}].
+
+exit_in_iptc(_) ->
+ ok.
+
+exit_in_eptc(_) ->
+ ok.
+
diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl
index 25debf09d4..acce4eca14 100644
--- a/lib/common_test/test/ct_telnet_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE.erl
@@ -215,23 +215,18 @@ all_cases(Suite,Config) ->
fun({group,G}) ->
{value,{G,Props,GTCs}} =
lists:keysearch(G,1,Suite:groups()),
- GTCs1 = case lists:member(parallel,Props) of
- true ->
- %%! TEMPORARY WORKAROUND FOR PROBLEM
- %%! WITH ct_test_support NOT HANDLING
- %%! VERIFICATION OF PARALLEL GROUPS
- %%! CORRECTLY!
- [];
- false ->
- [[{?eh,tc_start,{Suite,GTC}},
- {?eh,tc_done,{Suite,GTC,ok}}] ||
- GTC <- GTCs]
- end,
- [{?eh,tc_start,{Suite,{init_per_group,G,Props}}},
- {?eh,tc_done,{Suite,{init_per_group,G,Props},ok}} |
- GTCs1] ++
- [{?eh,tc_start,{Suite,{end_per_group,G,Props}}},
- {?eh,tc_done,{Suite,{end_per_group,G,Props},ok}}];
+ GTCs1 = [[{?eh,tc_start,{Suite,GTC}},
+ {?eh,tc_done,{Suite,GTC,ok}}] ||
+ GTC <- GTCs],
+ GEvs = [{?eh,tc_start,{Suite,{init_per_group,G,Props}}},
+ {?eh,tc_done,{Suite,{init_per_group,G,Props},ok}} |
+ GTCs1] ++
+ [{?eh,tc_start,{Suite,{end_per_group,G,Props}}},
+ {?eh,tc_done,{Suite,{end_per_group,G,Props},ok}}],
+ case lists:member(parallel, Props) of
+ true -> [{parallel,GEvs}];
+ false -> GEvs
+ end;
(TC) ->
[{?eh,tc_done,{Suite,TC,ok}}]
end, GroupsAndTCs),
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 772274ce7e..2e2b45d59f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -40,6 +40,8 @@
-export([ct_test_halt/1, ct_rpc/2]).
+-export([random_error/1]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
@@ -110,7 +112,6 @@ start_slave(NodeName, Config, Level) ->
_ ->
ok
end,
-
TraceFile = filename:join(DataDir, "ct.trace"),
case file:read_file_info(TraceFile) of
{ok,_} ->
@@ -395,6 +396,55 @@ ct_rpc({M,F,A}, Config) ->
%%%-----------------------------------------------------------------
+%%% random_error/1
+random_error(Config) when is_list(Config) ->
+ random:seed(now()),
+ Gen = fun(0,_) -> ok; (N,Fun) -> Fun(N-1, Fun) end,
+ Gen(random:uniform(100), Gen),
+
+ ErrorTypes = ['BADMATCH','BADARG','CASE_CLAUSE','FUNCTION_CLAUSE',
+ 'EXIT','THROW','UNDEF'],
+ Type = lists:nth(random:uniform(length(ErrorTypes)), ErrorTypes),
+ Where = case random:uniform(2) of
+ 1 ->
+ io:format("ct_test_support *returning* error of type ~w",
+ [Type]),
+ tc;
+ 2 ->
+ io:format("ct_test_support *generating* error of type ~w",
+ [Type]),
+ lib
+ end,
+ ErrorFun =
+ fun() ->
+ case Type of
+ 'BADMATCH' ->
+ ok = proplists:get_value(undefined, Config);
+ 'BADARG' ->
+ size(proplists:get_value(priv_dir, Config));
+ 'FUNCTION_CLAUSE' ->
+ random_error(x);
+ 'EXIT' ->
+ spawn_link(fun() ->
+ undef_proc ! hello,
+ ok
+ end);
+ 'THROW' ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ if is_list(PrivDir) -> throw(generated_throw) end;
+ 'UNDEF' ->
+ apply(?MODULE, random_error, [])
+ end
+ end,
+ %% either call the fun here or return it to the caller (to be
+ %% executed in a test case instead)
+ case Where of
+ tc -> ErrorFun;
+ lib -> ErrorFun()
+ end.
+
+
+%%%-----------------------------------------------------------------
%%% EVENT HANDLING
handle_event(EH, Event) ->
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 1459f696a0..c66c8ea4bf 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -408,6 +408,11 @@ module.beam: module.erl \
<code>-compile({no_auto_import,[error/1]}).</code>
</item>
+ <tag><c>no_auto_import</c></tag>
+ <item>
+ <p>Do not auto import any functions from the module <c>erlang</c>.</p>
+ </item>
+
<tag><c>no_line_info</c></tag>
<item>
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index fdfcb08125..d54c2a9fde 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -209,6 +209,7 @@ btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->
btb_call(Arity+2, apply, Regs, Is, D);
btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) ->
+ btb_ensure_not_used([{x,Live}], I, Regs),
btb_call(Live, I, Regs, Is, D);
btb_reaches_match_2([{make_fun2,_,_,_,Live}|Is], Regs, D) ->
btb_call(Live, make_fun2, Regs, Is, D);
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index e0d0d0fd1d..57fdf95677 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1134,7 +1134,7 @@ resolve_inst({line,[Index]},_,_,_) ->
{line,resolve_arg(Index)};
%%
-%% R17A.
+%% 17.0
%%
resolve_inst({put_map_assoc,Args},_,_,_) ->
[FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
@@ -1150,6 +1150,10 @@ resolve_inst({is_map,Args0},_,_,_) ->
[FLbl|Args] = resolve_args(Args0),
{test, is_map, FLbl, Args};
+resolve_inst({has_map_field,Args0},_,_,_) ->
+ [FLbl|Args] = resolve_args(Args0),
+ {test,has_map_field,FLbl,Args};
+
resolve_inst({get_map_element,Args},_,_,_) ->
[FLbl,Src,Key,Dst] = resolve_args(Args),
{get_map_element,FLbl,Src,Key,Dst};
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 97f84da08f..2486d486ed 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -574,6 +574,7 @@ valfun_4({apply,Live}, Vst) ->
valfun_4({apply_last,Live,_}, Vst) ->
tail_call(apply, Live+2, Vst);
valfun_4({call_fun,Live}, Vst) ->
+ validate_src([{x,Live}], Vst),
call('fun', Live+1, Vst);
valfun_4({call,Live,Func}, Vst) ->
call(Func, Live, Vst);
@@ -881,7 +882,7 @@ valfun_4(_, _) ->
verify_put_map(Fail, Src, Dst, Live, List, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
- [assert_term(Term, Vst0) || Term <- List],
+ foreach(fun (Term) -> assert_term(Term, Vst0) end, List),
assert_term(Src, Vst0),
Vst1 = heap_alloc(0, Vst0),
Vst2 = branch_state(Fail, Vst1),
@@ -908,7 +909,7 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->
branch_state(Fail, Vst).
%%
-%% Special state handling for setelement/3 and the set_tuple_element/3 instruction.
+%% Special state handling for setelement/3 and set_tuple_element/3 instructions.
%% A possibility for garbage collection must not occur between setelement/3 and
%% set_tuple_element/3.
%%
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 0bb4de6f17..e79fe41f9b 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -623,9 +623,11 @@ core_passes() ->
[{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},
{iff,doldinline,{listing,"oldinline"}},
?pass(core_fold_module),
+ {iff,dcorefold,{listing,"corefold"}},
{core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},
{iff,dinline,{listing,"inline"}},
- {core_fold_after_inlining,fun test_core_inliner/1,fun core_fold_module_after_inlining/1},
+ {core_fold_after_inlining,fun test_any_inliner/1,
+ fun core_fold_module_after_inlining/1},
?pass(core_transforms)]},
{iff,dcopt,{listing,"copt"}},
{iff,'to_core',{done,"core"}}]}
@@ -1171,6 +1173,9 @@ test_core_inliner(#compile{options=Opts}) ->
end, Opts)
end.
+test_any_inliner(St) ->
+ test_old_inliner(St) orelse test_core_inliner(St).
+
core_old_inliner(#compile{code=Code0,options=Opts}=St) ->
{ok,Code} = sys_core_inline:module(Code0, Opts),
{ok,St#compile{code=Code}}.
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index f506901099..ed181e3baa 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -105,8 +105,8 @@ vu_expr(V, #c_cons{hd=H,tl=T}) ->
vu_expr(V, H) orelse vu_expr(V, T);
vu_expr(V, #c_tuple{es=Es}) ->
vu_expr_list(V, Es);
-vu_expr(V, #c_map{es=Es}) ->
- vu_expr_list(V, Es);
+vu_expr(V, #c_map{var=M,es=Es}) ->
+ vu_expr(V, M) orelse vu_expr_list(V, Es);
vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
vu_expr_list(V, [Key,Val]);
vu_expr(V, #c_binary{segments=Ss}) ->
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 1cdbac5693..e2b9213891 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -305,6 +305,10 @@ expr(#c_let{}=Let, Ctxt, Sub) ->
%% Now recursively re-process the new expression.
expr(Expr, Ctxt, sub_new_preserve_types(Sub))
end;
+expr(#c_letrec{body=#c_var{}}=Letrec, effect, _Sub) ->
+ %% This is named fun in an 'effect' context. Warn and ignore.
+ add_warning(Letrec, useless_building),
+ void();
expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) ->
Fs1 = map(fun ({Name,Fb}) ->
{Name,expr(Fb, {letrec,Ctxt}, Sub)}
@@ -349,7 +353,12 @@ expr(#c_case{}=Case0, Ctxt, Sub) ->
Case = Case1#c_case{arg=Arg2,clauses=Cs2},
warn_no_clause_match(Case1, Case),
Expr = eval_case(Case, Sub),
- bsm_an(Expr);
+ case move_case_into_arg(Case, Sub) of
+ impossible ->
+ bsm_an(Expr);
+ Other ->
+ expr(Other, Ctxt, sub_new_preserve_types(Sub))
+ end;
Other ->
expr(Other, Ctxt, Sub)
end;
@@ -598,6 +607,14 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz},
error:_ ->
throw(impossible)
end;
+eval_binary_1([#c_bitstr{val=#c_literal{},size=#c_literal{},
+ unit=#c_literal{},type=#c_literal{},
+ flags=#c_cons{}=Flags}=Bitstr|Ss], Acc0) ->
+ case cerl:fold_literal(Flags) of
+ #c_literal{} = Flags1 ->
+ eval_binary_1([Bitstr#c_bitstr{flags=Flags1}|Ss], Acc0);
+ _ -> throw(impossible)
+ end;
eval_binary_1([], Acc) -> Acc;
eval_binary_1(_, _) -> throw(impossible).
@@ -1536,9 +1553,17 @@ map_pair_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0),
{Ps,Osub}.
-map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair, {Isub,Osub0}) ->
- {K,Osub1} = pattern(K0, Isub, Osub0),
- {V,Osub} = pattern(V0, Isub, Osub1),
+map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,Osub0}) ->
+ {K,Osub1} = case cerl:type(K0) of
+ binary ->
+ K1 = eval_binary(K0),
+ case cerl:type(K1) of
+ literal -> {K1,Osub0};
+ _ -> pattern(K0,Isub,Osub0)
+ end;
+ _ -> pattern(K0,Isub,Osub0)
+ end,
+ {V,Osub} = pattern(V0,Isub,Osub1),
{Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
bin_pattern_list(Ps0, Isub, Osub0) ->
@@ -1920,14 +1945,45 @@ opt_bool_case_guard(Arg, [#c_clause{pats=[#c_literal{val=false}]}=Fc,Tc]) ->
%% last clause is guaranteed to match so if there is only one clause
%% with a pattern containing only variables then rewrite to a let.
-eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,body=B}]}, Sub) ->
+eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,
+ guard=#c_literal{val=true},
+ body=B}]}=Case, Sub) ->
Es = case cerl:is_c_values(E) of
true -> cerl:values_es(E);
false -> [E]
end,
- {true,Bs} = cerl_clauses:match_list(Ps0, Es),
- {Ps,As} = unzip(Bs),
- expr(#c_let{vars=Ps,arg=core_lib:make_values(As),body=B}, sub_new(Sub));
+ %% Consider:
+ %%
+ %% case SomeSideEffect() of
+ %% X=Y -> ...
+ %% end
+ %%
+ %% We must not rewrite it to:
+ %%
+ %% let <X,Y> = <SomeSideEffect(),SomeSideEffect()> in ...
+ %%
+ %% because SomeSideEffect() would be evaluated twice.
+ %%
+ %% Instead we must evaluate the case expression in an outer let
+ %% like this:
+ %%
+ %% let NewVar = SomeSideEffect() in
+ %% let <X,Y> = <NewVar,NewVar> in ...
+ %%
+ Vs = make_vars([], length(Es)),
+ case cerl_clauses:match_list(Ps0, Vs) of
+ {false,_} ->
+ %% This can only happen if the Core Erlang code is
+ %% handwritten or generated by another code generator
+ %% than v3_core. Assuming that the Core Erlang program
+ %% is correct, the clause will always match at run-time.
+ Case;
+ {true,Bs} ->
+ {Ps,As} = unzip(Bs),
+ InnerLet = cerl:c_let(Ps, core_lib:make_values(As), B),
+ Let = cerl:c_let(Vs, E, InnerLet),
+ expr(Let, sub_new(Sub))
+ end;
eval_case(Case, _) -> Case.
%% case_opt(CaseArg, [Clause]) -> {CaseArg,[Clause]}.
@@ -2555,6 +2611,77 @@ opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->
value, Sub)
end.
+move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg,
+ body=InnerArg0}=Outer,
+ clauses=InnerClauses}=Inner, Sub) ->
+ %%
+ %% case let <OuterVars> = <OuterArg> in <InnerArg> of
+ %% <InnerClauses>
+ %% end
+ %%
+ %% ==>
+ %%
+ %% let <OuterVars> = <OuterArg>
+ %% in case <InnerArg> of <InnerClauses> end
+ %%
+ ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}),
+ {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0),
+ InnerArg = body(InnerArg0, ScopeSub),
+ Outer#c_let{vars=OuterVars,arg=OuterArg,
+ body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}};
+move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg,
+ clauses=[OuterCa0,OuterCb]}=Outer,
+ clauses=InnerClauses}=Inner0, Sub) ->
+ case is_failing_clause(OuterCb) of
+ true ->
+ #c_clause{pats=OuterPats0,guard=OuterGuard0,
+ body=InnerArg0} = OuterCa0,
+ %%
+ %% case case <OuterArg> of
+ %% <OuterPats> when <OuterGuard> -> <InnerArg>
+ %% <OuterCb>
+ %% ...
+ %% end of
+ %% <InnerClauses>
+ %% end
+ %%
+ %% ==>
+ %%
+ %% case <OuterArg> of
+ %% <OuterPats> when <OuterGuard> ->
+ %% case <InnerArg> of <InnerClauses> end
+ %% <OuterCb>
+ %% end
+ %%
+ ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}),
+ {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0),
+ OuterGuard = guard(OuterGuard0, ScopeSub),
+ InnerArg = body(InnerArg0, ScopeSub),
+ Inner = Inner0#c_case{arg=InnerArg,clauses=InnerClauses},
+ OuterCa = OuterCa0#c_clause{pats=OuterPats,guard=OuterGuard,
+ body=Inner},
+ Outer#c_case{arg=OuterArg,
+ clauses=[OuterCa,OuterCb]};
+ false ->
+ impossible
+ end;
+move_case_into_arg(#c_case{arg=#c_seq{arg=OuterArg,body=InnerArg}=Outer,
+ clauses=InnerClauses}=Inner, _Sub) ->
+ %%
+ %% case do <OuterArg> <InnerArg> of
+ %% <InnerClauses>
+ %% end
+ %%
+ %% ==>
+ %%
+ %% do <OuterArg>
+ %% case <InnerArg> of <InerClauses> end
+ %%
+ Outer#c_seq{arg=OuterArg,
+ body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}};
+move_case_into_arg(_, _) ->
+ impossible.
+
%% In guards only, rewrite a case in a let argument like
%%
%% let <Var> = case <> of
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index c8735a76e8..4d155c0fd0 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -459,7 +459,7 @@ basic_block([Le|Les], Acc) ->
%% sets that may garbage collect are not allowed in basic blocks.
collect_block({set,_,{binary,_}}) -> no_block;
-collect_block({set,_,{map,_,_}}) -> no_block;
+collect_block({set,_,{map,_,_,_}}) -> no_block;
collect_block({set,_,_}) -> include;
collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};
collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)};
@@ -928,7 +928,7 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->
select_map(Scs, V, Tf, Vf, Bef, St0) ->
Reg = fetch_var(V, Bef),
{Is,Aft,St1} =
- match_fmf(fun(#l{ke={val_clause,{map,Es},B},i=I,vdb=Vdb}, Fail, St1) ->
+ match_fmf(fun(#l{ke={val_clause,{map,_,Es},B},i=I,vdb=Vdb}, Fail, St1) ->
select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1)
end, Vf, St0, Scs),
{[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}.
@@ -1488,55 +1488,35 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
%% Now generate the complete code for constructing the binary.
Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
{Sis++Code,Aft,St};
-set_cg([{var,R}], {map,SrcMap,Es0}, Le, Vdb, Bef,
+set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef,
#cg{in_catch=InCatch,bfail=Bfail}=St) ->
+
Fail = {f,Bfail},
{Sis,Int0} =
case InCatch of
true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
false -> {[],Bef}
end,
+ SrcReg = cg_reg_arg(Map,Int0),
Line = line(Le#l.a),
- SrcReg = case SrcMap of
- {var,SrcVar} -> fetch_var(SrcVar, Int0);
- _ -> SrcMap
- end,
- {Assoc,Exact} =
- try
- cg_map_pairs(Es0)
- catch
- throw:badarg ->
- {[],[{{float,0.0},{atom,badarg}},
- {{integer,0},{atom,badarg}}]}
- end,
- F = fun ({K,{var,V}}) -> [K,fetch_var(V, Int0)];
- ({K,E}) -> [K,E]
- end,
- AssocList = flatmap(F, Assoc),
- ExactList = flatmap(F, Exact),
- Live0 = max_reg(Bef#sr.reg),
- Int1 = clear_dead(Int0, Le#l.i, Vdb),
- Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)},
- Target = fetch_reg(R, Aft#sr.reg),
- Code = [Line] ++
- case {AssocList,ExactList} of
- {[_|_],[]} ->
- [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}];
- {[_|_],[_|_]} ->
- Live = case Target of
- {x,TargetX} when TargetX =:= Live0 ->
- Live0 + 1;
- _ ->
- Live0
- end,
- [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}},
- {put_map_exact,Fail,Target,Target,Live,{list,ExactList}}];
- {[],[_|_]} ->
- [{put_map_exact,Fail,SrcReg,Target,Live0,{list,ExactList}}];
- {[],[]} ->
- [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,[]}}]
- end,
- {Sis++Code,Aft,St};
+
+ %% The instruction needs to store keys in term sorted order
+ %% All keys has to be unique here
+ Pairs = map_pair_strip_and_termsort(Es),
+
+ %% fetch registers for values to be put into the map
+ List = flatmap(fun({K,V}) -> [K,cg_reg_arg(V,Int0)] end, Pairs),
+
+ Live = max_reg(Bef#sr.reg),
+ Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
+ Aft = clear_dead(Int1, Le#l.i, Vdb),
+ Target = fetch_reg(R, Int1#sr.reg),
+
+ I = case Op of
+ assoc -> put_map_assoc;
+ exact -> put_map_exact
+ end,
+ {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],Aft,St};
set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
%% Find a place for the return register first.
Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
@@ -1549,70 +1529,12 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
end,
{Ais,clear_dead(Int, Le#l.i, Vdb),St}.
-%% cg_map_pairs(MapPairs) -> {Assoc,Exact}
-%% Assoc = Exact = [{K,V}]
-%%
-%% Remove multiple assignments to the same key, and return
-%% one list key-value list with all keys that may or may not exist
-%% (Assoc), and one with keys that must exist (Exact).
-%%
-
-cg_map_pairs(Es0) ->
- Es = cg_map_pairs_1(Es0, 0),
- R0 = sofs:relation(Es),
- R1 = sofs:relation_to_family(R0),
- R2 = sofs:to_external(R1),
-
- %% R2 is now [{KeyValue,[{Order,Op,OriginalKey,Value}]}]
- R3 = [begin
- %% The value for the last pair determines the value.
- {_,_,_,V} = lists:last(Vs),
- {Op,{_,SortOrder}=K} = map_pair_op_and_key(Vs),
- {Op,{SortOrder,K,V}}
- end || {_,Vs} <- R2],
-
- %% R3 is now [{Op,{Key,Value}}]
- R = termsort(R3),
-
- %% R4 is now sorted with all alloc first in the list, followed by
- %% all exact.
- {Assoc,Exact} = lists:partition(fun({Op,_}) -> Op =:= assoc end, R),
- {[{K,V} || {_,{_,K,V}} <- Assoc],
- [{K,V} || {_,{_,K,V}} <- Exact]}.
-
-cg_map_pairs_1([{map_pair_assoc,{_,Kv}=K,V}|T], Order) ->
- [{Kv,{Order,assoc,K,V}}|cg_map_pairs_1(T, Order+1)];
-cg_map_pairs_1([{map_pair_exact,{_,Kv}=K,V}|T], Order) ->
- [{Kv,{Order,exact,K,V}}|cg_map_pairs_1(T, Order+1)];
-cg_map_pairs_1([], _) -> [].
-
-%% map_pair_op_and_key({_,Op,K,_}) -> {Operator,Key}
-%% Determine the operator and key to use. Throw a 'badarg'
-%% exception if there are contradictory exact updates.
-
-map_pair_op_and_key(L) ->
- case [K || {_,exact,K,_} <- L] of
- [K] ->
- %% There is a single ':=' operator. Use that key.
- {exact,K};
- [K|T] ->
- %% There is more than one ':=' operator. All of them
- %% must have the same key.
- case lists:all(fun(E) -> E =:= K end, T) of
- true ->
- {exact,K};
- false ->
- %% Some keys are different, e.g. 1 and 1.0.
- throw(badarg)
- end;
- [] ->
- %% Only '=>' operators. Use the first key in the list.
- [{_,assoc,K,_}|_] = L,
- {assoc,K}
- end.
-
-termsort(Ls) ->
- lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Ls).
+map_pair_strip_and_termsort(Es) ->
+ %% format in
+ %% [{map_pair,K,V}]
+ %% where K is for example {integer, 1} and we want to sort on 1.
+ Ls = [{K,V}||{_,K,V}<-Es],
+ lists:sort(fun({{_,A},_},{{_,B},_}) -> erts_internal:cmp_term(A,B) < 0 end, Ls).
%%%
%%% Code generation for constructing binaries.
@@ -2085,7 +2007,7 @@ load_vars(Vs, Regs) ->
foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).
%% put_reg(Val, Regs) -> Regs.
-%% find_reg(Val, Regs) -> ok{r{R}} | error.
+%% find_reg(Val, Regs) -> {ok,r{R}} | error.
%% fetch_reg(Val, Regs) -> r{R}.
%% Functions to interface the registers.
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index e30bfa729c..3435a46ca9 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -74,7 +74,7 @@
-export([module/2,format_error/1]).
-import(lists, [reverse/1,reverse/2,map/2,member/2,foldl/3,foldr/3,mapfoldl/3,
- splitwith/2,keyfind/3,sort/1,foreach/2]).
+ splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]).
-import(ordsets, [add_element/2,del_element/2,is_element/2,
union/1,union/2,intersection/2,subtract/2]).
-import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1]).
@@ -101,6 +101,8 @@
-record(ireceive2, {anno=#a{},clauses,timeout,action}).
-record(iset, {anno=#a{},var,arg}).
-record(itry, {anno=#a{},args,vars,body,evars,handler}).
+-record(ifilter, {anno=#a{},arg}).
+-record(igen, {anno=#a{},acc_pat,acc_guard,skip_pat,tail,tail_pat,arg}).
-type iapply() :: #iapply{}.
-type ibinary() :: #ibinary{}.
@@ -117,10 +119,13 @@
-type ireceive2() :: #ireceive2{}.
-type iset() :: #iset{}.
-type itry() :: #itry{}.
+-type ifilter() :: #ifilter{}.
+-type igen() :: #igen{}.
-type i() :: iapply() | ibinary() | icall() | icase() | icatch()
| iclause() | ifun() | iletrec() | imatch() | iprimop()
- | iprotect() | ireceive1() | ireceive2() | iset() | itry().
+ | iprotect() | ireceive1() | ireceive2() | iset() | itry()
+ | ifilter() | igen().
-type warning() :: {file:filename(), [{integer(), module(), term()}]}.
@@ -226,13 +231,13 @@ guard(Gs0, St0) ->
Gt1 = guard_tests(Gt0),
L = element(2, Gt1),
{op,L,'or',Gt1,Rhs}
- end, guard_tests(last(Gs0)), first(Gs0)),
+ end, guard_tests(last(Gs0)), droplast(Gs0)),
{Gs,St} = gexpr_top(Gs1, St0#core{in_guard=true}),
{Gs,St#core{in_guard=false}}.
guard_tests(Gs) ->
L = element(2, hd(Gs)),
- {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}.
+ {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), droplast(Gs))}.
%% gexpr_top(Expr, State) -> {Cexpr,State}.
%% Generate an internal core expression of a guard test. Explicitly
@@ -479,8 +484,9 @@ expr({cons,L,H0,T0}, St0) ->
{T1,Tps,St2} = safe(T0, St1),
A = lineno_anno(L, St2),
{ann_c_cons(A, H1, T1),Hps ++ Tps,St2};
-expr({lc,L,E,Qs}, St) ->
- lc_tq(L, E, Qs, #c_literal{anno=lineno_anno(L, St),val=[]}, St);
+expr({lc,L,E,Qs0}, St0) ->
+ {Qs1,St1} = preprocess_quals(L, Qs0, St0),
+ lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
expr({bc,L,E,Qs}, St) ->
bc_tq(L, E, Qs, {nil,L}, St);
expr({tuple,L,Es0}, St0) ->
@@ -513,7 +519,7 @@ expr({bin,L,Es0}, St0) ->
end;
expr({block,_,Es0}, St0) ->
%% Inline the block directly.
- {Es1,St1} = exprs(first(Es0), St0),
+ {Es1,St1} = exprs(droplast(Es0), St0),
{E1,Eps,St2} = expr(last(Es0), St1),
{E1,Es1 ++ Eps,St2};
expr({'if',L,Cs0}, St0) ->
@@ -647,7 +653,7 @@ expr({match,L,P0,E0}, St0) ->
Other when not is_atom(Other) ->
{#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps,St4}
end;
-expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) ->
+expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->
%% Optimise '++' here because of the list comprehension algorithm.
%%
%% To avoid achieving quadratic complexity if there is a chain of
@@ -655,7 +661,8 @@ expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) ->
%% evaluation of More now. Evaluating More here could also reduce the
%% number variables in the environment for letrec.
{Mc,Mps,St1} = safe(More, St0),
- {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St1),
+ {Qs,St2} = preprocess_quals(Llc, Qs0, St1),
+ {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2),
{Y,Mps++Yps,St};
expr({op,L,'andalso',E1,E2}, St0) ->
{#c_var{name=V0},St} = new_var(L, St0),
@@ -889,133 +896,45 @@ fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) ->
%% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
%% This TQ from Simon PJ pp 127-138.
-%% This gets a bit messy as we must transform all directly here. We
-%% recognise guard tests and try to fold them together and join to a
-%% preceding generators, this should give us better and more compact
-%% code.
-lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+lc_tq(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard,
+ skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
+ arg={Pre,Arg}}|Qs], Mc, St0) ->
{Name,St1} = new_fun_name("lc", St0),
- {Head,St2} = new_var(St1),
- {Tname,St3} = new_var_name(St2),
- LA = lineno_anno(Line, St3),
- LAnno = #a{anno=LA},
- Tail = #c_var{anno=LA,name=Tname},
- {Arg,St4} = new_var(St3),
- {Nc,[],St5} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St4),
- {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat!
- {Lc,Lps,St7} = lc_tq(Line, E, Qs1, Nc, St6),
- {Pc,St8} = list_gen_pattern(P, Line, St7),
- {Gc,Gps,St9} = safe(G, St8), %Will be a function argument!
- Fc = function_clause([Arg], LA, {Name,1}),
-
- %% Avoid constructing a default clause if the list comprehension
- %% only has a variable as generator and there are no guard
- %% tests. In other words, if the comprehension is equivalent to
- %% lists:map/2.
- Cs0 = case {Guardc, Pc} of
- {[], #c_var{}} ->
- [#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]}],guard=[],
- body=[Mc]}];
- _ ->
- [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[ann_c_cons(LA, Head, Tail)],
- guard=[],
- body=[Nc]},
- #iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]}],guard=[],
- body=[Mc]}]
- end,
- Cs = case Pc of
- nomatch -> Cs0;
- _ ->
- [#iclause{anno=LAnno,
- pats=[ann_c_cons(LA, Pc, Tail)],
- guard=Guardc,
- body=Lps ++ [Lc]}|Cs0]
- end,
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,1}},
- args=[Gc]}]},
- [],St9};
-lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Name,St1} = new_fun_name("blc", St0),
LA = lineno_anno(Line, St1),
LAnno = #a{anno=LA},
- HeadBinPattern = pattern(P, St1),
- #c_binary{segments=Ps0} = HeadBinPattern,
- {Ps,Tail,St2} = append_tail_segment(Ps0, St1),
- {EPs,St3} = emasculate_segments(Ps, St2),
- Pattern = HeadBinPattern#c_binary{segments=Ps},
- EPattern = HeadBinPattern#c_binary{segments=EPs},
- {Arg,St4} = new_var(St3),
- {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
- Tname = Tail#c_var.name,
- {Nc,[],St6} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St5),
- {Bc,Bps,St7} = lc_tq(Line, E, Qs1, Nc, St6),
- {Gc,Gps,St10} = safe(G, St7), %Will be a function argument!
- Fc = function_clause([Arg], LA, {Name,1}),
- {TailSegList,_,St} = append_tail_segment([], St10),
- Cs = [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[Pattern],
- guard=Guardc,
- body=Bps ++ [Bc]},
- #iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[EPattern],
- guard=[],
- body=[#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,1}},
- args=[Tail]}]},
- #iclause{anno=LAnno,
- pats=[#c_binary{anno=LA,segments=TailSegList}],guard=[],
- body=[Mc]}],
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,1}},
- args=[Gc]}]},
- [],St};
-lc_tq(Line, E, [Fil0|Qs0], Mc, St0) ->
- %% Special case sequences guard tests.
- LA = lineno_anno(element(2, Fil0), St0),
- LAnno = #a{anno=LA},
- case is_guard_test(Fil0) of
- true ->
- {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Lc,Lps,St1} = lc_tq(Line, E, Qs1, Mc, St0),
- {Gs,St2} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat!
- {#icase{anno=LAnno,
- args=[],
- clauses=[#iclause{anno=LAnno,pats=[],
- guard=Gs,body=Lps ++ [Lc]}],
- fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[],guard=[],body=[Mc]}},
- [],St2};
- false ->
- {Lc,Lps,St1} = lc_tq(Line, E, Qs0, Mc, St0),
- {Fpat,St2} = new_var(St1),
- Fc = fail_clause([Fpat], LA,
- c_tuple([#c_literal{val=case_clause},Fpat])),
- %% Do a novars little optimisation here.
- {Filc,Fps,St3} = novars(Fil0, St2),
- {#icase{anno=LAnno,
- args=[Filc],
- clauses=[#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=true}],
- guard=[],
- body=Lps ++ [Lc]},
- #iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[#c_literal{anno=LA,val=false}],
- guard=[],
- body=[Mc]}],
- fc=Fc},
- Fps,St3}
- end;
+ F = #c_var{anno=LA,name={Name,1}},
+ Nc = #iapply{anno=GAnno,op=F,args=[Tail]},
+ {Var,St2} = new_var(St1),
+ Fc = function_clause([Var], LA, {Name,1}),
+ TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]},
+ Cs0 = case {AccPat,AccGuard} of
+ {SkipPat,[]} ->
+ %% Skip and accumulator patterns are the same and there is
+ %% no guard, no need to generate a skip clause.
+ [TailClause];
+ _ ->
+ [#iclause{anno=#a{anno=[compiler_generated|LA]},
+ pats=[SkipPat],guard=[],body=[Nc]},
+ TailClause]
+ end,
+ {Cs,St4} = case AccPat of
+ nomatch ->
+ %% The accumulator pattern never matches, no need
+ %% for an accumulator clause.
+ {Cs0,St2};
+ _ ->
+ {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2),
+ {[#iclause{anno=LAnno,pats=[AccPat],guard=AccGuard,
+ body=Lps ++ [Lc]}|Cs0],
+ St3}
+ end,
+ Fun = #ifun{anno=LAnno,id=[],vars=[Var],clauses=Cs,fc=Fc},
+ {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,1},Fun}],
+ body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg]}]},
+ [],St4};
+lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
+ filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5);
lc_tq(Line, E0, [], Mc0, St0) ->
{H1,Hps,St1} = safe(E0, St0),
{T1,Tps,St} = force_safe(Mc0, St1),
@@ -1025,143 +944,60 @@ lc_tq(Line, E0, [], Mc0, St0) ->
%% bc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.
%% This TQ from Gustafsson ERLANG'05.
-%% This gets a bit messy as we must transform all directly here. We
-%% recognise guard tests and try to fold them together and join to a
-%% preceding generators, this should give us better and more compact
-%% code.
%% More could be transformed before calling bc_tq.
-bc_tq(Line, Exp, Qualifiers, _, St0) ->
+bc_tq(Line, Exp, Qs0, _, St0) ->
{BinVar,St1} = new_var(St0),
- {Sz,SzPre,St2} = bc_initial_size(Exp, Qualifiers, St1),
- {E,BcPre,St} = bc_tq1(Line, Exp, Qualifiers, BinVar, St2),
+ {Sz,SzPre,St2} = bc_initial_size(Exp, Qs0, St1),
+ {Qs,St3} = preprocess_quals(Line, Qs0, St2),
+ {E,BcPre,St} = bc_tq1(Line, Exp, Qs, BinVar, St3),
Pre = SzPre ++
[#iset{var=BinVar,
arg=#iprimop{name=#c_literal{val=bs_init_writable},
args=[Sz]}}] ++ BcPre,
{E,Pre,St}.
-bc_tq1(Line, E, [{generate,Lg,P,G}|Qs0], AccExpr, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Name,St1} = new_fun_name("lbc", St0),
- LA = lineno_anno(Line, St1),
- {[Head,Tail,AccVar],St2} = new_vars(LA, 3, St1),
- LAnno = #a{anno=LA},
- {Arg,St3} = new_var(St2),
- NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name},
- {var,Lg,AccVar#c_var.name}]},
- {Guardc,St4} = lc_guard_tests(Gs, St3), %These are always flat!
- {Lc,Lps,St5} = bc_tq1(Line, E, Qs1, AccVar, St4),
- {Nc,Nps,St6} = expr(NewMore, St5),
- {Pc,St7} = list_gen_pattern(P, Line, St6),
- {Gc,Gps,St8} = safe(G, St7), %Will be a function argument!
- Fc = function_clause([Arg,AccVar], LA, {Name,2}),
- Cs0 = case {Guardc, Pc} of
- {[], #c_var{}} ->
- [#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[],
- body=[AccVar]}];
- _ ->
- [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[ann_c_cons(LA, Head, Tail),AccVar],
- guard=[],
- body=Nps ++ [Nc]},
- #iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[],
- body=[AccVar]}]
- end,
- Cs = case Pc of
- nomatch -> Cs0;
- _ ->
- Body = Lps ++ Nps ++ [#iset{var=AccVar,arg=Lc},Nc],
- [#iclause{anno=LAnno,
- pats=[ann_c_cons(LA,Pc,Tail),AccVar],
- guard=Guardc,
- body=Body}|Cs0]
- end,
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,2}},
- args=[Gc,AccExpr]}]},
- [],St8};
-bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+bc_tq1(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard,
+ skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
+ arg={Pre,Arg}}|Qs], Mc, St0) ->
{Name,St1} = new_fun_name("lbc", St0),
LA = lineno_anno(Line, St1),
- {AccVar,St2} = new_var(LA, St1),
- LAnno = #a{anno=LA},
- HeadBinPattern = pattern(P, St2),
- #c_binary{segments=Ps0} = HeadBinPattern,
- {Ps,Tail,St3} = append_tail_segment(Ps0, St2),
- {EPs,St4} = emasculate_segments(Ps, St3),
- Pattern = HeadBinPattern#c_binary{segments=Ps},
- EPattern = HeadBinPattern#c_binary{segments=EPs},
- {Arg,St5} = new_var(St4),
- NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name},
- {var,Lg,AccVar#c_var.name}]},
- {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat!
- {Bc,Bps,St7} = bc_tq1(Line, E, Qs1, AccVar, St6),
- {Nc,Nps,St8} = expr(NewMore, St7),
- {Gc,Gps,St9} = safe(G, St8), %Will be a function argument!
- Fc = function_clause([Arg,AccVar], LA, {Name,2}),
- Body = Bps ++ Nps ++ [#iset{var=AccVar,arg=Bc},Nc],
- {TailSegList,_,St} = append_tail_segment([], St9),
- Cs = [#iclause{anno=LAnno,
- pats=[Pattern,AccVar],
- guard=Guardc,
- body=Body},
- #iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[EPattern,AccVar],
- guard=[],
- body=Nps ++ [Nc]},
- #iclause{anno=LAnno,
- pats=[#c_binary{anno=LA,segments=TailSegList},AccVar],
- guard=[],
- body=[AccVar]}],
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,2}},
- args=[Gc,AccExpr]}]},
- [],St};
-bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) ->
- %% Special case sequences guard tests.
- LA = lineno_anno(element(2, Fil0), St0),
LAnno = #a{anno=LA},
- case is_guard_test(Fil0) of
- true ->
- {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Bc,Bps,St1} = bc_tq1(Line, E, Qs1, AccVar, St0),
- {Gs,St} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat!
- {#icase{anno=LAnno,
- args=[],
- clauses=[#iclause{anno=LAnno,
- pats=[],
- guard=Gs,body=Bps ++ [Bc]}],
- fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[],guard=[],body=[AccVar]}},
- [],St};
- false ->
- {Bc,Bps,St1} = bc_tq1(Line, E, Qs0, AccVar, St0),
- {Fpat,St2} = new_var(St1),
- Fc = fail_clause([Fpat], LA,
- c_tuple([#c_literal{val=case_clause},Fpat])),
- %% Do a novars little optimisation here.
- {Filc,Fps,St} = novars(Fil0, St2),
- {#icase{anno=LAnno,
- args=[Filc],
- clauses=[#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=true}],
- guard=[],
- body=Bps ++ [Bc]},
- #iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[#c_literal{anno=LA,val=false}],
- guard=[],
- body=[AccVar]}],
- fc=Fc},
- Fps,St}
- end;
+ {Vars=[_,AccVar],St2} = new_vars(LA, 2, St1),
+ F = #c_var{anno=LA,name={Name,2}},
+ Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]},
+ Fc = function_clause(Vars, LA, {Name,2}),
+ TailClause = #iclause{anno=LAnno,pats=[TailPat,AccVar],guard=[],
+ body=[AccVar]},
+ Cs0 = case {AccPat,AccGuard} of
+ {SkipPat,[]} ->
+ %% Skip and accumulator patterns are the same and there is
+ %% no guard, no need to generate a skip clause.
+ [TailClause];
+ _ ->
+ [#iclause{anno=#a{anno=[compiler_generated|LA]},
+ pats=[SkipPat,AccVar],guard=[],body=[Nc]},
+ TailClause]
+ end,
+ {Cs,St4} = case AccPat of
+ nomatch ->
+ %% The accumulator pattern never matches, no need
+ %% for an accumulator clause.
+ {Cs0,St2};
+ _ ->
+ {Bc,Bps,St3} = bc_tq1(Line, E, Qs, AccVar, St2),
+ Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc],
+ {[#iclause{anno=LAnno,
+ pats=[AccPat,AccVar],guard=AccGuard,
+ body=Body}|Cs0],
+ St3}
+ end,
+ Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc},
+ {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,2},Fun}],
+ body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]},
+ [],St4};
+bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
+ filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5);
bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
{E,Pre,St} = expr({bin,Bl,[{bin_element,Bl,
{var,Bl,AccVar#c_var.name},
@@ -1169,16 +1005,154 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
[binary,{unit,1}]}|Elements]}, St0),
#a{anno=A} = Anno0 = get_anno(E),
Anno = Anno0#a{anno=[compiler_generated,single_use|A]},
- %%Anno = Anno0#a{anno=[compiler_generated|A]},
{set_anno(E, Anno),Pre,St}.
+%% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) ->
+%% {Case,[PreExpr],State}.
+%% Transform an intermediate comprehension filter to its intermediate case
+%% representation.
+
+filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg={Pre,Arg}},
+ Mc, St0, Qs, TqFun) ->
+ %% The filter is an expression, it is compiled to a case of degree 1 with
+ %% 3 clauses, one accumulating, one skipping and the final one throwing
+ %% {case_clause,Value} where Value is the result of the filter and is not a
+ %% boolean.
+ {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0),
+ {FailPat,St2} = new_var(St1),
+ Fc = fail_clause([FailPat], LA,
+ c_tuple([#c_literal{val=case_clause},FailPat])),
+ {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[Arg],
+ clauses=[#iclause{anno=LAnno,
+ pats=[#c_literal{val=true}],guard=[],
+ body=Lps ++ [Lc]},
+ #iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
+ pats=[#c_literal{val=false}],guard=[],
+ body=[Mc]}],
+ fc=Fc},
+ Pre,St2};
+filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg=Guard},
+ Mc, St0, Qs, TqFun) when is_list(Guard) ->
+ %% Otherwise it is a guard, compiled to a case of degree 0 with 2 clauses,
+ %% the first matches if the guard succeeds and the comprehension continues
+ %% or the second one is selected and the current element is skipped.
+ {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0),
+ {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[],
+ clauses=[#iclause{anno=LAnno,pats=[],guard=Guard,body=Lps ++ [Lc]}],
+ fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
+ pats=[],guard=[],body=[Mc]}},
+ [],St1}.
+
+%% preprocess_quals(Line, [Qualifier], State) -> {[Qualifier'],State}.
+%% Preprocess a list of Erlang qualifiers into its intermediate representation,
+%% represented as a list of #igen{} and #ifilter{} records. We recognise guard
+%% tests and try to fold them together and join to a preceding generators, this
+%% should give us better and more compact code.
+
+preprocess_quals(Line, Qs, St) ->
+ preprocess_quals(Line, Qs, St, []).
+
+preprocess_quals(Line, [Q|Qs0], St0, Acc) ->
+ case is_generator(Q) of
+ true ->
+ {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0),
+ {Gen,St} = generator(Line, Q, Gs, St0),
+ preprocess_quals(Line, Qs, St, [Gen|Acc]);
+ false ->
+ LAnno = #a{anno=lineno_anno(get_anno(Q), St0)},
+ case is_guard_test(Q) of
+ true ->
+ %% When a filter is a guard test, its argument in the
+ %% #ifilter{} record is a list as returned by
+ %% lc_guard_tests/2.
+ {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0),
+ {Cg,St} = lc_guard_tests([Q|Gs], St0),
+ Filter = #ifilter{anno=LAnno,arg=Cg},
+ preprocess_quals(Line, Qs, St, [Filter|Acc]);
+ false ->
+ %% Otherwise, it is a pair {Pre,Arg} as in a generator
+ %% input.
+ {Ce,Pre,St} = novars(Q, St0),
+ Filter = #ifilter{anno=LAnno,arg={Pre,Ce}},
+ preprocess_quals(Line, Qs0, St, [Filter|Acc])
+ end
+ end;
+preprocess_quals(_, [], St, Acc) ->
+ {reverse(Acc),St}.
+
+is_generator({generate,_,_,_}) -> true;
+is_generator({b_generate,_,_,_}) -> true;
+is_generator(_) -> false.
+
+%%
+%% Generators are abstracted as sextuplets:
+%% - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr.
+%% - acc_guard is the list of guards immediately following the current
+%% generator in the qualifier list input.
+%% - skip_pat is the skip pattern, e.g. <<X,_:X,Tail/bitstring>> for
+%% <<X,1:X>> <= Expr.
+%% - tail is the variable used in AccPat and SkipPat bound to the rest of the
+%% generator input.
+%% - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list
+%% and bit string generators.
+%% - arg is a pair {Pre,Arg} where Pre is the list of expressions to be
+%% inserted before the comprehension function and Arg is the expression
+%% that it should be passed.
+%%
+
+%% generator(Line, Generator, Guard, State) -> {Generator',State}.
+%% Transform a given generator into its #igen{} representation.
+
+generator(Line, {generate,Lg,P0,E}, Gs, St0) ->
+ LA = lineno_anno(Line, St0),
+ GA = lineno_anno(Lg, St0),
+ {Head,St1} = list_gen_pattern(P0, Line, St0),
+ {[Tail,Skip],St2} = new_vars(2, St1),
+ {Cg,St3} = lc_guard_tests(Gs, St2),
+ {AccPat,SkipPat} = case Head of
+ #c_var{} ->
+ %% If the generator pattern is a variable, the
+ %% pattern from the accumulator clause can be
+ %% reused in the skip one. lc_tq and bc_tq1 takes
+ %% care of dismissing the latter in that case.
+ Cons = ann_c_cons(LA, Head, Tail),
+ {Cons,Cons};
+ nomatch ->
+ %% If it never matches, there is no need for
+ %% an accumulator clause.
+ {nomatch,ann_c_cons(LA, Skip, Tail)};
+ _ ->
+ {ann_c_cons(LA, Head, Tail),
+ ann_c_cons(LA, Skip, Tail)}
+ end,
+ {Ce,Pre,St4} = safe(E, St3),
+ Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
+ tail=Tail,tail_pat=#c_literal{anno=LA,val=[]},arg={Pre,Ce}},
+ {Gen,St4};
+generator(Line, {b_generate,Lg,P,E}, Gs, St0) ->
+ LA = lineno_anno(Line, St0),
+ GA = lineno_anno(Lg, St0),
+ Cp = #c_binary{segments=Segs} = pattern(P, St0),
+ %% The function append_tail_segment/2 keeps variable patterns as-is, making
+ %% it possible to have the same skip clause removal as with list generators.
+ {AccSegs,Tail,TailSeg,St1} = append_tail_segment(Segs, St0),
+ AccPat = Cp#c_binary{segments=AccSegs},
+ {Cg,St2} = lc_guard_tests(Gs, St1),
+ {SkipSegs,St3} = emasculate_segments(AccSegs, St2),
+ SkipPat = Cp#c_binary{segments=SkipSegs},
+ {Ce,Pre,St4} = safe(E, St3),
+ Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
+ tail=Tail,tail_pat=#c_binary{anno=LA,segments=[TailSeg]},
+ arg={Pre,Ce}},
+ {Gen,St4}.
+
append_tail_segment(Segs, St0) ->
{Var,St} = new_var(St0),
Tail = #c_bitstr{val=Var,size=#c_literal{val=all},
unit=#c_literal{val=1},
type=#c_literal{val=binary},
flags=#c_literal{val=[unsigned,big]}},
- {Segs++[Tail],Var,St}.
+ {Segs++[Tail],Var,Tail,St}.
emasculate_segments(Segs, St) ->
emasculate_segments(Segs, St, []).
@@ -1189,7 +1163,7 @@ emasculate_segments([B|Rest], St0, Acc) ->
{Var,St1} = new_var(St0),
emasculate_segments(Rest, St1, [B#c_bitstr{val=Var}|Acc]);
emasculate_segments([], St, Acc) ->
- {lists:reverse(Acc),St}.
+ {reverse(Acc),St}.
lc_guard_tests([], St) -> {[],St};
lc_guard_tests(Gs0, St0) ->
@@ -1588,15 +1562,6 @@ pat_alias_list(_, _) -> throw(nomatch).
pattern_list(Ps, St) -> [pattern(P, St) || P <- Ps].
-%% first([A]) -> [A].
-%% last([A]) -> A.
-
-first([_]) -> [];
-first([H|T]) -> [H|first(T)].
-
-last([L]) -> L;
-last([_|T]) -> last(T).
-
%% make_vars([Name]) -> [{Var,Name}].
make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ].
@@ -1679,13 +1644,13 @@ uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) ->
uguard([], [], _, St) -> {[],St};
uguard(Pg, [], Ks, St) ->
%% No guard, so fold together equality tests.
- uguard(first(Pg), [last(Pg)], Ks, St);
+ uguard(droplast(Pg), [last(Pg)], Ks, St);
uguard(Pg, Gs0, Ks, St0) ->
%% Gs0 must contain at least one element here.
{Gs3,St5} = foldr(fun (T, {Gs1,St1}) ->
{L,St2} = new_var(St1),
{R,St3} = new_var(St2),
- {[#iset{var=L,arg=T}] ++ first(Gs1) ++
+ {[#iset{var=L,arg=T}] ++ droplast(Gs1) ++
[#iset{var=R,arg=last(Gs1)},
#icall{anno=#a{}, %Must have an #a{}
module=#c_literal{val=erlang},
@@ -2088,7 +2053,8 @@ cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0,
RecVar = #c_var{name={Name,length(Ps)}},
Let = #c_let{vars=[#c_var{name=Name}],arg=RecVar,body=Body},
CFun1 = CFun0#c_fun{body=Let},
- Letrec = #c_letrec{defs=[{RecVar,CFun1}],
+ Letrec = #c_letrec{anno=A0#a.anno,
+ defs=[{RecVar,CFun1}],
body=RecVar},
{Letrec,[],Us1,St1}
end;
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 9a2b1605ad..bc5ca0314a 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -81,7 +81,7 @@
-export([module/2,format_error/1]).
-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2,
- keymember/3,keyfind/3,partition/2]).
+ keymember/3,keyfind/3,partition/2,droplast/1,last/1]).
-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
-import(cerl, [c_tuple/1]).
@@ -274,8 +274,7 @@ expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
{#k_tuple{anno=A,es=Kes},Ep,St1};
expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) ->
{Var,[],St1} = expr(Var0, Sub, St0),
- {Kes,Ep,St2} = map_pairs(Ces, Sub, St1),
- {#k_map{anno=A,var=Var,es=Kes},Ep,St2};
+ map_split_pairs(A, Var, Ces, Sub, St1);
expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
try atomic_bin(Cv, Sub, St0) of
{Kv,Ep,St1} ->
@@ -351,7 +350,7 @@ expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->
{Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here!
{Km,St3} = kmatch(Kvs, Ccs, Sub, St2),
Match = flatten_seq(build_match(Kvs, Km)),
- {last(Match),Pa ++ Pv ++ first(Match),St3};
+ {last(Match),Pa ++ Pv ++ droplast(Match),St3};
expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->
{Ke,Pe,St1} = atomic(Ce, Sub, St0), %Force this to be atomic!
{Rvar,St2} = new_var(St1),
@@ -497,15 +496,71 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
translate_fc(Args) ->
[#c_literal{val=function_clause},make_list(Args)].
-%% FIXME: Not completed
-map_pairs(Es, Sub, St) ->
- foldr(fun
- (#c_map_pair{op=#c_literal{val=Op},key=K0,val=V0}, {Kes,Esp,St0}) when
- Op =:= assoc; Op =:= exact -> %% assert Op
- {K,[],St1} = expr(K0, Sub, St0),
- {V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{op=Op,key=K,val=V}|Kes],Ep ++ Esp,St2}
- end, {[],[],St}, Es).
+ %{Kes,Ep,St2} = map_pairs(Ces, Sub, St1),
+map_split_pairs(A, Var, Ces, Sub, St0) ->
+ %% two steps
+ %% 1. force variables
+ %% 2. remove multiples
+ Pairs0 = [{Op,K,V} || #c_map_pair{op=#c_literal{val=Op},key=K,val=V} <- Ces],
+ {Pairs,Esp,St1} = foldr(fun
+ ({Op,K0,V0}, {Ops,Espi,Sti0}) when Op =:= assoc; Op =:= exact ->
+ {K,[],Sti1} = expr(K0, Sub, Sti0),
+ {V,Ep,Sti2} = atomic(V0, Sub, Sti1),
+ {[{Op,K,V}|Ops],Ep ++ Espi,Sti2}
+ end, {[],[],St0}, Pairs0),
+
+ case map_group_pairs(Pairs) of
+ {Assoc,[]} ->
+ Kes = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc],
+ {#k_map{anno=A,op=assoc,var=Var,es=Kes},Esp,St1};
+ {[],Exact} ->
+ Kes = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact],
+ {#k_map{anno=A,op=exact,var=Var,es=Kes},Esp,St1};
+ {Assoc,Exact} ->
+ Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc],
+ {Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1),
+ Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact],
+ {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Em ++ Esp,St2}
+
+ end.
+
+%% Group map by Assoc operations and Exact operations
+
+map_group_pairs(Es) ->
+ Groups = dict:to_list(map_group_pairs(Es,dict:new())),
+ partition(fun({_,{Op,_,_}}) -> Op =:= assoc end, Groups).
+
+map_group_pairs([{assoc,K,V}|Es0],Used0) ->
+ Used1 = case map_key_is_used(K,Used0) of
+ {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0);
+ {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0);
+ _ -> map_key_set_used(K,{assoc,K,V},Used0)
+ end,
+ map_group_pairs(Es0,Used1);
+map_group_pairs([{exact,K,V}|Es0],Used0) ->
+ Used1 = case map_key_is_used(K,Used0) of
+ {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0);
+ {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0);
+ _ -> map_key_set_used(K,{exact,K,V},Used0)
+ end,
+ map_group_pairs(Es0,Used1);
+map_group_pairs([],Used) ->
+ Used.
+
+map_key_set_used(K,How,Used) ->
+ dict:store(map_key_clean(K),How,Used).
+
+map_key_is_used(K,Used) ->
+ dict:find(map_key_clean(K),Used).
+
+%% Be explicit instead of using set_kanno(K,[])
+map_key_clean(#k_literal{val=V}) -> {k_literal,V};
+map_key_clean(#k_int{val=V}) -> {k_int,V};
+map_key_clean(#k_float{val=V}) -> {k_float,V};
+map_key_clean(#k_atom{val=V}) -> {k_atom,V};
+map_key_clean(#k_nil{}) -> k_nil;
+map_key_clean(#k_var{name=V}) -> {k_var,V}.
+
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
%% Classify the call.
@@ -664,11 +719,11 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
{#k_tuple{anno=A,es=Kes},Osub1,St1};
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
- {#k_map{anno=A,es=Kes},Osub1,St1};
+ {#k_map{anno=A,op=exact,es=Kes},Osub1,St1};
pattern(#c_map_pair{op=#c_literal{val=exact},anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
{Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
{Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
- {#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2};
+ {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -847,15 +902,6 @@ foldr2(Fun, Acc0, [E1|L1], [E2|L2]) ->
foldr2(Fun, Acc1, L1, L2);
foldr2(_, Acc, [], []) -> Acc.
-%% first([A]) -> [A].
-%% last([A]) -> A.
-
-last([L]) -> L;
-last([_|T]) -> last(T).
-
-first([_]) -> [];
-first([H|T]) -> [H|first(T)].
-
%% This code implements the algorithm for an optimizing compiler for
%% pattern matching given "The Implementation of Functional
%% Programming Languages" by Simon Peyton Jones. The code is much
@@ -1365,9 +1411,8 @@ new_clauses(Cs0, U, St) ->
[S,N|As];
#k_bin_int{next=N} ->
[N|As];
- #k_map{es=Es} ->
- Vals = [V ||
- #k_map_pair{op=exact,val=V} <- Es],
+ #k_map{op=exact,es=Es} ->
+ Vals = [V || #k_map_pair{val=V} <- Es],
Vals ++ As;
_Other ->
As
@@ -1467,9 +1512,9 @@ arg_val(Arg, C) ->
_ ->
{set_kanno(S, []),U,T,Fs}
end;
- #k_map{es=Es} ->
+ #k_map{op=exact,es=Es} ->
Keys = [begin
- #k_map_pair{op=exact,key=#k_literal{val=Key}} = Pair,
+ #k_map_pair{key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
%% multiple keys may have the same name
@@ -1885,7 +1930,7 @@ pat_vars(#k_tuple{es=Es}) ->
pat_list_vars(Es);
pat_vars(#k_map{es=Es}) ->
pat_list_vars(Es);
-pat_vars(#k_map_pair{op=exact,val=V}) ->
+pat_vars(#k_map_pair{val=V}) ->
pat_vars(V).
pat_list_vars(Ps) ->
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index c7886a070d..ab66445f73 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -38,8 +38,8 @@
-record(k_nil, {anno=[]}).
-record(k_tuple, {anno=[],es}).
--record(k_map, {anno=[],var,es}).
--record(k_map_pair, {anno=[],op,key,val}).
+-record(k_map, {anno=[],var,op,es}).
+-record(k_map_pair, {anno=[],key,val}).
-record(k_cons, {anno=[],hd,tl}).
-record(k_binary, {anno=[],segs}).
-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index edbd3f74f8..639c6737e2 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -110,15 +110,18 @@ format_1(#k_map{var=#k_var{}=Var,es=Es}, Ctxt) ->
" | ",format_1(Var, Ctxt),
$},$~
];
-format_1(#k_map{es=Es}, Ctxt) ->
- [$~,${,
+format_1(#k_map{op=assoc,es=Es}, Ctxt) ->
+ ["~{",
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- $},$~
+ "}~"
+ ];
+format_1(#k_map{op=exact,es=Es}, Ctxt) ->
+ ["::{",
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ "}::"
];
-format_1(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) ->
- ["~<",format(K, Ctxt),",",format(V, Ctxt),">"];
-format_1(#k_map_pair{op=exact,key=K,val=V}, Ctxt) ->
- ["::<",format(K, Ctxt),",",format(V, Ctxt),">"];
+format_1(#k_map_pair{key=K,val=V}, Ctxt) ->
+ ["<",format(K, Ctxt),",",format(V, Ctxt),">"];
format_1(#k_binary{segs=S}, Ctxt) ->
["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
format_1(#k_bin_seg{next=Next}=S, Ctxt) ->
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index ae928e955c..c4f54a7970 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -367,12 +367,10 @@ literal(#k_bin_end{}, Ctxt) ->
{bin_end,Ctxt};
literal(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list(Es, Ctxt)};
-literal(#k_map{var=Var,es=Es}, Ctxt) ->
- {map,literal(Var, Ctxt),literal_list(Es, Ctxt)};
-literal(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) ->
- {map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)};
-literal(#k_map_pair{op=exact,key=K,val=V}, Ctxt) ->
- {map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)};
+literal(#k_map{op=Op,var=Var,es=Es}, Ctxt) ->
+ {map,Op,literal(Var, Ctxt),literal_list(Es, Ctxt)};
+literal(#k_map_pair{key=K,val=V}, Ctxt) ->
+ {map_pair,literal(K, Ctxt),literal(V, Ctxt)};
literal(#k_literal{val=V}, _Ctxt) ->
{literal,V}.
@@ -402,8 +400,8 @@ literal2(#k_bin_end{}, Ctxt) ->
{bin_end,Ctxt};
literal2(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list2(Es, Ctxt)};
-literal2(#k_map{es=Es}, Ctxt) ->
- {map,literal_list2(Es, Ctxt)};
+literal2(#k_map{op=Op,es=Es}, Ctxt) ->
+ {map,Op,literal_list2(Es, Ctxt)};
literal2(#k_map_pair{key=K,val=V}, Ctxt) ->
{map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}.
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 51b3064589..39c9fea5d9 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -26,6 +26,7 @@ MODULES= \
guard_SUITE \
inline_SUITE \
lc_SUITE \
+ map_SUITE \
match_SUITE \
misc_SUITE \
num_bif_SUITE \
@@ -47,6 +48,7 @@ NO_OPT= \
fun \
guard \
lc \
+ map \
match \
misc \
num_bif \
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 9f15845d33..149b9bbb8f 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -34,7 +34,7 @@
otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1,
match_string/1,zero_width/1,bad_size/1,haystack/1,
cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
- no_partition/1]).
+ no_partition/1,calling_a_binary/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -59,7 +59,7 @@ groups() ->
matching_and_andalso,otp_7188,otp_7233,otp_7240,
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
- no_partition]}].
+ no_partition,calling_a_binary]}].
init_per_suite(Config) ->
@@ -1178,6 +1178,17 @@ no_partition_2([], a5) ->
no_partition_2(42.0, a6) ->
six.
+calling_a_binary(Config) when is_list(Config) ->
+ [] = call_binary(<<>>, []),
+ {'EXIT',{badarg,_}} = (catch call_binary(<<1>>, [])),
+ {'EXIT',{badarg,_}} = (catch call_binary(<<1,2,3>>, [])),
+ ok.
+
+call_binary(<<>>, Acc) ->
+ Acc;
+call_binary(<<H,T/bits>>, Acc) ->
+ T(<<Acc/binary,H>>).
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index de35ebc7bd..34c4b1e264 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -286,57 +286,67 @@ cond_and_ifdef(Config) when is_list(Config) ->
ok.
listings(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(8)),
- ?line DataDir = ?config(data_dir, Config),
- ?line PrivDir = ?config(priv_dir, Config),
- ?line Simple = filename:join(DataDir, simple),
- ?line TargetDir = filename:join(PrivDir, listings),
- ?line ok = file:make_dir(TargetDir),
+ Dog = test_server:timetrap(test_server:minutes(8)),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ok = do_file_listings(DataDir, PrivDir, [
+ "simple",
+ "small",
+ "small_maps"
+ ]),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+do_file_listings(_, _, []) -> ok;
+do_file_listings(DataDir, PrivDir, [File|Files]) ->
+ Simple = filename:join(DataDir, File),
+ TargetDir = filename:join(PrivDir, listings),
+ ok = file:make_dir(TargetDir),
%% Test all dedicated listing options.
- ?line do_listing(Simple, TargetDir, 'S'),
- ?line do_listing(Simple, TargetDir, 'E'),
- ?line do_listing(Simple, TargetDir, 'P'),
- ?line do_listing(Simple, TargetDir, dpp, ".pp"),
- ?line do_listing(Simple, TargetDir, dabstr, ".abstr"),
- ?line do_listing(Simple, TargetDir, dexp, ".expand"),
- ?line do_listing(Simple, TargetDir, dcore, ".core"),
- ?line do_listing(Simple, TargetDir, doldinline, ".oldinline"),
- ?line do_listing(Simple, TargetDir, dinline, ".inline"),
- ?line do_listing(Simple, TargetDir, dcore, ".core"),
- ?line do_listing(Simple, TargetDir, dcopt, ".copt"),
- ?line do_listing(Simple, TargetDir, dsetel, ".dsetel"),
- ?line do_listing(Simple, TargetDir, dkern, ".kernel"),
- ?line do_listing(Simple, TargetDir, dlife, ".life"),
- ?line do_listing(Simple, TargetDir, dcg, ".codegen"),
- ?line do_listing(Simple, TargetDir, dblk, ".block"),
- ?line do_listing(Simple, TargetDir, dbool, ".bool"),
- ?line do_listing(Simple, TargetDir, dtype, ".type"),
- ?line do_listing(Simple, TargetDir, ddead, ".dead"),
- ?line do_listing(Simple, TargetDir, djmp, ".jump"),
- ?line do_listing(Simple, TargetDir, dclean, ".clean"),
- ?line do_listing(Simple, TargetDir, dpeep, ".peep"),
- ?line do_listing(Simple, TargetDir, dopt, ".optimize"),
+ do_listing(Simple, TargetDir, 'S'),
+ do_listing(Simple, TargetDir, 'E'),
+ do_listing(Simple, TargetDir, 'P'),
+ do_listing(Simple, TargetDir, dpp, ".pp"),
+ do_listing(Simple, TargetDir, dabstr, ".abstr"),
+ do_listing(Simple, TargetDir, dexp, ".expand"),
+ do_listing(Simple, TargetDir, dcore, ".core"),
+ do_listing(Simple, TargetDir, doldinline, ".oldinline"),
+ do_listing(Simple, TargetDir, dinline, ".inline"),
+ do_listing(Simple, TargetDir, dcore, ".core"),
+ do_listing(Simple, TargetDir, dcopt, ".copt"),
+ do_listing(Simple, TargetDir, dsetel, ".dsetel"),
+ do_listing(Simple, TargetDir, dkern, ".kernel"),
+ do_listing(Simple, TargetDir, dlife, ".life"),
+ do_listing(Simple, TargetDir, dcg, ".codegen"),
+ do_listing(Simple, TargetDir, dblk, ".block"),
+ do_listing(Simple, TargetDir, dbool, ".bool"),
+ do_listing(Simple, TargetDir, dtype, ".type"),
+ do_listing(Simple, TargetDir, ddead, ".dead"),
+ do_listing(Simple, TargetDir, djmp, ".jump"),
+ do_listing(Simple, TargetDir, dclean, ".clean"),
+ do_listing(Simple, TargetDir, dpeep, ".peep"),
+ do_listing(Simple, TargetDir, dopt, ".optimize"),
%% First clean up.
- ?line Listings = filename:join(PrivDir, listings),
- ?line lists:foreach(fun(F) -> ok = file:delete(F) end,
- filelib:wildcard(filename:join(Listings, "*"))),
+ Listings = filename:join(PrivDir, listings),
+ lists:foreach(fun(F) -> ok = file:delete(F) end,
+ filelib:wildcard(filename:join(Listings, "*"))),
%% Test options that produce a listing file if 'binary' is not given.
- ?line do_listing(Simple, TargetDir, to_pp, ".P"),
- ?line do_listing(Simple, TargetDir, to_exp, ".E"),
- ?line do_listing(Simple, TargetDir, to_core0, ".core"),
- ?line ok = file:delete(filename:join(Listings, "simple.core")),
- ?line do_listing(Simple, TargetDir, to_core, ".core"),
- ?line do_listing(Simple, TargetDir, to_kernel, ".kernel"),
+ do_listing(Simple, TargetDir, to_pp, ".P"),
+ do_listing(Simple, TargetDir, to_exp, ".E"),
+ do_listing(Simple, TargetDir, to_core0, ".core"),
+ ok = file:delete(filename:join(Listings, File ++ ".core")),
+ do_listing(Simple, TargetDir, to_core, ".core"),
+ do_listing(Simple, TargetDir, to_kernel, ".kernel"),
%% Final clean up.
- ?line lists:foreach(fun(F) -> ok = file:delete(F) end,
- filelib:wildcard(filename:join(Listings, "*"))),
- ?line ok = file:del_dir(Listings),
- ?line test_server:timetrap_cancel(Dog),
- ok.
+ lists:foreach(fun(F) -> ok = file:delete(F) end,
+ filelib:wildcard(filename:join(Listings, "*"))),
+ ok = file:del_dir(Listings),
+
+ do_file_listings(DataDir,PrivDir,Files).
listings_big(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:minutes(10)),
diff --git a/lib/compiler/test/compile_SUITE_data/small.erl b/lib/compiler/test/compile_SUITE_data/small.erl
new file mode 100644
index 0000000000..37cd270e50
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/small.erl
@@ -0,0 +1,48 @@
+-module(small).
+
+-export([go/0,go/2]).
+
+
+-small_attribute({value,3}).
+
+go() -> go(3, 3.0).
+go(A,B) ->
+ V1 = A + B,
+ V2 = A * B,
+ V3 = V1 / V2,
+ V4 = V3 / 0.3,
+ V5 = V1 + V2 + V3 + V4,
+ try
+ R = call(<<"wazzup">>, A),
+ {A,B,V5,R,t(),recv()}
+ catch
+ C:E ->
+ {error, C, E}
+ end.
+
+-spec call(binary(), term()) -> binary().
+
+call(<<"wa", B/binary>>,V) when is_integer(V) -> B;
+call(B,_) -> B.
+
+t() ->
+ <<23:32, V:14, _:2, B/binary>> = id(<<"wazzup world">>),
+ {V,B}.
+
+recv() ->
+ F = fun() ->
+ receive
+ 1 -> ok;
+ 2 -> ok;
+ 3 -> ok;
+ a -> ok;
+ _ -> none
+ after 0 -> tmo
+ end
+ end,
+ tmo = F(),
+ ok.
+
+
+id(I) -> I.
+
diff --git a/lib/compiler/test/compile_SUITE_data/small_maps.erl b/lib/compiler/test/compile_SUITE_data/small_maps.erl
new file mode 100644
index 0000000000..a17a136a7d
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/small_maps.erl
@@ -0,0 +1,16 @@
+-module(small_maps).
+
+-export([go/0,go/1]).
+
+go() ->
+ go(1337).
+
+go(V0) ->
+ M0 = #{ a => 1, val => V0},
+ V1 = get_val(M0),
+ M1 = M0#{ val := [V0,V1] },
+ {some_val,[1337,{some_val,1337}]} = get_val(M1),
+ ok.
+
+get_val(#{ "wazzup" := _, val := V}) -> V;
+get_val(#{ val := V }) -> {some_val, V}.
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 1a521c3591..aa222c48de 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -24,7 +24,7 @@
dehydrated_itracer/1,nested_tries/1,
seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
unsafe_case/1,nomatch_shadow/1,reversed_annos/1,
- map_core_test/1]).
+ map_core_test/1,eval_case/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -50,7 +50,7 @@ groups() ->
[{p,test_lib:parallel(),
[dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos,
- map_core_test
+ map_core_test,eval_case
]}].
@@ -76,6 +76,7 @@ end_per_group(_GroupName, Config) ->
?comp(nomatch_shadow).
?comp(reversed_annos).
?comp(map_core_test).
+?comp(eval_case).
try_it(Mod, Conf) ->
Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)),
diff --git a/lib/compiler/test/core_SUITE_data/eval_case.core b/lib/compiler/test/core_SUITE_data/eval_case.core
new file mode 100644
index 0000000000..f2776e2b1f
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/eval_case.core
@@ -0,0 +1,34 @@
+module 'eval_case' ['eval_case'/0]
+ attributes []
+'eval_case'/0 =
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ case apply 'do_case'/0() of
+ <'ok'> when 'true' ->
+ 'ok'
+ ( <_cor0> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_cor0})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ ( primop 'match_fail'
+ ({'function_clause'})
+ -| [{'function_name',{'eval_case',0}}] )
+ -| ['compiler_generated'] )
+ end
+'do_case'/0 =
+ fun () ->
+ case let <_cor0> =
+ apply 'id'/1(42)
+ in let <_cor1> =
+ call 'erlang':'+'
+ (_cor0, 7)
+ in {'x',_cor1} of
+ <{'x',49}> when 'true' ->
+ 'ok'
+ end
+'id'/1 =
+ fun (_cor0) -> _cor0
+end
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 69f61a046f..8151dc1b16 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -22,7 +22,8 @@
init_per_group/2,end_per_group/2,
t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1,
eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1,
- unused_multiple_values_error/1,unused_multiple_values/1]).
+ unused_multiple_values_error/1,unused_multiple_values/1,
+ multiple_aliases/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -38,7 +39,8 @@ groups() ->
[{p,test_lib:parallel(),
[t_element,setelement,t_length,append,t_apply,bifs,
eq,nested_call_in_case,guard_try_catch,coverage,
- unused_multiple_values_error,unused_multiple_values]}].
+ unused_multiple_values_error,unused_multiple_values,
+ multiple_aliases]}].
init_per_suite(Config) ->
@@ -299,8 +301,6 @@ cover_is_safe_bool_expr(X) ->
bsm_an_inlined(<<_:8>>, _) -> ok;
bsm_an_inlined(_, _) -> error.
-id(I) -> I.
-
unused_multiple_values_error(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
Dir = filename:dirname(code:which(?MODULE)),
@@ -338,3 +338,31 @@ do_something(I) ->
put(unused_multiple_values,
[I|get(unused_multiple_values)]),
I.
+
+
+%% Make sure that multiple aliases does not cause
+%% the case expression to be evaluated twice.
+multiple_aliases(Config) when is_list(Config) ->
+ do_ma(fun() ->
+ X = Y = run_once(),
+ {X,Y}
+ end, {ok,ok}),
+ do_ma(fun() ->
+ case {true,run_once()} of
+ {true=A=B,ok=X=Y} ->
+ {A,B,X,Y}
+ end
+ end, {true,true,ok,ok}),
+ ok.
+
+do_ma(Fun, Expected) when is_function(Fun, 0) ->
+ Expected = Fun(),
+ ran_once = erase(run_once),
+ ok.
+
+run_once() ->
+ undefined = put(run_once, ran_once),
+ ok.
+
+
+id(I) -> I.
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
new file mode 100644
index 0000000000..86f65c3ed6
--- /dev/null
+++ b/lib/compiler/test/map_SUITE.erl
@@ -0,0 +1,507 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(map_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2
+ ]).
+
+-export([
+ t_build_and_match_literals/1,
+ t_update_literals/1,t_match_and_update_literals/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1,t_update_exact/1,
+ t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
+ t_guard_receive/1, t_guard_fun/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ t_map_size/1,
+
+ %% warnings
+ t_warn_useless_build/1,
+ t_warn_pair_key_overloaded/1,
+
+ %% not covered in 17.0-rc1
+ t_build_and_match_over_alloc/1,
+ t_build_and_match_empty_val/1,
+ t_build_and_match_val/1,
+
+ %% errors in 17.0-rc1
+ t_update_values/1
+ ]).
+
+suite() -> [].
+
+all() -> [
+ t_build_and_match_literals,
+ t_update_literals, t_match_and_update_literals,
+ t_update_map_expressions,
+ t_update_assoc,t_update_exact,
+ t_guard_bifs, t_guard_sequence, t_guard_update,
+ t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_sort_literals,
+
+ %% warnings
+ t_warn_useless_build,
+ t_warn_pair_key_overloaded,
+
+ %% not covered in 17.0-rc1
+ t_build_and_match_over_alloc,
+ t_build_and_match_empty_val,
+ t_build_and_match_val,
+
+ %% errors in 17.0-rc1
+ t_update_values
+ ].
+
+groups() -> [].
+
+init_per_suite(Config) -> Config.
+end_per_suite(_Config) -> ok.
+
+init_per_group(_GroupName, Config) -> Config.
+end_per_group(_GroupName, Config) -> Config.
+
+%% tests
+
+t_build_and_match_literals(Config) when is_list(Config) ->
+ #{} = id(#{}),
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% error case
+ %V = 32,
+ %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ ok.
+
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 1),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "c" => 2}, 2),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+% test map updates without matching
+t_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
+
+% test map updates with matching
+t_match_and_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+
+t_update_map_expressions(Config) when is_list(Config) ->
+ M = maps:new(),
+ #{ a := 1 } = M#{a => 1},
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Test need to be in a fun.
+ %% This tests that let expr optimisation in sys_core_fold
+ %% covers maps correctly.
+ F = fun() ->
+ M0 = id(#{ "a" => [1,2,3] }),
+ #{ "a" := _ } = M0,
+ M0#{ "a" := b }
+ end,
+
+ #{ "a" := b } = F(),
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+
+t_update_assoc(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+
+ ok.
+
+t_update_exact(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ true = M2 =/= M0#{3=>right,3.0:=new},
+ #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new},
+
+ M3 = id(#{ 1 => val}),
+ #{1 := update2,1.0 := new_val4} = M3#{
+ 1.0 => new_val1, 1 := update, 1=> update3,
+ 1 := update2, 1.0 := new_val2, 1.0 => new_val3,
+ 1.0 => new_val4 },
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })),
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+ ok.
+
+t_update_values(Config) when is_list(Config) ->
+ V0 = id(1337),
+ M0 = #{ a => 1, val => V0},
+ V1 = get_val(M0),
+ M1 = M0#{ val := [V0,V1], "wazzup" => 42 },
+ [1337, {some_val, 1337}] = get_val(M1),
+
+ N = 110,
+ List = [{[I,1,2,3,I],{1,2,3,"wat",I}}|| I <- lists:seq(1,N)],
+
+ {_,_,#{val2 := {1,2,3,"wat",N}, val1 := [N,1,2,3,N]}} = lists:foldl(fun
+ ({V2,V3},{Old2,Old3,Mi}) ->
+ ok = check_val(Mi,Old2,Old3),
+ #{ val1 := Old2, val2 := Old3 } = Mi,
+ {V2,V3, Mi#{ val1 := id(V2), val2 := V1, val2 => id(V3)}}
+ end, {none, none, #{val1=>none,val2=>none}},List),
+ ok.
+
+check_val(#{val1:=V1, val2:=V2},V1,V2) -> ok.
+
+get_val(#{ "wazzup" := _, val := V}) -> V;
+get_val(#{ val := V }) -> {some_val, V}.
+
+t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ ok.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+t_guard_sequence(Config) when is_list(Config) ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+
+t_guard_update(Config) when is_list(Config) ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third;
+map_guard_update(_, _) -> error.
+
+t_guard_receive(Config) when is_list(Config) ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <<B1/binary, B2/binary>>,
+
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
+
+
+t_list_comprehension(Config) when is_list(Config) ->
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
+
+t_guard_fun(Config) when is_list(Config) ->
+ F1 = fun
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
+ ok.
+
+
+t_map_sort_literals(Config) when is_list(Config) ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = id(#{ a => 1 }) < id(#{ b => 1}),
+ false = id(#{ b => 1 }) < id(#{ a => 1}),
+ true = id(#{ a => 1, b => 1, c => 1 }) < id(#{ b => 1, c => 1, d => 1}),
+ true = id(#{ b => 1, c => 1, d => 1 }) > id(#{ a => 1, b => 1, c => 1}),
+ true = id(#{ c => 1, b => 1, a => 1 }) < id(#{ b => 1, c => 1, d => 1}),
+ true = id(#{ "a" => 1 }) < id(#{ <<"a">> => 1}),
+ false = id(#{ <<"a">> => 1 }) < id(#{ "a" => 1}),
+ false = id(#{ 1 => 1 }) < id(#{ 1.0 => 1}),
+ false = id(#{ 1.0 => 1 }) < id(#{ 1 => 1}),
+
+ %% value order
+ true = id(#{ a => 1 }) < id(#{ a => 2}),
+ false = id(#{ a => 2 }) < id(#{ a => 1}),
+ false = id(#{ a => 2, b => 1 }) < id(#{ a => 1, b => 3}),
+ true = id(#{ a => 1, b => 1 }) < id(#{ a => 1, b => 3}),
+
+ true = id(#{ "a" => "hi", b => 134 }) == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+t_warn_pair_key_overloaded(Config) when is_list(Config) ->
+ #{ "hi1" := 42 } = id(#{ "hi1" => 1, "hi1" => 42 }),
+
+ #{ "hi1" := 1337, "hi2" := [2], "hi3" := 3 } = id(#{
+ "hi1" => erlang:atom_to_binary(?MODULE,utf8),
+ "hi1" => erlang:binary_to_atom(<<"wazzup">>,utf8),
+ "hi1" => erlang:binary_to_float(<<"3.1416">>),
+ "hi1" => erlang:float_to_binary(3.1416),
+ "hi2" => erlang:pid_to_list(self()),
+ "hi3" => erlang:float_to_binary(3.1416),
+ "hi2" => lists:subtract([1,2],[1]),
+ "hi3" => +3,
+ "hi1" => erlang:min(1,2),
+ "hi1" => erlang:hash({1,2},35),
+ "hi1" => erlang:phash({1,2},33),
+ "hi1" => erlang:phash2({1,2},34),
+ "hi1" => erlang:integer_to_binary(1337),
+ "hi1" => erlang:binary_to_integer(<<"1337">>),
+ "hi4" => erlang:float_to_binary(3.1416)
+ }),
+ ok.
+
+t_warn_useless_build(Config) when is_list(Config) ->
+ [#{ a => id(I)} || I <- [1,2,3]],
+ ok.
+
+t_build_and_match_over_alloc(Config) when is_list(Config) ->
+ Ls = id([1,2,3]),
+ V0 = [a|Ls],
+ M0 = id(#{ "a" => V0 }),
+ #{ "a" := V1 } = M0,
+ V2 = id([c|Ls]),
+ M2 = id(#{ "a" => V2 }),
+ #{ "a" := V3 } = M2,
+ {[a,1,2,3],[c,1,2,3]} = id({V1,V3}),
+ ok.
+
+t_build_and_match_empty_val(Config) when is_list(Config) ->
+ F = fun(#{ "hi":=_,{1,2}:=_,1337:=_}) -> ok end,
+ ok = F(id(#{"hi"=>ok,{1,2}=>ok,1337=>ok})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch (F(id(#{"hi"=>ok})))),
+ ok.
+
+t_build_and_match_val(Config) when is_list(Config) ->
+ F = fun
+ (#{ "hi" := first, v := V}) -> {1,V};
+ (#{ "hi" := second, v := V}) -> {2,V}
+ end,
+
+
+ {1,"hello"} = F(id(#{"hi"=>first,v=>"hello"})),
+ {2,"second"} = F(id(#{"hi"=>second,v=>"second"})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch (F(id(#{"hi"=>ok})))),
+ ok.
+
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 7186956603..16d15a59e5 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -390,6 +390,10 @@ effect(Config) when is_list(Config) ->
<<X:8>>;
unused_fun ->
fun() -> {ok,X} end;
+ unused_named_fun ->
+ fun F(0) -> 1;
+ F(N) -> N*F(N-1)
+ end;
unused_atom ->
ignore; %no warning
unused_nil ->
@@ -484,8 +488,9 @@ effect(Config) when is_list(Config) ->
{22,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
{24,sys_core_fold,useless_building},
{26,sys_core_fold,useless_building},
- {32,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
- {34,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
+ {28,sys_core_fold,useless_building},
+ {36,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
+ {38,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
?line [] = run(Config, Ts),
ok.
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 925ad0c091..2e1a21178f 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -444,6 +444,15 @@ static ERL_NIF_TERM atom_ppbasis;
static ERL_NIF_TERM atom_onbasis;
#endif
+static ErlNifResourceType* hmac_context_rtype;
+struct hmac_context
+{
+ ErlNifMutex* mtx;
+ int alive;
+ HMAC_CTX ctx;
+};
+static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*);
+
/*
#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
@@ -498,6 +507,15 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
return 0;
}
+ hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context",
+ (ErlNifResourceDtor*) hmac_context_dtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
+ if (!hmac_context_rtype) {
+ PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'");
+ return 0;
+ }
+
if (library_refc > 0) {
/* Repeated loading of this library (module upgrade).
* Atoms and callbacks are already set, we are done.
@@ -1280,11 +1298,19 @@ static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
#endif
}
+static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj)
+{
+ if (obj->alive) {
+ HMAC_CTX_cleanup(&obj->ctx);
+ obj->alive = 0;
+ }
+ enif_mutex_destroy(obj->mtx);
+}
+
static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Key) */
ErlNifBinary key;
- ERL_NIF_TERM ret;
- unsigned char * ctx_buf;
+ struct hmac_context* obj;
const EVP_MD *md;
if (argv[0] == atom_sha) md = EVP_sha1();
@@ -1309,57 +1335,60 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return enif_make_badarg(env);
}
- ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret);
- HMAC_CTX_init((HMAC_CTX *) ctx_buf);
- HMAC_Init((HMAC_CTX *) ctx_buf, key.data, key.size, md);
+ obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context));
+ obj->mtx = enif_mutex_create("crypto.hmac");
+ obj->alive = 1;
+ HMAC_CTX_init(&obj->ctx);
+ HMAC_Init(&obj->ctx, key.data, key.size, md);
- return ret;
+ return enif_make_resource(env, obj);
}
static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context, Data) */
- ErlNifBinary context, data;
- ERL_NIF_TERM ret;
- unsigned char * ctx_buf;
+ ErlNifBinary data;
+ struct hmac_context* obj;
- if (!enif_inspect_binary(env, argv[0], &context)
- || !enif_inspect_iolist_as_binary(env, argv[1], &data)
- || context.size != sizeof(HMAC_CTX)) {
+ if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)) {
+ return enif_make_badarg(env);
+ }
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ enif_mutex_unlock(obj->mtx);
return enif_make_badarg(env);
}
+ HMAC_Update(&obj->ctx, data.data, data.size);
+ enif_mutex_unlock(obj->mtx);
- ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret);
- memcpy(ctx_buf, context.data, context.size);
- HMAC_Update((HMAC_CTX *)ctx_buf, data.data, data.size);
CONSUME_REDS(env,data);
-
- return ret;
+ return argv[0];
}
static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context) or (Context, HashLen) */
- ErlNifBinary context;
ERL_NIF_TERM ret;
- HMAC_CTX ctx;
+ struct hmac_context* obj;
unsigned char mac_buf[EVP_MAX_MD_SIZE];
unsigned char * mac_bin;
unsigned int req_len = 0;
unsigned int mac_len;
- if (!enif_inspect_binary(env, argv[0], &context)) {
- return enif_make_badarg(env);
- }
- if (argc == 2 && !enif_get_uint(env, argv[1], &req_len)) {
+ if (!enif_get_resource(env,argv[0],hmac_context_rtype, (void**)&obj)
+ || (argc == 2 && !enif_get_uint(env, argv[1], &req_len))) {
return enif_make_badarg(env);
}
- if (context.size != sizeof(ctx)) {
- return enif_make_badarg(env);
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ enif_mutex_unlock(obj->mtx);
+ return enif_make_badarg(env);
}
- memcpy(&ctx, context.data, context.size);
- HMAC_Final(&ctx, mac_buf, &mac_len);
- HMAC_CTX_cleanup(&ctx);
+ HMAC_Final(&obj->ctx, mac_buf, &mac_len);
+ HMAC_CTX_cleanup(&obj->ctx);
+ obj->alive = 0;
+ enif_mutex_unlock(obj->mtx);
if (argc == 2 && req_len < mac_len) {
/* Only truncate to req_len bytes if asked. */
diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c
index 81106b4cc2..a08dcec463 100644
--- a/lib/crypto/c_src/crypto_callback.c
+++ b/lib/crypto/c_src/crypto_callback.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012. All Rights Reserved.
+ * Copyright Ericsson AB 2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -17,6 +17,7 @@
* %CopyrightEnd%
*/
+#include <stdio.h>
#include <string.h>
#include <openssl/opensslconf.h>
@@ -51,13 +52,28 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks);
static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */
+static void nomem(size_t size, const char* op)
+{
+ fprintf(stderr, "Out of memory abort. Crypto failed to %s %zu bytes.\r\n",
+ op, size);
+ abort();
+}
+
static void* crypto_alloc(size_t size)
{
- return enif_alloc(size);
+ void *ret = enif_alloc(size);
+
+ if (!ret && size)
+ nomem(size, "allocate");
+ return ret;
}
static void* crypto_realloc(void* ptr, size_t size)
{
- return enif_realloc(ptr, size);
+ void* ret = enif_realloc(ptr, size);
+
+ if (!ret && size)
+ nomem(size, "reallocate");
+ return ret;
}
static void crypto_free(void* ptr)
{
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 40f829e704..c95827c371 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -366,7 +366,11 @@
or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and
<seealso marker="#hmac_final_n-2">hmac_final_n</seealso>
</p>
-
+ <warning><p>Do not use a <c>Context</c> as argument in more than one
+ call to hmac_update or hmac_final. The semantics of reusing old contexts
+ in any way is undefined and could even crash the VM in earlier releases.
+ The reason for this limitation is a lack of support in the underlying
+ OpenSSL API.</p></warning>
</desc>
</func>
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 14a17fe304..1d36aae8ee 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -505,12 +505,12 @@ lambda(Fun, As) when is_function(Fun) ->
%% ... and the fun was defined in interpreted code
{module, ?MODULE} ->
- {Mod,Name,Bs} =
+ {Mod,Name,Bs, Cs} =
case erlang:fun_info(Fun, env) of
- {env,[{{M,F},Bs0,Cs}]} ->
- {M,F,Bs0};
- {env,[{{M,F},Bs0,Cs,FName}]} ->
- {M,F,add_binding(FName, Fun, Bs0)}
+ {env,[{{M,F},Bs0,Cs0}]} ->
+ {M,F,Bs0, Cs0};
+ {env,[{{M,F},Bs0,Cs0,FName}]} ->
+ {M,F,add_binding(FName, Fun, Bs0), Cs0}
end,
{arity, Arity} = erlang:fun_info(Fun, arity),
if
@@ -654,6 +654,21 @@ expr({tuple,Line,Es0}, Bs0, Ieval) ->
{Vs,Bs} = eval_list(Es0, Bs0, Ieval#ieval{line=Line}),
{value,list_to_tuple(Vs),Bs};
+%% Map
+expr({map,Line,Fs0}, Bs0, Ieval) ->
+ {Fs,Bs} = eval_map_fields(Fs0, Bs0, Ieval#ieval{line=Line,top=false}),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) end,
+ #{}, Fs),
+ {value,Value,Bs};
+expr({map,Line,E0,Fs0}, Bs0, Ieval0) ->
+ Ieval = Ieval0#ieval{line=Line,top=false},
+ {value,E,Bs1} = expr(E0, Bs0, Ieval),
+ {Fs,Bs2} = eval_map_fields(Fs0, Bs1, Ieval),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end,
+ E, Fs),
+ {value,Value,Bs2};
+
%% A block of statements
expr({block,Line,Es},Bs,Ieval) ->
seq(Es, Bs, Ieval#ieval{line=Line});
@@ -1127,7 +1142,7 @@ eval_generate([V|Rest], P, Bs0, CompFun, Ieval) ->
case catch match1(P, V, erl_eval:new_bindings(), Bs0) of
{match,Bsn} ->
Bs2 = add_bindings(Bsn, Bs0),
- CompFun(Bs2) ++ eval_generate(Rest, P, Bs2, CompFun, Ieval);
+ CompFun(Bs2) ++ eval_generate(Rest, P, Bs0, CompFun, Ieval);
nomatch ->
eval_generate(Rest, P, Bs0, CompFun, Ieval)
end;
@@ -1468,6 +1483,19 @@ guard_expr({cons,_,H0,T0}, Bs) ->
guard_expr({tuple,_,Es0}, Bs) ->
{values,Es} = guard_exprs(Es0, Bs),
{value,list_to_tuple(Es)};
+guard_expr({map,_,Fs0}, Bs) ->
+ Fs = eval_map_fields_guard(Fs0, Bs),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) end,
+ #{}, Fs),
+ {value,Value};
+guard_expr({map,_,E0,Fs0}, Bs) ->
+ {value,E} = guard_expr(E0, Bs),
+ Fs = eval_map_fields_guard(Fs0, Bs),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end,
+ E, Fs),
+ io:format("~p~n", [{E,Value}]),
+ {value,Value};
guard_expr({bin,_,Flds}, Bs) ->
{value,V,_Bs} =
eval_bits:expr_grp(Flds, Bs,
@@ -1477,6 +1505,37 @@ guard_expr({bin,_,Flds}, Bs) ->
end, [], false),
{value,V}.
+
+%% eval_map_fields([Field], Bindings, IEvalState) ->
+%% {[{map_assoc | map_exact,Key,Value}],Bindings}
+
+eval_map_fields(Fs, Bs, Ieval) ->
+ eval_map_fields(Fs, Bs, Ieval, fun expr/3).
+
+eval_map_fields_guard(Fs0, Bs) ->
+ {Fs,_} = eval_map_fields(Fs0, Bs, #ieval{},
+ fun (G0, Bs0, _) ->
+ {value,G} = guard_expr(G0, Bs0),
+ {value,G,Bs0}
+ end),
+ Fs.
+
+eval_map_fields(Fs, Bs, Ieval, F) ->
+ eval_map_fields(Fs, Bs, Ieval, F, []).
+
+eval_map_fields([{map_field_assoc,Line,K0,V0}|Fs], Bs0, Ieval0, F, Acc) ->
+ Ieval = Ieval0#ieval{line=Line},
+ {value,K,Bs1} = F(K0, Bs0, Ieval),
+ {value,V,Bs2} = F(V0, Bs1, Ieval),
+ eval_map_fields(Fs, Bs2, Ieval0, F, [{map_assoc,K,V}|Acc]);
+eval_map_fields([{map_field_exact,Line,K0,V0}|Fs], Bs0, Ieval0, F, Acc) ->
+ Ieval = Ieval0#ieval{line=Line},
+ {value,K,Bs1} = F(K0, Bs0, Ieval),
+ {value,V,Bs2} = F(V0, Bs1, Ieval),
+ eval_map_fields(Fs, Bs2, Ieval0, F, [{map_exact,K,V}|Acc]);
+eval_map_fields([], Bs, _Ieval, _F, Acc) ->
+ {lists:reverse(Acc),Bs}.
+
%% match(Pattern,Term,Bs) -> {match,Bs} | nomatch
match(Pat, Term, Bs) ->
try match1(Pat, Term, Bs, Bs)
@@ -1506,6 +1565,8 @@ match1({cons,_,H,T}, [H1|T1], Bs0, BBs) ->
match1({tuple,_,Elts}, Tuple, Bs, BBs)
when length(Elts) =:= tuple_size(Tuple) ->
match_tuple(Elts, Tuple, 1, Bs, BBs);
+match1({map,_,Fields}, Map, Bs, BBs) when is_map(Map) ->
+ match_map(Fields, Map, Bs, BBs);
match1({bin,_,Fs}, B, Bs0, BBs) when is_bitstring(B) ->
try eval_bits:match_bits(Fs, B, Bs0, BBs,
match_fun(BBs),
@@ -1529,6 +1590,17 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) ->
match_tuple([], _, _, Bs, _BBs) ->
{match,Bs}.
+match_map([{map_field_exact,_,K0,Pat}|Fs], Map, Bs0, BBs) ->
+ {value,K,BBs} = expr(K0, BBs, #ieval{}),
+ case maps:find(K, Map) of
+ {ok,Value} ->
+ {match,Bs} = match1(Pat, Value, Bs0, BBs),
+ match_map(Fs, Map, Bs, BBs);
+ error -> throw(nomatch)
+ end;
+match_map([], _, Bs, _BBs) ->
+ {match,Bs}.
+
head_match([Par|Pars], [Arg|Args], Bs0, BBs) ->
try match1(Par, Arg, Bs0, BBs) of
{match,Bs} -> head_match(Pars, Args, Bs, BBs)
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 9806692afc..266cf239dd 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -194,6 +194,11 @@ pattern({cons,Line,H0,T0}) ->
pattern({tuple,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{tuple,Line,Ps1};
+pattern({map,Line,Fs0}) ->
+ Fs1 = lists:map(fun ({map_field_exact,L,K,V}) ->
+ {map_field_exact,L,expr(K, false),pattern(V)}
+ end, Fs0),
+ {map,Line,Fs1};
pattern({op,_,'-',{integer,Line,I}}) ->
{value,Line,-I};
pattern({op,_,'+',{integer,Line,I}}) ->
@@ -262,6 +267,8 @@ guard_test({string,Line,_}) -> {value,Line,false};
guard_test({nil,Line}) -> {value,Line,false};
guard_test({cons,Line,_,_}) -> {value,Line,false};
guard_test({tuple,Line,_}) -> {value,Line,false};
+guard_test({map,Line,_}) -> {value,Line,false};
+guard_test({map,Line,_,_}) -> {value,Line,false};
guard_test({bin,Line,_}) -> {value,Line,false}.
gexpr({var,Line,V}) -> {var,Line,V};
@@ -279,6 +286,13 @@ gexpr({cons,Line,H0,T0}) ->
gexpr({tuple,Line,Es0}) ->
Es1 = gexpr_list(Es0),
{tuple,Line,Es1};
+gexpr({map,Line,Fs0}) ->
+ Fs1 = map_fields(Fs0, fun gexpr/1),
+ {map,Line,Fs1};
+gexpr({map,Line,E0,Fs0}) ->
+ E1 = gexpr(E0),
+ Fs1 = map_fields(Fs0, fun gexpr/1),
+ {map,Line,E1,Fs1};
gexpr({bin,Line,Flds0}) ->
Flds = gexpr_list(Flds0),
{bin,Line,Flds};
@@ -341,6 +355,13 @@ expr({cons,Line,H0,T0}, _Lc) ->
expr({tuple,Line,Es0}, _Lc) ->
Es1 = expr_list(Es0),
{tuple,Line,Es1};
+expr({map,Line,Fs0}, _Lc) ->
+ Fs1 = map_fields(Fs0),
+ {map,Line,Fs1};
+expr({map,Line,E0,Fs0}, _Lc) ->
+ E1 = expr(E0, false),
+ Fs1 = map_fields(Fs0),
+ {map,Line,E1,Fs1};
expr({block,Line,Es0}, Lc) ->
%% Unfold block into a sequence.
Es1 = exprs(Es0, Lc),
@@ -436,7 +457,7 @@ expr({lc,Line,E0,Gs0}, _Lc) -> %R8.
({b_generate,L,P0,Qs}) -> %R12.
{b_generate,L,expr(P0, false),expr(Qs, false)};
(Expr) ->
- case is_guard(Expr) of
+ case erl_lint:is_guard_test(Expr) of
true -> {guard,guard([[Expr]])};
false -> expr(Expr, false)
end
@@ -448,7 +469,7 @@ expr({bc,Line,E0,Gs0}, _Lc) -> %R12.
({b_generate,L,P0,Qs}) -> %R12.
{b_generate,L,expr(P0, false),expr(Qs, false)};
(Expr) ->
- case is_guard(Expr) of
+ case erl_lint:is_guard_test(Expr) of
true -> {guard,guard([[Expr]])};
false -> expr(Expr, false)
end
@@ -491,42 +512,6 @@ expr({bin_element,Line,Expr,Size,Type}, _Lc) ->
expr(Other, _Lc) ->
exit({?MODULE,{unknown_expr,Other}}).
-%% is_guard(Expression) -> true | false.
-%% Test if a general expression is a guard test or guard BIF.
-%% Cannot use erl_lint here as sys_pre_expand has transformed source.
-
-is_guard({op,_,Op,L,R}) ->
- erl_internal:comp_op(Op, 2) andalso is_gexpr_list([L,R]);
-is_guard({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) ->
- Arity = length(As),
- (erl_internal:guard_bif(Test, Arity) orelse
- erl_internal:old_type_test(Test, Arity)) andalso is_gexpr_list(As);
-is_guard({atom,_,true}) -> true;
-is_guard(_) -> false.
-
-is_gexpr({var,_,_}) -> true;
-is_gexpr({atom,_,_}) -> true;
-is_gexpr({integer,_,_}) -> true;
-is_gexpr({char,_,_}) -> true;
-is_gexpr({float,_,_}) -> true;
-is_gexpr({string,_,_}) -> true;
-is_gexpr({nil,_}) -> true;
-is_gexpr({cons,_,H,T}) -> is_gexpr_list([H,T]);
-is_gexpr({tuple,_,Es}) -> is_gexpr_list(Es);
-is_gexpr({call,_,{remote,_,{atom,_,erlang},{atom,_,F}},As}) ->
- Ar = length(As),
- case erl_internal:guard_bif(F, Ar) of
- true -> is_gexpr_list(As);
- false -> erl_internal:arith_op(F, Ar) andalso is_gexpr_list(As)
- end;
-is_gexpr({op,_,Op,A}) ->
- erl_internal:arith_op(Op, 1) andalso is_gexpr(A);
-is_gexpr({op,_,Op,A1,A2}) ->
- erl_internal:arith_op(Op, 2) andalso is_gexpr_list([A1,A2]);
-is_gexpr(_) -> false.
-
-is_gexpr_list(Es) -> lists:all(fun (E) -> is_gexpr(E) end, Es).
-
consify([A|As]) ->
{cons,0,A,consify(As)};
consify([]) -> {value,0,[]}.
@@ -550,6 +535,15 @@ fun_clauses([{clause,L,H,G,B}|Cs]) ->
[{clause,L,head(H),guard(G),exprs(B, true)}|fun_clauses(Cs)];
fun_clauses([]) -> [].
+map_fields(Fs) ->
+ map_fields(Fs, fun (E) -> expr(E, false) end).
+
+map_fields([{map_field_assoc,L,N,V}|Fs], F) ->
+ [{map_field_assoc,L,F(N),F(V)}|map_fields(Fs)];
+map_fields([{map_field_exact,L,N,V}|Fs], F) ->
+ [{map_field_exact,L,F(N),F(V)}|map_fields(Fs)];
+map_fields([], _) -> [].
+
%% new_var_name() -> VarName.
new_var_name() ->
diff --git a/lib/debugger/src/dbg_wx_filedialog_win.erl b/lib/debugger/src/dbg_wx_filedialog_win.erl
index c8ecb7b5d4..ea34295067 100644
--- a/lib/debugger/src/dbg_wx_filedialog_win.erl
+++ b/lib/debugger/src/dbg_wx_filedialog_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -242,16 +242,13 @@ handle_event(#wx{event=#wxList{itemIndex=Index}},
{noreply, State0}
end;
-handle_event(#wx{event=#wxCommand{type=command_text_updated, cmdString=Wanted}},
+handle_event(#wx{event=#wxCommand{type=command_text_updated, cmdString=Wanted}},
State = #state{ptext=Previous, completion=Comp}) ->
case Previous =:= undefined orelse lists:prefix(Wanted, Previous) of
- true ->
- case Comp of
- {Temp,_} -> wxWindow:destroy(Temp);
- undefined -> ok
- end,
+ true ->
+ destroy_completion(Comp),
{noreply, State#state{ptext=Wanted,completion=undefined}};
- false ->
+ false ->
{noreply, show_completion(Wanted, State)}
end;
@@ -310,8 +307,7 @@ handle_event(#wx{event=#wxSize{size={Width,_}}}, State = #state{list=LC}) ->
end),
{noreply, State};
-handle_event(Event,State) ->
- io:format("~p Got ~p ~n",[self(), Event]),
+handle_event(_Event,State) ->
{noreply, State}.
handle_info(_Msg, State) ->
@@ -419,8 +415,9 @@ show_completion(Wanted, State = #state{text=TC, win=Win, list=LC, completion=Com
end.
destroy_completion(undefined) -> ok;
-destroy_completion({Window, _}) ->
+destroy_completion({Window, _LB}) ->
Parent = wxWindow:getParent(Window),
+ wxWindow:hide(Window),
wxWindow:destroy(Window),
wxWindow:refresh(Parent).
diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl
index 4a01492cf5..4ab03985d3 100644
--- a/lib/debugger/src/dbg_wx_mon.erl
+++ b/lib/debugger/src/dbg_wx_mon.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -148,7 +148,7 @@ init2(CallingPid, Mode, SFile, GS) ->
win = Win,
focus = undefined,
- coords = {20,20},
+ coords = {-1,-1},
intdir = element(2, file:get_cwd()),
pinfos = [],
diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl
index 0071b27027..d94eb14937 100644
--- a/lib/debugger/src/dbg_wx_mon_win.erl
+++ b/lib/debugger/src/dbg_wx_mon_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -617,8 +617,6 @@ handle_event(#wx{userData=Data,
_WinInfo) ->
Data;
handle_event(_Event, _WinInfo) ->
-%% FIXME
- io:format("Ev: ~p~n",[_Event]),
ignore.
%%====================================================================
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 1ac796bb4c..4438466bb0 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -125,7 +125,7 @@ init(Pid, Parent, Meta, TraceWin, BackTrace, Strings) ->
dbg_wx_winman:insert(Title, Window),
%% Initial process state
- State1 = #state{win=Win, coords={0,0}, pid=Pid, meta=Meta,
+ State1 = #state{win=Win, coords={-1,-1}, pid=Pid, meta=Meta,
status={idle,null,null},
stack={1,1}, strings=[str_on]},
diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl
index 6242b9d0e0..fc7ffc0d56 100644
--- a/lib/debugger/src/dbg_wx_view.erl
+++ b/lib/debugger/src/dbg_wx_view.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -72,7 +72,7 @@ init(GS, Env, Mod, Title) ->
Win2,
int:all_breaks(Mod)),
- try loop(#state{gs=GS, win=Win3, coords={0,0}, mod=Mod})
+ try loop(#state{gs=GS, win=Win3, coords={-1,-1}, mod=Mod})
catch _E:normal ->
exit(normal);
_E:_R ->
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index bdd671cff1..2755db64b8 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -630,14 +630,13 @@ find_beam(Mod, Src) ->
File = filename:join(SrcDir, BeamFile),
case is_file(File) of
true -> File;
- false -> find_beam_1(Mod, BeamFile, SrcDir)
+ false -> find_beam_1(BeamFile, SrcDir)
end.
-find_beam_1(Mod, BeamFile, SrcDir) ->
+find_beam_1(BeamFile, SrcDir) ->
RootDir = filename:dirname(SrcDir),
EbinDir = filename:join(RootDir, "ebin"),
CodePath = [EbinDir | code:get_path()],
- BeamFile = atom_to_list(Mod) ++ code:objfile_extension(),
lists:foldl(fun(_, Beam) when is_list(Beam) -> Beam;
(Dir, error) ->
File = filename:join(Dir, BeamFile),
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index 5aa290c61b..9a0ce14ecd 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -44,6 +44,7 @@ MODULES= \
fun_SUITE \
lc_SUITE \
line_number_SUITE \
+ map_SUITE \
record_SUITE \
trycatch_SUITE \
test_lib \
diff --git a/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
index be9312b68f..ba60ed6fb3 100644
--- a/lib/debugger/test/erl_eval_SUITE.erl
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -64,6 +64,7 @@ config(priv_dir,_) ->
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
end_per_testcase(_Case, Config) ->
diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl
new file mode 100644
index 0000000000..e9f4ea1fad
--- /dev/null
+++ b/lib/debugger/test/map_SUITE.erl
@@ -0,0 +1,1002 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(map_SUITE).
+
+%% Copied from map_SUITE in erts.
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2
+ ]).
+
+-export([
+ t_build_and_match_literals/1,
+ t_update_literals/1,t_match_and_update_literals/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1,t_update_exact/1,
+ t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
+ t_guard_receive/1, t_guard_fun/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ %t_size/1,
+ t_map_size/1,
+
+ %% Specific Map BIFs
+ t_bif_map_get/1,
+ t_bif_map_find/1,
+ t_bif_map_is_key/1,
+ t_bif_map_keys/1,
+ t_bif_map_merge/1,
+ t_bif_map_new/1,
+ t_bif_map_put/1,
+ t_bif_map_remove/1,
+ t_bif_map_update/1,
+ t_bif_map_values/1,
+ t_bif_map_to_list/1,
+ t_bif_map_from_list/1,
+
+ %% erlang
+ t_erlang_hash/1,
+ t_map_encode_decode/1,
+
+ %% maps module not bifs
+ t_maps_fold/1,
+ t_maps_map/1,
+ t_maps_size/1,
+ t_maps_without/1,
+
+ %% misc
+ t_pdict/1,
+ t_ets/1,
+ t_dets/1
+ ]).
+
+-include_lib("stdlib/include/ms_transform.hrl").
+
+suite() -> [].
+
+all() -> [
+ t_build_and_match_literals,
+ t_update_literals, t_match_and_update_literals,
+ t_update_map_expressions,
+ t_update_assoc,t_update_exact,
+ t_guard_bifs, t_guard_sequence, t_guard_update,
+ t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_sort_literals,
+
+ %% Specific Map BIFs
+ t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
+ t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
+ t_bif_map_put,
+ t_bif_map_remove, t_bif_map_update,
+ t_bif_map_values,
+ t_bif_map_to_list, t_bif_map_from_list,
+
+ %% erlang
+ t_erlang_hash, t_map_encode_decode,
+ t_map_size,
+
+ %% maps module
+ t_maps_fold, t_maps_map,
+ t_maps_size, t_maps_without,
+
+
+ %% Other functions
+ t_pdict,
+ t_ets
+ ].
+
+groups() -> [].
+
+init_per_suite(Config) ->
+ test_lib:interpret(?MODULE),
+ Config.
+
+end_per_suite(_Config) -> ok.
+
+init_per_group(_GroupName, Config) -> Config.
+end_per_group(_GroupName, Config) -> Config.
+
+%% tests
+
+t_build_and_match_literals(Config) when is_list(Config) ->
+ #{} = id(#{}),
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% error case
+ %V = 32,
+ %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ ok.
+
+
+%% Tests size(Map).
+%% not implemented, perhaps it shouldn't be either
+
+%t_size(Config) when is_list(Config) ->
+% 0 = size(#{}),
+% 1 = size(#{a=>1}),
+% 1 = size(#{a=>#{a=>1}}),
+% 2 = size(#{a=>1, b=>2}),
+% 3 = size(#{a=>1, b=>2, b=>"3"}),
+% ok.
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 1),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "c" => 2}, 2),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+% test map updates without matching
+t_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
+
+% test map updates with matching
+t_match_and_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+
+t_update_map_expressions(Config) when is_list(Config) ->
+ M = maps:new(),
+ #{ a := 1 } = M#{a => 1},
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+
+t_update_assoc(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+
+ ok.
+
+t_update_exact(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ %% M2 = M0#{3=>wrong,3.0:=new}, %% FIXME
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+
+ ok.
+
+t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ ok.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+t_guard_sequence(Config) when is_list(Config) ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+
+t_guard_update(Config) when is_list(Config) ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(_, _) -> error.
+
+t_guard_receive(Config) when is_list(Config) ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <<B1/binary, B2/binary>>,
+
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
+
+
+t_list_comprehension(Config) when is_list(Config) ->
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
+
+t_guard_fun(Config) when is_list(Config) ->
+ F1 = fun
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
+ ok.
+
+
+t_map_sort_literals(Config) when is_list(Config) ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = #{ a => 1 } < id(#{ b => 1}),
+ false = #{ b => 1 } < id(#{ a => 1}),
+ true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}),
+ true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
+ false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
+ false = #{ 1 => 1 } < id(#{ 1.0 => 1}),
+ false = #{ 1.0 => 1 } < id(#{ 1 => 1}),
+
+ %% value order
+ true = #{ a => 1 } < id(#{ a => 2}),
+ false = #{ a => 2 } < id(#{ a => 1}),
+ false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}),
+ true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}),
+
+ true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+%% BIFs
+t_bif_map_get(Config) when is_list(Config) ->
+
+ 1 = maps:get(a, #{ a=> 1}),
+ 2 = maps:get(b, #{ a=> 1, b => 2}),
+ "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}),
+ "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])),
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})),
+ ok.
+
+t_bif_map_find(Config) when is_list(Config) ->
+
+ {ok, 1} = maps:find(a, #{ a=> 1}),
+ {ok, 2} = maps:find(b, #{ a=> 1, b => 2}),
+ {ok, "int"} = maps:find(1, #{ 1 => "int"}),
+ {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}),
+
+ {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
+ {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ error = maps:find(a,#{}),
+ error = maps:find(a,#{b=>1, c=>2}),
+ error = maps:find(1.0, #{ 1 => "int"}),
+ error = maps:find(1, #{ 1.0 => "float"}),
+ error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+
+
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))),
+ ok.
+
+
+t_bif_map_is_key(Config) when is_list(Config) ->
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(int, M1),
+ true = maps:is_key(<<"key">>, M1),
+ true = maps:is_key(4, M1),
+
+ false = maps:is_key(5, M1),
+ false = maps:is_key(<<"key2">>, M1),
+ false = maps:is_key("h", M1),
+ false = maps:is_key("hello", M1),
+ false = maps:is_key(atom, M1),
+ false = maps:is_key(any, id(#{})),
+
+ false = maps:is_key("hi", maps:remove("hi", M1)),
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(1, maps:put(1, "number", M1)),
+ false = maps:is_key(1.0, maps:put(1, "number", M1)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))),
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))),
+ ok.
+
+t_bif_map_keys(Config) when is_list(Config) ->
+ [] = maps:keys(#{}),
+
+ [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ [4,int,"hi",<<"key">>] = maps:keys(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)),
+ ok.
+
+t_bif_map_new(Config) when is_list(Config) ->
+ #{} = maps:new(),
+ 0 = erlang:map_size(maps:new()),
+ ok.
+
+t_bif_map_merge(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:merge(#{},#{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}),
+
+ M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer },
+
+ #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0),
+
+ #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )),
+
+ ok.
+
+
+t_bif_map_put(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}),
+
+ ["hi"] = maps:keys(M1),
+ ["hello"] = maps:values(M1),
+
+ M2 = #{ int := 3 } = maps:put(int, 3, M1),
+
+ [int,"hi"] = maps:keys(M2),
+ [3,"hello"] = maps:values(M2),
+
+ M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),
+
+ [int,"hi",<<"key">>] = maps:keys(M3),
+ [3,"hello",<<"value">>] = maps:values(M3),
+
+ M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),
+
+ [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4),
+ [wat,3,"hello",<<"value">>] = maps:values(M4),
+
+ M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5),
+ [number,wat,3,"hello",<<"value">>] = maps:values(M5),
+
+ M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6),
+ [number,wat,3,"hello",<<"other value">>] = maps:values(M6),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)),
+ ok.
+
+t_bif_map_remove(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:remove(some_key, #{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = maps:remove("hi", M0),
+ [4,18446744073709551629,int,<<"key">>] = maps:keys(M1),
+ [number,wat,3,<<"value">>] = maps:values(M1),
+
+ M2 = maps:remove(int, M1),
+ [4,18446744073709551629,<<"key">>] = maps:keys(M2),
+ [number,wat,<<"value">>] = maps:values(M2),
+
+ M3 = maps:remove(<<"key">>, M2),
+ [4,18446744073709551629] = maps:keys(M3),
+ [number,wat] = maps:values(M3),
+
+ M4 = maps:remove(18446744073709551629, M3),
+ [4] = maps:keys(M4),
+ [number] = maps:values(M4),
+
+ M5 = maps:remove(4, M4),
+ [] = maps:keys(M5),
+ [] = maps:values(M5),
+
+ M0 = maps:remove(5,M0),
+ M0 = maps:remove("hi there",M0),
+
+ #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)),
+ ok.
+
+t_bif_map_update(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0),
+
+ #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)),
+
+ ok.
+
+
+
+t_bif_map_values(Config) when is_list(Config) ->
+
+ [] = maps:values(#{}),
+
+ [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
+ [number,3,"hello2",<<"value2">>] = maps:values(M2),
+ [number,3,"hello",<<"value">>] = maps:values(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)),
+ ok.
+
+t_erlang_hash(Config) when is_list(Config) ->
+
+ ok = t_bif_erlang_phash2(),
+ ok = t_bif_erlang_phash(),
+ ok = t_bif_erlang_hash(),
+
+ ok.
+
+t_bif_erlang_phash2() ->
+
+ 39679005 = erlang:phash2(#{}),
+ 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
+ 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
+ 14363616 = erlang:phash2(#{ 1 => a }),
+ 51612236 = erlang:phash2(#{ a => 1 }),
+
+ 37468437 = erlang:phash2(#{{} => <<>>}),
+ 44049159 = erlang:phash2(#{<<>> => {}}),
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 118679416 = erlang:phash2(M0),
+ 51612236 = erlang:phash2(M1),
+ 118679416 = erlang:phash2(M2),
+ ok.
+
+t_bif_erlang_phash() ->
+ Sz = 1 bsl 32,
+ 268440612 = erlang:phash(#{},Sz),
+ 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 1394238263 = erlang:phash(#{ 1 => a },Sz),
+ 4066388227 = erlang:phash(#{ a => 1 },Sz),
+
+ 1578050717 = erlang:phash(#{{} => <<>>},Sz),
+ 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 3590546636 = erlang:phash(M0,Sz),
+ 4066388227 = erlang:phash(M1,Sz),
+ 3590546636 = erlang:phash(M2,Sz),
+ ok.
+
+t_bif_erlang_hash() ->
+ Sz = 1 bsl 27 - 1,
+ 5158 = erlang:hash(#{},Sz),
+ 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 126071654 = erlang:hash(#{ 1 => a },Sz),
+ 126426236 = erlang:hash(#{ a => 1 },Sz),
+
+ 101655720 = erlang:hash(#{{} => <<>>},Sz),
+ 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 38260486 = erlang:hash(M0,Sz),
+ 126426236 = erlang:hash(M1,Sz),
+ 38260486 = erlang:hash(M2,Sz),
+ ok.
+
+
+t_map_encode_decode(Config) when is_list(Config) ->
+ <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}),
+ Pairs = [
+ {a,b},{"key","values"},{<<"key">>,<<"value">>},
+ {1,b},{[atom,1],{<<"wat">>,1,2,3}},
+ {aa,"values"},
+ {1 bsl 64 + (1 bsl 50 - 1), sc1},
+ {99, sc2},
+ {1 bsl 65 + (1 bsl 51 - 1), sc3},
+ {88, sc4},
+ {1 bsl 66 + (1 bsl 52 - 1), sc5},
+ {77, sc6},
+ {1 bsl 67 + (1 bsl 53 - 1), sc3},
+ {75, sc6}, {-10,sc8},
+ {<<>>, sc9}, {3.14158, sc10},
+ {[3.14158], sc11}, {more_atoms, sc12},
+ {{more_tuples}, sc13}, {self(), sc14},
+ {{},{}},{[],[]}
+ ],
+ ok = map_encode_decode_and_match(Pairs,[],#{}),
+
+ %% check sorting
+
+ %% literally #{ b=>2, a=>1 } in the internal order
+ #{ a:=1, b:=2 } =
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>),
+
+
+ %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
+ #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
+ 107,0,2,104,105, % "hi" :: list()
+ 100,0,1,97, % a :: atom()
+ 100,0,1,98, % b :: atom()
+ 107,0,5,118,97,108,117,101, % "value" :: list()
+ 97,33, % 33 :: integer()
+ 97,55 % 55 :: integer()
+ >>),
+
+
+ %% error cases
+ %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>>
+ %% which is: #{ a=>1, b=>1 }
+
+ %% uniqueness violation
+ %% literally #{ a=>1, "hi"=>"value", a=>2 }
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)),
+
+ %% bad size (too large)
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)),
+
+ %% bad size (too small) .. should fail just truncate it .. weird.
+ %% possibly change external format so truncated will be #{a:=1}
+ #{ a:=b } =
+ erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),
+
+ ok.
+
+map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
+ M1 = maps:put(K,V,M0),
+ B0 = erlang:term_to_binary(M1),
+ Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
+ %% sort Ks and Vs according to term spec, then match it
+ ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]),
+ %% decode and match it
+ M1 = erlang:binary_to_term(B0),
+ map_encode_decode_and_match(Pairs,Ls,M1);
+map_encode_decode_and_match([],_,_) -> ok.
+
+match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) ->
+ match_encoded_map(Encoded,Items);
+match_encoded_map(_,_,_) -> no_match_size.
+
+match_encoded_map(<<>>,[]) -> ok;
+match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
+ Size = erlang:byte_size(Item),
+ <<EncodedTerm:Size/binary, Bin1/binary>> = Bin,
+ EncodedTerm = Item, %% Asssert
+ match_encoded_map(Bin1,Items).
+
+
+t_bif_map_to_list(Config) when is_list(Config) ->
+ [] = maps:to_list(#{}),
+ [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}),
+ [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
+ [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}),
+
+ [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
+ <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))),
+ ok.
+
+
+t_bif_map_from_list(Config) when is_list(Config) ->
+ #{} = maps:from_list([]),
+ A = maps:from_list([]),
+ 0 = erlang:map_size(A),
+
+ #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]),
+ #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]),
+ #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]),
+
+ #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]),
+
+ #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} =
+ maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),
+
+ #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} =
+ maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
+ {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
+ ok.
+
+%% Maps module, not BIFs
+t_maps_fold(_Config) ->
+ Vs = lists:seq(1,100),
+ M = maps:from_list([{{k,I},{v,I}}||I<-Vs]),
+
+ %% fold
+ 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M),
+
+ ok.
+
+t_maps_map(_Config) ->
+ Vs = lists:seq(1,100),
+ M1 = maps:from_list([{I,I}||I<-Vs]),
+ M2 = maps:from_list([{I,{token,I}}||I<-Vs]),
+
+ M2 = maps:map(fun(_K,V) -> {token,V} end, M1),
+ ok.
+
+t_maps_size(_Config) ->
+ Vs = lists:seq(1,100),
+ lists:foldl(fun(I,M) ->
+ M1 = maps:put(I,I,M),
+ I = maps:size(M1),
+ M1
+ end, #{}, Vs),
+ ok.
+
+
+t_maps_without(_Config) ->
+ Ki = [11,22,33,44,55,66,77,88,99],
+ M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
+ M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]),
+ M1 = maps:without([{k,I}||I <- Ki],M0),
+ ok.
+
+
+%% MISC
+t_pdict(_Config) ->
+
+ put(#{ a => b, b => a},#{ c => d}),
+ put(get(#{ a => b, b => a}),1),
+ 1 = get(#{ c => d}),
+ #{ c := d } = get(#{ a => b, b => a}).
+
+t_ets(_Config) ->
+
+ Tid = ets:new(map_table,[]),
+
+ [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)],
+
+
+ [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }),
+
+ %% Test equal
+ [3,4] = lists:sort(
+ ets:select(Tid,[{{'$1','$2'},
+ [{'or',{'==','$1',#{ 3 => -3 }},
+ {'==','$1',#{ 4 => -4 }}}],
+ ['$2']}])),
+ %% Test match
+ [30,50] = lists:sort(
+ ets:select(Tid,
+ [{{#{ 30 => -30}, '$1'},[],['$1']},
+ {{#{ 50 => -50}, '$1'},[],['$1']}]
+ )),
+
+ ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}),
+
+ %% Test equal with map of different size
+ [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]),
+
+ %% Test match with map of different size
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),
+
+ %%% Test match with don't care value
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),
+
+ %% Test is_map bif
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{not_a_map,2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{{nope,a,tuple},2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+
+ %% Test map_size bif
+ [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}],
+ [{map_size,'$1'}]}]),
+
+ true = ets:delete(Tid,#{50 => -50}),
+ [] = ets:lookup(Tid,#{50 => -50}),
+
+ ets:delete(Tid),
+ ok.
+
+t_dets(_Config) ->
+ ok.
+
+getmsg(_Tracer) ->
+ receive V -> V after 100 -> timeout end.
+
+trace_collector(Msg,Parent) ->
+ io:format("~p~n",[Msg]),
+ Parent ! Msg,
+ Parent.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 2a631c3010..a92b890a80 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -229,7 +229,7 @@
match.</item>
<tag><c><![CDATA[-Wno_opaque]]></c></tag>
<item>Suppress warnings for violations of opaqueness of data types.</item>
- <tag><c><![CDATA[-Wno_behaviours]]></c>***</tag>
+ <tag><c><![CDATA[-Wno_behaviours]]></c></tag>
<item>Suppress warnings about behaviour callbacks which drift from the
published recommended interfaces.</item>
<tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index 9222a28a77..0d048b607e 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,17 +29,19 @@
dialyzer_cl_parse,
dialyzer_codeserver,
dialyzer_contracts,
+ dialyzer_coordinator,
dialyzer_dataflow,
dialyzer_dep,
dialyzer_explanation,
- dialyzer_gui,
dialyzer_gui_wx,
dialyzer_options,
dialyzer_plt,
dialyzer_races,
dialyzer_succ_typings,
dialyzer_typesig,
- dialyzer_utils]},
+ dialyzer_utils,
+ dialyzer_timing,
+ dialyzer_worker]},
{registered, []},
{applications, [compiler, gs, hipe, kernel, stdlib, wx]},
{env, []}]}.
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 33fa107019..b00e0465e0 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -58,10 +58,12 @@
t_fun_range/2, t_integer/0, t_integers/1,
t_is_any/1, t_is_atom/1, t_is_atom/2, t_is_any_atom/3,
t_is_boolean/2,
- t_is_integer/2, t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
+ t_is_integer/2, t_is_list/1,
+ t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
t_is_number/2, t_is_reference/2, t_is_pid/2, t_is_port/2,
t_is_unit/1,
- t_limit/2, t_list/0, t_maybe_improper_list/0, t_module/0,
+ t_limit/2, t_list/0, t_list_elements/2,
+ t_maybe_improper_list/0, t_module/0,
t_none/0, t_non_neg_integer/0, t_number/0, t_number_vals/2,
t_pid/0, t_port/0, t_product/1, t_reference/0,
t_to_string/2, t_to_tlist/1,
@@ -293,6 +295,7 @@ traverse(Tree, Map, State) ->
t_is_any(ArgType)
orelse t_is_simple(ArgType, State)
orelse is_call_to_send(Arg)
+ orelse is_lc_simple_list(Arg, ArgType, State)
of
true -> % do not warn in these cases
State1;
@@ -2302,6 +2305,9 @@ bind_guard_list([], Map, _Env, _Eval, _State, Acc) ->
signal_guard_fail(Eval, Guard, ArgTypes, State) ->
signal_guard_failure(Eval, Guard, ArgTypes, fail, State).
+-spec signal_guard_fatal_fail(eval(), cerl:c_call(), [erl_types:erl_type()],
+ state()) -> no_return().
+
signal_guard_fatal_fail(Eval, Guard, ArgTypes, State) ->
signal_guard_failure(Eval, Guard, ArgTypes, fatal_fail, State).
@@ -2710,6 +2716,13 @@ is_call_to_send(Tree) ->
andalso (Arity =:= 2)
end.
+is_lc_simple_list(Tree, TreeType, State) ->
+ Opaques = State#state.opaques,
+ Ann = cerl:get_ann(Tree),
+ lists:member(list_comprehension, Ann)
+ andalso t_is_list(TreeType)
+ andalso t_is_simple(t_list_elements(TreeType, Opaques), State).
+
filter_match_fail([Clause] = Cls) ->
Body = cerl:clause_body(Clause),
case cerl:type(Body) of
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index b4b3d5a092..70e14781f7 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -2535,8 +2535,10 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
erase_type(Key, Map);
false ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
- [?debug("LimitedVal ~s\n", [format_type(LimitedVal)]) ||
- not is_equal(LimitedVal, Val)],
+ case is_equal(LimitedVal, Val) of
+ true -> ok;
+ false -> ?debug("LimitedVal ~s\n", [format_type(LimitedVal)])
+ end,
case dict:find(Key, Map) of
{ok, Value} ->
case is_equal(Value, LimitedVal) of
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
index 27cabc8ef8..f43e04dd59 100644
--- a/lib/dialyzer/test/Makefile
+++ b/lib/dialyzer/test/Makefile
@@ -11,6 +11,7 @@ AUXILIARY_FILES=\
dialyzer_test_constants.hrl\
dialyzer_common.erl\
file_utils.erl\
+ dialyzer_SUITE.erl\
plt_SUITE.erl
# ----------------------------------------------------
diff --git a/lib/dialyzer/test/dialyzer_SUITE.erl b/lib/dialyzer/test/dialyzer_SUITE.erl
new file mode 100644
index 0000000000..63214c915d
--- /dev/null
+++ b/lib/dialyzer/test/dialyzer_SUITE.erl
@@ -0,0 +1,73 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(dialyzer_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+-define(application, dialyzer).
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases must be exported.
+-export([app_test/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%
+%%% Test cases starts here.
+%%%
+
+app_test(doc) ->
+ ["Test that the .app file does not contain any `basic' errors"];
+app_test(suite) ->
+ [];
+app_test(Config) when is_list(Config) ->
+ ?line ?t:app_test(dialyzer).
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options b/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..49ac917f61
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [unmatched_returns]}]}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings
new file mode 100644
index 0000000000..2784f2119e
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings
@@ -0,0 +1,5 @@
+
+lc_warnings.erl:32: Expression produces a value of type [opaque_atom_adt:opaque_atom()], but this value is unmatched
+lc_warnings.erl:43: Expression produces a value of type [array()], but this value is unmatched
+lc_warnings.erl:65: Expression produces a value of type [lc_warnings:opaque_tuple()], but this value is unmatched
+lc_warnings.erl:7: Expression produces a value of type ['ok' | {'error',atom()}], but this value is unmatched
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl
new file mode 100644
index 0000000000..cb01a8fde3
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl
@@ -0,0 +1,95 @@
+-module(lc_warnings).
+-compile([export_all]).
+
+close(Fs) ->
+ %% There should be a warning since we ignore a potential
+ %% {error,Error} return from file:close/1.
+ [file:close(F) || F <- Fs],
+
+ %% No warning because the type of unmatched return will be ['ok']
+ %% (which is a list of a simple type).
+ [ok = file:close(F) || F <- Fs],
+
+ %% Suppressed.
+ _ = [file:close(F) || F <- Fs],
+ ok.
+
+format(X) ->
+ %% No warning since the result of the list comprehension is
+ %% a list of simple.
+ [io:format("~p\n", [E]) || E <- X],
+
+ %% Warning explicitly suppressed.
+ _ = [io:format("~p\n", [E]) || E <- X],
+ ok.
+
+opaque1() ->
+ List = gen_atom(),
+ %% This is a list of an externally defined opaque type. Since
+ %% we are not allowed to peek inside opaque types, there should
+ %% be a warning (even though the type in this case happens to be
+ %% an atom).
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque2() ->
+ List = gen_array(),
+ %% This is an list of an externally defined opaque type. Since
+ %% we are not allowed to peek inside opaque types, there should
+ %% be a warning.
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque3() ->
+ List = gen_int(),
+
+ %% No warning, since we are allowed to look into the type and can
+ %% see that it is a simple type.
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque4() ->
+ List = gen_tuple(),
+
+ %% There should be a warning, since we are allowed to look inside
+ %% the opaque type and see that it is a tuple (non-simple).
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+gen_atom() ->
+ [opaque_atom_adt:atom(ok)].
+
+gen_array() ->
+ [array:new()].
+
+
+gen_int() ->
+ [opaque_int(42)].
+
+gen_tuple() ->
+ [opaque_tuple(x, 25)].
+
+-opaque opaque_int() :: integer().
+
+-spec opaque_int(integer()) -> opaque_int().
+
+opaque_int(Int) -> Int.
+
+-opaque opaque_tuple() :: {any(),any()}.
+
+-spec opaque_tuple(any(), any()) -> opaque_tuple().
+
+opaque_tuple(X, Y) ->
+ {X,Y}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl
new file mode 100644
index 0000000000..b5b51fe75b
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl
@@ -0,0 +1,9 @@
+-module(opaque_atom_adt).
+-export([atom/1]).
+
+-opaque opaque_atom() :: atom().
+
+-spec atom(atom()) -> opaque_atom().
+
+atom(Atom) ->
+ Atom.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index f5275e66b5..d0a01351f3 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -171,18 +171,33 @@ start_link(T) ->
info({gen_sctp, Sock}) ->
lists:flatmap(fun(K) -> info(K, Sock) end,
- [{socket, sockname},
- {peer, peername},
+ [{socket, socknames},
+ {peer, peernames},
{statistics, getstat}]).
info({K,F}, Sock) ->
case inet:F(Sock) of
{ok, V} ->
- [{K,V}];
+ [{K, map(F,V)}];
_ ->
[]
end.
+%% inet:{sock,peer}names/1 returns [{Addr, Port}] but the port number
+%% should be the same in each tuple. Map to a {[Addr], Port} tuple if
+%% so.
+map(K, [{_, Port} | _] = APs)
+ when K == socknames;
+ K == peernames ->
+ try [A || {A,P} <- APs, P == Port orelse throw(?MODULE)] of
+ As -> {As, Port}
+ catch
+ ?MODULE -> APs
+ end;
+
+map(_, V) ->
+ V.
+
%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -549,7 +564,7 @@ accept_peer(_, []) ->
ok;
accept_peer(Sock, Matches) ->
- {RAddrs, _} = ok(inet:peername(Sock)),
+ RAddrs = [A || {A,_} <- ok(inet:peernames(Sock))],
diameter_peer:match(RAddrs, Matches)
orelse x({accept, RAddrs, Matches}),
ok.
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 74702102f0..264a533a52 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -454,7 +454,7 @@ check_type(#tag{line = L, data = Data}, P0, Ls, Ts) ->
check_type(#t_def{type = Type}, P, Ls, Ts) ->
check_type(Type, P, Ls, Ts);
check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) ->
- check_used_type(Name, Args, P, Ls),
+ _ = check_used_type(Name, Args, P, Ls),
check_types3(Args++Ts, P, Ls);
check_type(#t_var{}, P, Ls, Ts) ->
check_types3(Ts, P, Ls);
diff --git a/lib/gs/doc/src/gs.xml b/lib/gs/doc/src/gs.xml
index e0d7fb20da..7b589d002d 100644
--- a/lib/gs/doc/src/gs.xml
+++ b/lib/gs/doc/src/gs.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -39,7 +39,7 @@
graphical user interface.
</p>
<p>
- GS is deprecated and will be removed in the R16 release.
+ GS is deprecated and will be removed in the 18.0 release.
</p>
</warning>
<p>The Graphics System, GS, is easy to learn and
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 34d701eb26..c0d73663d3 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,16 +42,17 @@ suite() ->
all() ->
[
- {group, http}
- %{group, https}
+ {group, http},
+ {group, http_limit}
+ %%{group, https}
].
groups() ->
[
{http, [], all_groups()},
- %{https, [], all_groups()},
- {http_1_1, [], [host, chunked, expect, cgi, max_clients
- ] ++ http_head() ++ http_get()},
+ %%{https, [], all_groups()},
+ {http_limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {http_1_1, [], [host, chunked, expect, cgi] ++ http_head() ++ http_get()},
{http_1_0, [], [host, cgi] ++ http_head() ++ http_get()},
{http_0_9, [], http_head() ++ http_get()}
].
@@ -95,7 +96,7 @@ init_per_group(https = Group, Config0) ->
{skip, "Could not start https apps"}
end;
-init_per_group(http = Group, Config0) ->
+init_per_group(Group, Config0) when Group == http; Group == http_limit ->
init_httpd(Group, [{type, ip_comm} | Config0]);
init_per_group(http_1_1, Config) ->
[{http_version, "HTTP/1.1"} | Config];
@@ -106,10 +107,9 @@ init_per_group(http_0_9, Config) ->
init_per_group(_, Config) ->
Config.
end_per_group(http, _Config) ->
- inets:stop();
+ ok;
end_per_group(https, _Config) ->
- ssl:stop(),
- inets:stop();
+ ssl:stop();
end_per_group(_, _) ->
ok.
@@ -119,7 +119,7 @@ init_httpd(Group, Config0) ->
{Pid, Port} = server_start(Group, server_config(Group, Config)),
[{server_pid, Pid}, {port, Port} | Config].
%%--------------------------------------------------------------------
-init_per_testcase(host = Case, Config) ->
+init_per_testcase(host, Config) ->
Prop = ?config(tc_group_properties, Config),
Name = proplists:get_value(name, Prop),
Cb = case Name of
@@ -128,45 +128,14 @@ init_per_testcase(host = Case, Config) ->
http_1_1 ->
httpd_1_1
end,
- common_init_per_test_case(Case, [{version_cb, Cb} | proplists:delete(version_cb, Config)]);
+ [{version_cb, Cb} | proplists:delete(version_cb, Config)];
+init_per_testcase(_, Config) ->
+ Config.
%% init_per_testcase(basic_auth = Case, Config) ->
%% start_mnesia(?config(node, Config)),
%% common_init_per_test_case(Case, Config);
-init_per_testcase(max_clients, Config) ->
- Pid = ?config(server_pid, Config),
- Prop = httpd:info(Pid),
- Port = proplists:get_value(port, Prop),
- TempProp = [{port, Port} | proplists:delete(port, server_config(http, Config))],
- NewProp = [{max_clients, 1} | TempProp],
- httpd:reload_config(NewProp, non_disturbing),
- Config;
-
-init_per_testcase(_Case, Config) ->
- common_init_per_test_case(_Case, Config).
-
-%%% Should be run by all test cases except max_clients, to make
-%%% sure failiure of max_clients does not affect other test cases
-common_init_per_test_case(_Case, Config) ->
- Pid = ?config(server_pid, Config),
- Prop = httpd:info(Pid),
- case proplists:get_value(max_clients, Prop, 150) of
- 150 ->
- Config;
- _ ->
- end_per_testcase(max_clients, Config)
- end.
-
-end_per_testcase(max_clients, Config) ->
- Pid = ?config(server_pid, Config),
- Prop = httpd:info(Pid),
- Port = proplists:get_value(port, Prop),
- TempProp = [{port, Port} | proplists:delete(port, server_config(http, Config))],
- NewProp = proplists:delete(max_clients, TempProp),
- httpd:reload_config(NewProp, non_disturbing),
- Config;
-
%% end_per_testcase(basic_auth, Config) ->
%% cleanup_mnesia();
end_per_testcase(_Case, _Config) ->
@@ -270,35 +239,24 @@ expect(Config) when is_list(Config) ->
httpd_1_1:expect(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-max_clients() ->
+max_clients_1_1() ->
[{doc, "Test max clients limit"}].
-max_clients(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Pid = ?config(server_pid, Config),
- ct:pal("Configurartion: ~p~n", [httpd:info(Pid)]),
- spawn(fun() -> httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /eval?httpd_example:delay(1000) ",
- Version, Host),
- [{statuscode, 200},
- {version, Version}])
- end),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
- [{statuscode, 503},
- {version, Version}]),
- receive
- after 1000 ->
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
- [{statuscode, 200},
- {version, Version}])
- end.
-
+max_clients_1_1(Config) when is_list(Config) ->
+ do_max_clients([{http_version, "HTTP/1.1"} | Config]).
+
+max_clients_1_0() ->
+ [{doc, "Test max clients limit"}].
+
+max_clients_1_0(Config) when is_list(Config) ->
+ do_max_clients([{http_version, "HTTP/1.0"} | Config]).
+
+max_clients_0_9() ->
+ [{doc, "Test max clients limit"}].
+
+max_clients_0_9(Config) when is_list(Config) ->
+ do_max_clients([{http_version, "HTTP/0.9"} | Config]).
+
esi() ->
[{doc, "Test mod_esi"}].
@@ -590,6 +548,24 @@ alias(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Internal functions -----------------------------------
%%--------------------------------------------------------------------
+do_max_clients(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ start_blocker(Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 503},
+ {version, Version}]),
+ receive
+ after 2000 ->
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {version, Version}])
+ end.
+
setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
CgiDir = filename:join(ServerRoot, "cgi-bin"),
AuthDir = filename:join(ServerRoot, "auth"),
@@ -658,6 +634,10 @@ server_config(http, Config) ->
{erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}},
{eval_script_alias, {"/eval", [httpd_example, io]}}
] ++ auth_conf(ServerRoot);
+
+server_config(http_limit, Config) ->
+ [{max_clients, 1}] ++ server_config(http, Config);
+
server_config(_, _) ->
[].
@@ -791,3 +771,24 @@ cleanup_mnesia() ->
stopped = mnesia:stop(),
mnesia:delete_schema([node()]),
ok.
+
+start_blocker(Config) ->
+ spawn(httpd_SUITE, init_blocker, [self(), Config]),
+ receive
+ blocker_start ->
+ ok
+ end.
+
+init_blocker(From, Config) ->
+ From ! blocker_start,
+ block(Config).
+
+block(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET /eval?httpd_example:delay(1000) ",
+ Version, Host),
+ [{statuscode, 200},
+ {version, Version}]).
diff --git a/lib/jinterface/doc/src/jinterface_users_guide.xml b/lib/jinterface/doc/src/jinterface_users_guide.xml
index aaf84e2e3d..5dfe5c0c6d 100644
--- a/lib/jinterface/doc/src/jinterface_users_guide.xml
+++ b/lib/jinterface/doc/src/jinterface_users_guide.xml
@@ -112,6 +112,10 @@
<cell align="left" valign="middle"><seealso marker="java/com/ericsson/otp/erlang/OtpErlangTuple">OtpErlangTuple</seealso></cell>
</row>
<row>
+ <cell align="left" valign="middle">map</cell>
+ <cell align="left" valign="middle"><seealso marker="java/com/ericsson/otp/erlang/OtpErlangMap">OtpErlangMap</seealso></cell>
+ </row>
+ <row>
<cell align="left" valign="middle">term</cell>
<cell align="left" valign="middle"><seealso marker="java/com/ericsson/otp/erlang/OtpErlangObject">OtpErlangObject</seealso></cell>
</row>
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
new file mode 100644
index 0000000000..7c1cf84e98
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -0,0 +1,293 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+package com.ericsson.otp.erlang;
+
+import java.io.Serializable;
+
+/**
+ * Provides a Java representation of Erlang maps. Maps are created from one or
+ * more arbitrary Erlang terms.
+ *
+ * <p>
+ * The arity of the map is the number of elements it contains. The keys and
+ * values can be retrieved as arrays and the value for a key can be queried.
+ *
+ */
+public class OtpErlangMap extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ private static final long serialVersionUID = -6410770117696198497L;
+
+ private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
+
+ private OtpErlangObject[] keys = NO_ELEMENTS;
+ private OtpErlangObject[] values = NO_ELEMENTS;
+
+ /**
+ * Create a map from an array of keys and an array of values.
+ *
+ * @param keys
+ * the array of terms to create the map keys from.
+ * @param values
+ * the array of terms to create the map values from.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if any array is empty (null) or contains null elements.
+ */
+ public OtpErlangMap(final OtpErlangObject[] keys,
+ final OtpErlangObject[] values) {
+ this(keys, 0, keys.length, values, 0, values.length);
+ }
+
+ /**
+ * Create a map from an array of terms.
+ *
+ * @param elems
+ * the array of terms to create the map from.
+ * @param start
+ * the offset of the first term to insert.
+ * @param vcount
+ * the number of terms to insert.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if any array is empty (null) or contains null elements.
+ */
+ public OtpErlangMap(final OtpErlangObject[] keys, final int kstart,
+ final int kcount, final OtpErlangObject[] values, final int vstart,
+ final int vcount) {
+ if (keys == null || values == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Map content can't be null");
+ } else if (kcount != vcount) {
+ throw new java.lang.IllegalArgumentException(
+ "Map keys and values must have same arity");
+ } else if (vcount < 1) {
+ this.keys = NO_ELEMENTS;
+ this.values = NO_ELEMENTS;
+ } else {
+ this.keys = new OtpErlangObject[vcount];
+ for (int i = 0; i < vcount; i++) {
+ if (keys[kstart + i] != null) {
+ this.keys[i] = keys[kstart + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Map key cannot be null (element" + (kstart + i)
+ + ")");
+ }
+ }
+ this.values = new OtpErlangObject[vcount];
+ for (int i = 0; i < vcount; i++) {
+ if (values[vstart + i] != null) {
+ this.values[i] = values[vstart + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Map value cannot be null (element" + (vstart + i)
+ + ")");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a map from a stream containing a map encoded in Erlang external
+ * format.
+ *
+ * @param buf
+ * the stream containing the encoded map.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang map.
+ */
+ public OtpErlangMap(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final int arity = buf.read_map_head();
+
+ if (arity > 0) {
+ keys = new OtpErlangObject[arity];
+ values = new OtpErlangObject[arity];
+
+ for (int i = 0; i < arity; i++) {
+ keys[i] = buf.read_any();
+ }
+ for (int i = 0; i < arity; i++) {
+ values[i] = buf.read_any();
+ }
+ } else {
+ keys = NO_ELEMENTS;
+ values = NO_ELEMENTS;
+ }
+ }
+
+ /**
+ * Get the arity of the map.
+ *
+ * @return the number of elements contained in the map.
+ */
+ public int arity() {
+ return keys.length;
+ }
+
+ /**
+ * Get the specified value from the map.
+ *
+ * @param key
+ * the key of the requested value.
+ *
+ * @return the requested value, of null if key is not a valid key.
+ */
+ public OtpErlangObject get(final OtpErlangObject key) {
+ if (key == null) {
+ return null;
+ }
+ for (int i = 0; i < keys.length; i++) {
+ if (key.equals(keys[i])) {
+ return values[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the keys from the map as an array.
+ *
+ * @return an array containing all of the map's keys.
+ */
+ public OtpErlangObject[] keys() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(keys, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get all the values from the map as an array.
+ *
+ * @return an array containing all of the map's values.
+ */
+ public OtpErlangObject[] values() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(values, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get the string representation of the map.
+ *
+ * @return the string representation of the map.
+ */
+ @Override
+ public String toString() {
+ int i;
+ final StringBuffer s = new StringBuffer();
+ final int arity = values.length;
+
+ s.append("#{");
+
+ for (i = 0; i < arity; i++) {
+ if (i > 0) {
+ s.append(",");
+ }
+ s.append(keys[i].toString());
+ s.append(" => ");
+ s.append(values[i].toString());
+ }
+
+ s.append("}");
+
+ return s.toString();
+ }
+
+ /**
+ * Convert this map to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded map should be written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ final int arity = values.length;
+
+ buf.write_map_head(arity);
+
+ for (int i = 0; i < arity; i++) {
+ buf.write_any(keys[i]);
+ }
+ for (int i = 0; i < arity; i++) {
+ buf.write_any(values[i]);
+ }
+ }
+
+ /**
+ * Determine if two maps are equal. Maps are equal if they have the same
+ * arity and all of the elements are equal.
+ *
+ * @param o
+ * the map to compare to.
+ *
+ * @return true if the maps have the same arity and all the elements are
+ * equal.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangMap)) {
+ return false;
+ }
+
+ final OtpErlangMap t = (OtpErlangMap) o;
+ final int a = arity();
+
+ if (a != t.arity()) {
+ return false;
+ }
+
+ for (int i = 0; i < a; i++) {
+ if (!keys[i].equals(t.keys[i])) {
+ return false; // early exit
+ }
+ }
+ for (int i = 0; i < a; i++) {
+ if (!values[i].equals(t.values[i])) {
+ return false; // early exit
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ protected int doHashCode() {
+ final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
+ final int a = arity();
+ hash.combine(a);
+ for (int i = 0; i < a; i++) {
+ hash.combine(keys[i].hashCode());
+ }
+ for (int i = 0; i < a; i++) {
+ hash.combine(values[i].hashCode());
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangMap newMap = (OtpErlangMap) super.clone();
+ newMap.values = values.clone();
+ return newMap;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
index 45a82d6c94..fa0fe18e95 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
@@ -85,6 +85,9 @@ public class OtpExternal {
/** The tag used for new style references */
public static final int newRefTag = 114;
+ /** The tag used for maps */
+ public static final int mapTag = 116;
+
/** The tag used for old Funs */
public static final int funTag = 117;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index 9dc1728346..0d1342d796 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -1202,6 +1202,9 @@ public class OtpInputStream extends ByteArrayInputStream {
case OtpExternal.newRefTag:
return new OtpErlangRef(this);
+ case OtpExternal.mapTag:
+ return new OtpErlangMap(this);
+
case OtpExternal.portTag:
return new OtpErlangPort(this);
@@ -1244,4 +1247,21 @@ public class OtpInputStream extends ByteArrayInputStream {
throw new OtpErlangDecodeException("Uknown data type: " + tag);
}
}
+
+ public int read_map_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ // decode the map header and get arity
+ switch (tag) {
+ case OtpExternal.mapTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid map tag: " + tag);
+ }
+
+ return arity;
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index 78f47aa32f..a78423db44 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -974,4 +974,9 @@ public class OtpOutputStream extends ByteArrayOutputStream {
write_atom(function);
write_long(arity);
}
+
+ public void write_map_head(final int arity) {
+ write1(OtpExternal.mapTag);
+ write4BE(arity);
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
index 1390542194..62fa7f990e 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
@@ -74,6 +74,7 @@ ERL = \
OtpErlangShort\
OtpErlangString\
OtpErlangTuple \
+ OtpErlangMap \
OtpErlangUInt \
OtpErlangUShort
diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl
index de8d611efc..cb725164cd 100644
--- a/lib/jinterface/test/jinterface_SUITE.erl
+++ b/lib/jinterface/test/jinterface_SUITE.erl
@@ -37,7 +37,9 @@
erl_exit_with_reason_any_term/1,
java_exit_with_reason_any_term/1,
status_handler_localStatus/1, status_handler_remoteStatus/1,
- status_handler_connAttempt/1]).
+ status_handler_connAttempt/1,
+ maps/1
+ ]).
-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
@@ -103,7 +105,8 @@ fundamental() ->
nodename, % Nodename.java
register_and_whereis, % RegisterAndWhereis.java
get_names, % GetNames.java
- boolean_atom % BooleanAtom.java
+ boolean_atom, % BooleanAtom.java
+ maps % Maps.java
].
ping() ->
@@ -675,6 +678,17 @@ status_handler_connAttempt(Config) when is_list(Config) ->
"NodeStatusHandler",
[erlang:get_cookie(),node(),?status_handler_connAttempt]).
+%%%-----------------------------------------------------------------
+maps(doc) ->
+ ["Maps.java: "
+ "Tests OtpErlangMap encoding, decoding, toString, get"];
+maps(suite) ->
+ [];
+maps(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "Maps",
+ []).
%%%-----------------------------------------------------------------
%%% INTERNAL FUNCTIONS
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
index 2a3dca463b..a15ed1aa63 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
+++ b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
@@ -46,7 +46,8 @@ JAVA_FILES = \
MboxPing.java \
MboxSendReceive.java \
MboxLinkUnlink.java \
- NodeStatusHandler.java
+ NodeStatusHandler.java \
+ Maps.java
CLASS_FILES = $(JAVA_FILES:.java=.class)
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Maps.java b/lib/jinterface/test/jinterface_SUITE_data/Maps.java
new file mode 100644
index 0000000000..136a665f23
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/Maps.java
@@ -0,0 +1,108 @@
+import java.util.Arrays;
+
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangDecodeException;
+import com.ericsson.otp.erlang.OtpErlangList;
+import com.ericsson.otp.erlang.OtpErlangLong;
+import com.ericsson.otp.erlang.OtpErlangMap;
+import com.ericsson.otp.erlang.OtpInputStream;
+import com.ericsson.otp.erlang.OtpOutputStream;
+
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+class Maps {
+
+ /*
+ * Implements test case jinterface_SUITE:maps/1
+ *
+ * Test the class OtpErlangMap
+ */
+
+ @SuppressWarnings("resource")
+ public static void main(final String argv[]) {
+
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 0 }, "#{}", 1);
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 1, 100, 0, 1, 97, 100,
+ 0, 1, 98 }, "#{a => b}", 2);
+ // make sure keys are sorted here, jinterface doesn't reorder them
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 100, 0, 1, 97,
+ 106, 97, 1 }, "#{2 => [],a => 1}", 3);
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 1, 104, 1, 97, 3, 108,
+ 0, 0, 0, 1, 100, 0, 1, 114, 106 }, "#{{3} => [r]}", 4);
+
+ try {
+ // #{2 => [],a => 1}
+ final OtpErlangMap map = new OtpErlangMap(new OtpInputStream(
+ new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 100, 0, 1,
+ 97, 106, 97, 1 }));
+
+ if (map.arity() != 2) {
+ fail(5);
+ }
+ if (!new OtpErlangLong(1).equals(map.get(new OtpErlangAtom("a")))) {
+ fail(6);
+ }
+ if (!new OtpErlangList().equals(map.get(new OtpErlangLong(2)))) {
+ fail(7);
+ }
+ if (map.get(new OtpErlangLong(1)) != null) {
+ fail(8);
+ }
+ } catch (final OtpErlangDecodeException e) {
+ fail(99);
+ }
+
+ }
+
+ @SuppressWarnings("resource")
+ private static void runTest(final byte[] in, final String out, final int err) {
+ try {
+ final OtpInputStream is = new OtpInputStream(in);
+
+ final OtpErlangMap map = new OtpErlangMap(is);
+ final String output = map.toString();
+ if (!output.equals(out)) {
+ fail("toString mismatch " + output + " <> " + out, err);
+ }
+
+ final OtpOutputStream os = new OtpOutputStream(map);
+ final byte[] outArray0 = os.toByteArray();
+ final byte[] outArray = new byte[outArray0.length + 1];
+ System.arraycopy(outArray0, 0, outArray, 1, outArray0.length);
+ outArray[0] = (byte) 131;
+ if (!Arrays.equals(in, outArray)) {
+ fail("encode error " + Arrays.toString(outArray), err);
+ }
+ } catch (final OtpErlangDecodeException e) {
+ fail("decode error " + e.getMessage(), err);
+ } catch (final Exception e) {
+ fail("error " + e.getMessage(), err);
+ }
+ }
+
+ private static void fail(final int reason) {
+ System.exit(reason);
+ }
+
+ private static void fail(final String str, final int reason) {
+ System.out.println(str);
+ System.exit(reason);
+ }
+}
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 29eaf348a9..016151891c 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -239,10 +239,19 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
<desc>
<p>Sets the value of the configuration parameter <c><anno>Par</anno></c> for
<c><anno>Application</anno></c>.</p>
- <p><c>set_env/3</c> uses the standard <c>gen_server</c> timeout
- value (5000 ms). A <c><anno>Timeout</anno></c> argument can be provided
+ <p><c>set_env/4</c> uses the standard <c>gen_server</c> timeout
+ value (5000 ms). The <c>timeout</c> option can be provided
if another timeout value is useful, for example, in situations
where the application controller is heavily loaded.</p>
+ <p>If <c>set_env/4</c> is called before the application is loaded,
+ the application environment values specified in the <c>Application.app</c>
+ file will override the ones previously set. This is also true for application
+ reloads.</p>
+ <p>The <c>persistent</c> option can be set to <c>true</c>
+ when there is a need to guarantee parameters set with <c>set_env/4</c>
+ will not be overridden by the ones defined in the application resource
+ file on load. This means persistent values will stick after the application
+ is loaded and also on application reload.</p>
<warning>
<p>Use this function only if you know what you are doing,
that is, on your own applications. It is very application
@@ -406,9 +415,11 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
<p>Removes the configuration parameter <c><anno>Par</anno></c> and its value
for <c><anno>Application</anno></c>.</p>
<p><c>unset_env/2</c> uses the standard <c>gen_server</c>
- timeout value (5000 ms). A <c><anno>Timeout</anno></c> argument can be
+ timeout value (5000 ms). The <c>timeout</c> option can be
provided if another timeout value is useful, for example, in
situations where the application controller is heavily loaded.</p>
+ <p><c>unset_env/3</c> also allows the persistent option to be passed
+ (see <c>set_env/4</c> above).</p>
<warning>
<p>Use this function only if you know what you are doing,
that is, on your own applications. It is very application
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 7b8d2940c1..b3ec9fd33d 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1751,7 +1751,7 @@
using async threads potentially makes your system volnerable to slow
client attacks. If set to true and no async threads are available,
the sendfile call will return <c>{error,einval}</c>.
- Introduced in 17.0. Default is false.</item>
+ Introduced in Erlang/OTP 17.0. Default is false.</item>
</taglist>
</p>
</desc>
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index 4e8ba1b78a..76a80553b0 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -132,7 +132,7 @@ ensure_all_started(Application, Type) ->
{ok, Started} ->
{ok, lists:reverse(Started)};
{error, Reason, Started} ->
- [stop(App) || App <- Started],
+ _ = [stop(App) || App <- Started],
{error, Reason}
end.
@@ -285,16 +285,18 @@ info() ->
set_env(Application, Key, Val) ->
application_controller:set_env(Application, Key, Val).
--spec set_env(Application, Par, Val, Timeout) -> 'ok' when
+-spec set_env(Application, Par, Val, Opts) -> 'ok' when
Application :: atom(),
Par :: atom(),
Val :: term(),
- Timeout :: timeout().
+ Opts :: [{timeout, timeout()} | {persistent, boolean()}].
set_env(Application, Key, Val, infinity) ->
- application_controller:set_env(Application, Key, Val, infinity);
+ set_env(Application, Key, Val, [{timeout, infinity}]);
set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 ->
- application_controller:set_env(Application, Key, Val, Timeout).
+ set_env(Application, Key, Val, [{timeout, Timeout}]);
+set_env(Application, Key, Val, Opts) when is_list(Opts) ->
+ application_controller:set_env(Application, Key, Val, Opts).
-spec unset_env(Application, Par) -> 'ok' when
Application :: atom(),
@@ -303,15 +305,17 @@ set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 ->
unset_env(Application, Key) ->
application_controller:unset_env(Application, Key).
--spec unset_env(Application, Par, Timeout) -> 'ok' when
+-spec unset_env(Application, Par, Opts) -> 'ok' when
Application :: atom(),
Par :: atom(),
- Timeout :: timeout().
+ Opts :: [{timeout, timeout()} | {persistent, boolean()}].
unset_env(Application, Key, infinity) ->
- application_controller:unset_env(Application, Key, infinity);
+ unset_env(Application, Key, [{timeout, infinity}]);
unset_env(Application, Key, Timeout) when is_integer(Timeout), Timeout>=0 ->
- application_controller:unset_env(Application, Key, Timeout).
+ unset_env(Application, Key, [{timeout, Timeout}]);
+unset_env(Application, Key, Opts) when is_list(Opts) ->
+ application_controller:unset_env(Application, Key, Opts).
-spec get_env(Par) -> 'undefined' | {'ok', Val} when
Par :: atom(),
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index 1a4473593a..ed13035104 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -461,14 +461,16 @@ permit_application(ApplName, Flag) ->
set_env(AppName, Key, Val) ->
- gen_server:call(?AC, {set_env, AppName, Key, Val}).
-set_env(AppName, Key, Val, Timeout) ->
- gen_server:call(?AC, {set_env, AppName, Key, Val}, Timeout).
+ gen_server:call(?AC, {set_env, AppName, Key, Val, []}).
+set_env(AppName, Key, Val, Opts) ->
+ Timeout = proplists:get_value(timeout, Opts, 5000),
+ gen_server:call(?AC, {set_env, AppName, Key, Val, Opts}, Timeout).
unset_env(AppName, Key) ->
- gen_server:call(?AC, {unset_env, AppName, Key}).
-unset_env(AppName, Key, Timeout) ->
- gen_server:call(?AC, {unset_env, AppName, Key}, Timeout).
+ gen_server:call(?AC, {unset_env, AppName, Key, []}).
+unset_env(AppName, Key, Opts) ->
+ Timeout = proplists:get_value(timeout, Opts, 5000),
+ gen_server:call(?AC, {unset_env, AppName, Key, Opts}, Timeout).
%%%-----------------------------------------------------------------
%%% call-back functions from gen_server
@@ -609,8 +611,8 @@ check_para([Else | _ParaList], AppName) ->
| {'change_application_data', _, _}
| {'permit_application', atom() | {'application',atom(),_},_}
| {'start_application', _, _}
- | {'unset_env', _, _}
- | {'set_env', _, _, _}.
+ | {'unset_env', _, _, _}
+ | {'set_env', _, _, _, _}.
-spec handle_call(calls(), {pid(), term()}, state()) ->
{'noreply', state()} | {'reply', term(), state()}.
@@ -858,13 +860,25 @@ handle_call(which_applications, _From, S) ->
end, S#state.running),
{reply, Reply, S};
-handle_call({set_env, AppName, Key, Val}, _From, S) ->
+handle_call({set_env, AppName, Key, Val, Opts}, _From, S) ->
ets:insert(ac_tab, {{env, AppName, Key}, Val}),
- {reply, ok, S};
+ case proplists:get_value(persistent, Opts, false) of
+ true ->
+ Fun = fun(Env) -> lists:keystore(Key, 1, Env, {Key, Val}) end,
+ {reply, ok, S#state{conf_data = change_app_env(S#state.conf_data, AppName, Fun)}};
+ false ->
+ {reply, ok, S}
+ end;
-handle_call({unset_env, AppName, Key}, _From, S) ->
+handle_call({unset_env, AppName, Key, Opts}, _From, S) ->
ets:delete(ac_tab, {env, AppName, Key}),
- {reply, ok, S};
+ case proplists:get_value(persistent, Opts, false) of
+ true ->
+ Fun = fun(Env) -> lists:keydelete(Key, 1, Env) end,
+ {reply, ok, S#state{conf_data = change_app_env(S#state.conf_data, AppName, Fun)}};
+ false ->
+ {reply, ok, S}
+ end;
handle_call({control_application, AppName}, {Pid, _Tag}, S) ->
Control = S#state.control,
@@ -1640,6 +1654,16 @@ merge_env([{App, AppEnv1} | T], Env2, Res) ->
merge_env([], Env2, Res) ->
Env2 ++ Res.
+%% Changes the environment for the given application
+%% If there is no application, an empty one is created
+change_app_env(Env, App, Fun) ->
+ case get_env_key(App, Env) of
+ {value, AppEnv, RestEnv} ->
+ [{App, Fun(AppEnv)} | RestEnv];
+ _ ->
+ [{App, Fun([])} | Env]
+ end.
+
%% Merges envs for an application. Env2 overrides Env1
merge_app_env(Env1, Env2) ->
merge_app_env(Env1, Env2, []).
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 9ec8a15861..ff62297f2d 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -33,7 +33,7 @@
permit_false_start_local/1, permit_false_start_dist/1, script_start/1,
nodedown_start/1, init2973/0, loop2973/0, loop5606/1]).
--export([config_change/1,
+-export([config_change/1, persistent_env/1,
distr_changed_tc1/1, distr_changed_tc2/1,
ensure_started/1, ensure_all_started/1,
shutdown_func/1, do_shutdown/1, shutdown_timeout/1]).
@@ -53,7 +53,8 @@ all() ->
load_use_cache, ensure_started, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
permit_false_start_dist, get_key, get_env, ensure_all_started,
- {group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
+ {group, distr_changed}, config_change, shutdown_func, shutdown_timeout,
+ persistent_env].
groups() ->
[{reported_bugs, [],
@@ -1987,6 +1988,50 @@ get_appls([_ | T], Res) ->
get_appls([], Res) ->
Res.
+persistent_env(suite) ->
+ [];
+persistent_env(doc) ->
+ ["Test set_env/4 and unset_env/3 with persistent true"];
+persistent_env(Conf) when is_list(Conf) ->
+ ok = application:set_env(appinc, own2, persist, [{persistent, true}]),
+ ok = application:set_env(appinc, key1, persist, [{persistent, true}]),
+
+ %% own_env1 and own2 are set in appinc
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, key1),
+
+ %% Changing the environment after loaded reflects and should persist
+ ok = application:set_env(appinc, own_env1, persist, [{persistent, true}]),
+ {ok, persist} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, key1),
+
+ %% On reload, own_env1, own2 and key1 should all persist
+ ok = application:unload(appinc),
+ ok = application:load(appinc()),
+ {ok, persist} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, key1),
+
+ %% Unset own_env1 and key1, own2 should still persist
+ ok = application:unset_env(appinc, own_env1, [{persistent, true}]),
+ ok = application:unset_env(appinc, key1, [{persistent, true}]),
+ undefined = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ undefined = application:get_env(appinc, key1),
+
+ %% own_env1 should be back to its application value on reload
+ ok = application:unload(appinc),
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ undefined = application:get_env(appinc, key1),
+
+ %% Clean up
+ ok = application:unload(appinc).
+
%%%-----------------------------------------------------------------
%%% Tests the 'shutdown_func' kernel config parameter
%%%-----------------------------------------------------------------
diff --git a/lib/mnesia/test/mnesia_frag_test.erl b/lib/mnesia/test/mnesia_frag_test.erl
index d3f6762af7..6695fbc880 100644
--- a/lib/mnesia/test/mnesia_frag_test.erl
+++ b/lib/mnesia/test/mnesia_frag_test.erl
@@ -461,7 +461,7 @@ nice_iter_access(Tab, FragNames, RawRead) ->
ExpectedLast = lists:last(Keys),
?match(ExpectedLast, mnesia:last(Tab)),
- ExpectedAllPrev = ['$end_of_table' | lists:reverse(tl(lists:reverse(Keys)))],
+ ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)],
?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
@@ -477,7 +477,7 @@ evil_iter_access(Tab, FragNames, RawRead) ->
ExpectedLast = lists:last(Keys),
?match(ExpectedLast, mnesia:last(Tab)),
- ExpectedAllPrev = ['$end_of_table' | lists:reverse(tl(lists:reverse(Keys)))],
+ ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)],
?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 0266047ee3..d5fbceff1e 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,26 +21,26 @@
detail_pages/0]).
%% Callbacks for cdv_detail_wx
-get_details({_, {T,Key}}) ->
+get_details({Type, {T,Key}}) ->
[{Key,Term}] = ets:lookup(T,Key),
- {ok,{"Expanded Binary", Term, []}};
+ {ok,{"Expanded Binary", {Type, Term}, []}};
get_details({cdv, Id}) ->
{ok,Bin} = crashdump_viewer:expand_binary(Id),
- {ok,{"Expanded Binary", Bin, []}}.
+ {ok,{"Expanded Binary", {cvd, Bin}, []}}.
detail_pages() ->
[{"Binary", fun init_bin_page/2}].
-init_bin_page(Parent,Bin) ->
+init_bin_page(Parent,{Type,Bin}) ->
cdv_multi_wx:start_link(
Parent,
- [{"Format \~p",cdv_html_wx,format_bin_fun("~p",Bin)},
- {"Format \~tp",cdv_html_wx,format_bin_fun("~tp",Bin)},
- {"Format \~w",cdv_html_wx,format_bin_fun("~w",Bin)},
- {"Format \~s",cdv_html_wx,format_bin_fun("~s",Bin)},
- {"Format \~ts",cdv_html_wx,format_bin_fun("~ts",Bin)},
- {"Hex",cdv_html_wx,hex_binary_fun(Bin)},
- {"Term",cdv_html_wx,binary_to_term_fun(Bin)}]).
+ [{"Format \~p",cdv_html_wx,{Type,format_bin_fun("~p",Bin)}},
+ {"Format \~tp",cdv_html_wx,{Type,format_bin_fun("~tp",Bin)}},
+ {"Format \~w",cdv_html_wx,{Type,format_bin_fun("~w",Bin)}},
+ {"Format \~s",cdv_html_wx,{Type,format_bin_fun("~s",Bin)}},
+ {"Format \~ts",cdv_html_wx,{Type,format_bin_fun("~ts",Bin)}},
+ {"Hex",cdv_html_wx,{Type,hex_binary_fun(Bin)}},
+ {"Term",cdv_html_wx,{Type,binary_to_term_fun(Bin)}}]).
format_bin_fun(Format,Bin) ->
fun() ->
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index fe77a41f59..b79c647f63 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,6 +30,7 @@
%% Records
-record(state,
{panel,
+ app, %% which tool is the user
expand_table,
expand_wins=[]}).
@@ -38,16 +39,21 @@ start_link(ParentWin, Info) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([ParentWin, Fun]) when is_function(Fun) ->
- init([ParentWin, Fun()]);
+init([ParentWin, {App, Fun}]) when is_function(Fun) ->
+ init([ParentWin, {App, Fun()}]);
init([ParentWin, {expand,HtmlText,Tab}]) ->
- HtmlWin = observer_lib:html_window(ParentWin),
- wxHtmlWindow:setPage(HtmlWin,HtmlText),
- {HtmlWin, #state{panel=HtmlWin,expand_table=Tab}};
+ init(ParentWin, HtmlText, Tab, cdv);
+init([ParentWin, {App, {expand,HtmlText,Tab}}]) ->
+ init(ParentWin, HtmlText, Tab, App);
+init([ParentWin, {App,HtmlText}]) ->
+ init(ParentWin, HtmlText, undefined, App);
init([ParentWin, HtmlText]) ->
+ init(ParentWin, HtmlText, undefined, cdv).
+
+init(ParentWin, HtmlText, Tab, App) ->
HtmlWin = observer_lib:html_window(ParentWin),
wxHtmlWindow:setPage(HtmlWin,HtmlText),
- {HtmlWin, #state{panel=HtmlWin}}.
+ {HtmlWin, #state{panel=HtmlWin,expand_table=Tab,app=App}}.
%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -78,7 +84,7 @@ handle_cast(Msg, State) ->
handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
linkInfo=#wxHtmlLinkInfo{href=Target}}},
- #state{expand_table=Tab}=State) ->
+ #state{expand_table=Tab, app=App}=State) ->
NewState=
case Target of
"#Binary?" ++ BinSpec ->
@@ -102,10 +108,12 @@ handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
list_to_integer(Key2),
list_to_integer(Key3)}}},
expand(Id,cdv_term_cb,State);
+ _ when App =:= obs ->
+ observer ! {open_link, Target};
_ ->
cdv_virtual_list_wx:start_detail_win(Target),
State
- end,
+ end,
{noreply, NewState};
handle_event(Event, State) ->
diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl
index 6426cc0803..4451045012 100644
--- a/lib/observer/src/cdv_term_cb.erl
+++ b/lib/observer/src/cdv_term_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,23 +21,23 @@
detail_pages/0]).
%% Callbacks for cdv_detail_wx
-get_details({_, {T,Key}}) ->
+get_details({Type, {T,Key}}) ->
[{Key,Term}] = ets:lookup(T,Key),
- {ok,{"Expanded Term", [Term, T], []}}.
+ {ok,{"Expanded Term", {Type,[Term, T]}, []}}.
detail_pages() ->
[{"Term", fun init_term_page/2}].
-init_term_page(ParentWin, [Term, Tab]) ->
+init_term_page(ParentWin, {Type, [Term, Tab]}) ->
Expanded = expand(Term, true),
BinSaved = expand(Term, Tab),
cdv_multi_wx:start_link(
ParentWin,
- [{"Format \~p",cdv_html_wx,format_term_fun("~p",BinSaved,Tab)},
- {"Format \~tp",cdv_html_wx,format_term_fun("~tp",BinSaved,Tab)},
- {"Format \~w",cdv_html_wx,format_term_fun("~w",BinSaved,Tab)},
- {"Format \~s",cdv_html_wx,format_term_fun("~s",Expanded,Tab)},
- {"Format \~ts",cdv_html_wx,format_term_fun("~ts",Expanded,Tab)}]).
+ [{"Format \~p",cdv_html_wx,{Type, format_term_fun("~p",BinSaved,Tab)}},
+ {"Format \~tp",cdv_html_wx,{Type,format_term_fun("~tp",BinSaved,Tab)}},
+ {"Format \~w",cdv_html_wx,{Type,format_term_fun("~w",BinSaved,Tab)}},
+ {"Format \~s",cdv_html_wx,{Type,format_term_fun("~s",Expanded,Tab)}},
+ {"Format \~ts",cdv_html_wx,{Type,format_term_fun("~ts",Expanded,Tab)}}]).
format_term_fun(Format,Term,Tab) ->
fun() ->
@@ -56,8 +56,9 @@ expand(['#CDVBin',Offset,Size,Pos], true) ->
{ok,Bin} = crashdump_viewer:expand_binary({Offset,Size,Pos}),
Bin;
expand(Bin, Tab) when is_binary(Bin), not is_boolean(Tab) ->
- <<Preview:80, _/binary>> = Bin,
Size = byte_size(Bin),
+ PrevSize = min(Size, 10) * 8,
+ <<Preview:PrevSize, _/binary>> = Bin,
Hash = erlang:phash2(Bin),
Key = {Preview, Size, Hash},
ets:insert(Tab, {Key,Bin}),
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index 9f77891426..c279218707 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -146,7 +146,8 @@ all_or_expand(Tab,Term) ->
Check = io_lib:format("~P",[Term,100]),
Exp = Preview=/=Check,
all_or_expand(Tab,Term,Preview,Exp).
-all_or_expand(_Tab,_Term,Str,false) ->
+all_or_expand(_Tab,Term,Str,false)
+ when not is_binary(Term) ->
href_proc_port(lists:flatten(Str));
all_or_expand(Tab,Term,Preview,true)
when not is_binary(Term) ->
@@ -158,20 +159,16 @@ all_or_expand(Tab,Term,Preview,true)
"&key2="++integer_to_list(Key2)++
"&key3="++integer_to_list(Key3)],
"Click to expand above term")];
-all_or_expand(Tab,Bin,PreviewStr,true) when is_binary(Bin) ->
- <<Preview:80, _/binary>> = Bin,
+all_or_expand(Tab,Bin,_PreviewStr,_Expand)
+ when is_binary(Bin) ->
Size = byte_size(Bin),
+ PrevSize = min(Size, 10) * 8,
+ <<Preview:PrevSize, _/binary>> = Bin,
Hash = erlang:phash2(Bin),
Key = {Preview, Size, Hash},
ets:insert(Tab,{Key,Bin}),
- [href_proc_port(lists:flatten(PreviewStr), false), $\n,
- href("TARGET=\"expanded\"",
- ["#OBSBinary?key1="++integer_to_list(Preview)++
- "&key2="++integer_to_list(Size)++
- "&key3="++integer_to_list(Hash)],
- "Click to expand above term")].
-
-
+ Term = io_lib:format("~p", [['#OBSBin',Preview,Size,Hash]]),
+ href_proc_port(lists:flatten(Term), true).
color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN));
color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)).
@@ -242,7 +239,7 @@ href(Args,Link,Text) ->
["<A HREF=\"",Link,"\" ",Args,">",Text,"</A>"].
font(Args,Text) ->
["<FONT ",Args,">\n",Text,"\n</FONT>\n"].
-p(Text) ->
+p(Text) ->
["<P>",Text,"</P>\n"].
br() ->
"<BR>\n".
@@ -336,34 +333,28 @@ href_proc_bin(From, T, Acc, LTB) ->
{OffsetSizePos,Rest} = split($],T),
BinStr =
case string:tokens(OffsetSizePos,",.| \n") of
- [Offset,Size,Pos] when From =:= cdv ->
+ [Offset,SizeStr,Pos] when From =:= cdv ->
Id = {list_to_integer(Offset),10,list_to_integer(Pos)},
{ok,PreviewBin} = crashdump_viewer:expand_binary(Id),
- PreviewStr = ["&lt;&lt;",
- [integer_to_list(X)++"," || <<X:8>> <= PreviewBin],
- "...(",
- observer_lib:to_str({bytes,Size}),
- ")&gt;&gt;"],
+ PreviewStr = preview_string(list_to_integer(SizeStr), PreviewBin),
if LTB ->
href("TARGET=\"expanded\"",
["#Binary?offset="++Offset++
- "&size="++Size++
+ "&size="++SizeStr++
"&pos="++Pos],
PreviewStr);
true ->
PreviewStr
end;
- [Preview,Size,Md5] when From =:= obs ->
- PreviewBin = <<(list_to_integer(Preview)):80>>,
- PreviewStr = ["&lt;&lt;",
- [integer_to_list(X)++"," || <<X:8>> <= PreviewBin],
- "...(",
- observer_lib:to_str({bytes,list_to_integer(Size)}),
- ")&gt;&gt;"],
+ [Preview,SizeStr,Md5] when From =:= obs ->
+ Size = list_to_integer(SizeStr),
+ PrevSize = min(Size, 10) * 8,
+ PreviewStr = preview_string(Size,
+ <<(list_to_integer(Preview)):PrevSize>>),
if LTB ->
href("TARGET=\"expanded\"",
["#OBSBinary?key1="++Preview++
- "&key2="++Size++
+ "&key2="++SizeStr++
"&key3="++Md5],
PreviewStr);
true ->
@@ -374,6 +365,35 @@ href_proc_bin(From, T, Acc, LTB) ->
end,
href_proc_port(Rest,[BinStr|Acc],LTB).
+preview_string(Size, PreviewBin) when Size > 10 ->
+ ["&lt;&lt;",
+ remove_lgt(io_lib:format("~p",[PreviewBin])),
+ "...(",
+ observer_lib:to_str({bytes,Size}),
+ ")",
+ "&gt;&gt"];
+preview_string(_, PreviewBin) ->
+ ["&lt;&lt;",
+ remove_lgt(io_lib:format("~p",[PreviewBin])),
+ "&gt;&gt"].
+
+remove_lgt(Deep) ->
+ remove_lgt_1(lists:flatten(Deep)).
+
+remove_lgt_1([$<,$<|Rest]) ->
+ [$>,$>|BinStr] = lists:reverse(Rest),
+ replace_lgt(lists:reverse(BinStr)).
+
+replace_lgt([$<|R]) ->
+ ["&lt;"|replace_lgt(R)];
+replace_lgt([$>|R]) ->
+ ["&gt;"|replace_lgt(R)];
+replace_lgt([L=[_|_]|R]) ->
+ [replace_lgt(L)|replace_lgt(R)];
+replace_lgt([A|R]) ->
+ [A|replace_lgt(R)];
+replace_lgt([]) -> [].
+
split(Char,Str) ->
split(Char,Str,[]).
split(Char,[Char|Str],Acc) -> % match Char
@@ -381,7 +401,6 @@ split(Char,[Char|Str],Acc) -> % match Char
split(Char,[H|T],Acc) ->
split(Char,T,[H|Acc]).
-
warn([]) ->
[];
warn(Warning) ->
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index e0f7bf482b..cedaf7d2b8 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -699,7 +699,9 @@ progress_handler(Caller,Env,Title,Str) ->
register(?progress_handler,self()),
wx:set_env(Env),
PD = progress_dialog(Env,Title,Str),
- progress_loop(Title,PD,Caller).
+ try progress_loop(Title,PD,Caller)
+ catch closed -> normal end.
+
progress_loop(Title,PD,Caller) ->
receive
{progress,{ok,done}} -> % to make wait_for_progress/0 return
@@ -738,8 +740,12 @@ progress_dialog(_Env,Title,Str) ->
PD.
update_progress(PD,Value) ->
- wxProgressDialog:update(PD,Value).
+ try wxProgressDialog:update(PD,Value)
+ catch _:_ -> throw(closed) %% Port or window have died
+ end.
update_progress_text(PD,Text) ->
- wxProgressDialog:update(PD,0,[{newmsg,Text}]).
+ try wxProgressDialog:update(PD,0,[{newmsg,Text}])
+ catch _:_ -> throw(closed) %% Port or window have died
+ end.
finish_progress(PD) ->
wxProgressDialog:destroy(PD).
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 988e04993c..3ffa5fc77d 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -136,9 +136,9 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}},
{noreply, State};
Callback ->
[{"key1",Key1},{"key2",Key2},{"key3",Key3}] = httpd:parse_query(Rest),
- Id = {observer, {T,{list_to_integer(Key1),
- list_to_integer(Key2),
- list_to_integer(Key3)}}},
+ Id = {obs, {T,{list_to_integer(Key1),
+ list_to_integer(Key2),
+ list_to_integer(Key3)}}},
Opened =
case lists:keyfind(Id,1,Opened0) of
false ->
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 4c385b76aa..ecb8e132fe 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -214,7 +214,7 @@ handle_event(#wx{event = #wxClose{}}, State) ->
{stop, normal, State};
handle_event(#wx{id = ?ID_CDV, event = #wxCommand{type = command_menu_selected}}, State) ->
- crashdump_viewer:start(),
+ spawn(crashdump_viewer, start, []),
{noreply, State};
handle_event(#wx{id = ?wxID_EXIT, event = #wxCommand{type = command_menu_selected}}, State) ->
diff --git a/lib/orber/src/orber_interceptors.erl b/lib/orber/src/orber_interceptors.erl
index 407823ea79..62870b35b5 100644
--- a/lib/orber/src/orber_interceptors.erl
+++ b/lib/orber/src/orber_interceptors.erl
@@ -112,7 +112,7 @@ pop_system_message_interceptor(out) ->
[{_, []}] ->
ok;
[{_, Interceptors}] ->
- ets:insert(orber_interceptors, {message_out_interceptors, remove_last_element(Interceptors)});
+ ets:insert(orber_interceptors, {message_out_interceptors, lists:droplast(Interceptors)});
_ ->
corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO})
end.
@@ -151,12 +151,3 @@ apply_message_interceptors([], F, ObjRef, Bytes) ->
apply_message_interceptors([M | Rest], F, ObjRef, Bytes) ->
apply_message_interceptors(Rest, F, ObjRef, apply(M, F, [ObjRef, Bytes])).
-
-remove_last_element([]) ->
- [];
-remove_last_element([M]) ->
- [];
-remove_last_element([M |Tail]) ->
- remove_last_element([Tail]).
-
-
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index b698beb558..f4657663e6 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -423,7 +423,7 @@ infile(Parent, Infilex, Options) ->
end,
case {St#yecc.errors, werror(St)} of
{[], false} -> ok;
- _ -> _ = file:delete(St#yecc.outfile)
+ _ -> _ = file:delete(St#yecc.outfile), ok
end,
Parent ! {self(), yecc_ret(St)}.
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index bd19d0e434..fc3479cb64 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -95,7 +95,7 @@
crypto:rand_bytes(8)} | 'PBES2-params'}</code></p>
<p><code>public_key() = rsa_public_key() | dsa_public_key() | ec_public_key()</code></p>
- <p><code>private_key() = rsa_public_key() | dsa_public_key() | ec_public_key()</code></p>
+ <p><code>private_key() = rsa_private_key() | dsa_private_key() | ec_private_key()</code></p>
<p><code>rsa_public_key() = #'RSAPublicKey'{}</code></p>
<p><code>rsa_private_key() = #'RSAPrivateKey'{}</code></p>
diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl
index 8afc841fa6..c6394115e3 100644
--- a/lib/public_key/include/public_key.hrl
+++ b/lib/public_key/include/public_key.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -76,7 +76,6 @@
point
}).
-
-define(unspecified, 0).
-define(keyCompromise, 1).
-define(cACompromise, 2).
@@ -88,23 +87,4 @@
-define(privilegeWithdrawn, 9).
-define(aACompromise, 10).
--type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key().
--type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key().
--type rsa_public_key() :: #'RSAPublicKey'{}.
--type rsa_private_key() :: #'RSAPrivateKey'{}.
--type dsa_private_key() :: #'DSAPrivateKey'{}.
--type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
--type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}}.
--type ec_private_key() :: #'ECPrivateKey'{}.
--type der_encoded() :: binary().
--type decrypt_der() :: binary().
--type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
- | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
- | 'SubjectPublicKeyInfo' | 'CertificationRequest' | 'CertificateList'.
--type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
- not_encrypted | {Cipher :: string(), Salt :: binary()}}.
--type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
--type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
- auth_keys.
-
-endif. % -ifdef(public_key).
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index f7a361d5a8..9a8e49f265 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -99,7 +99,7 @@ transform(Other,_) ->
Other.
%%--------------------------------------------------------------------
--spec supportedPublicKeyAlgorithms(Oid::tuple()) -> asn1_type().
+-spec supportedPublicKeyAlgorithms(Oid::tuple()) -> public_key:asn1_type().
%%
%% Description: Returns the public key type for an algorithm
%% identifier tuple as found in SubjectPublicKeyInfo.
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index c0a0de815b..3a1653d989 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -51,7 +51,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec decode(binary()) -> [pem_entry()].
+-spec decode(binary()) -> [public_key:pem_entry()].
%%
%% Description: Decodes a PEM binary.
%%--------------------------------------------------------------------
@@ -59,7 +59,7 @@ decode(Bin) ->
decode_pem_entries(split_bin(Bin), []).
%%--------------------------------------------------------------------
--spec encode([pem_entry()]) -> iolist().
+-spec encode([public_key:pem_entry()]) -> iolist().
%%
%% Description: Encodes a list of PEM entries.
%%--------------------------------------------------------------------
@@ -67,7 +67,7 @@ encode(PemEntries) ->
encode_pem_entries(PemEntries).
%%--------------------------------------------------------------------
--spec decipher({pki_asn1_type(), DerEncrypted::binary(),
+-spec decipher({public_key:pki_asn1_type(), DerEncrypted::binary(),
{Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}}},
string()) -> Der::binary().
%%
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index 41280b9e14..0522ea6ac3 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,7 +35,8 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
+-spec decode(binary(), public_key | public_key:ssh_file()) ->
+ [{public_key:public_key(), Attributes::list()}].
%%
%% Description: Decodes a ssh file-binary.
%%--------------------------------------------------------------------
@@ -52,7 +53,7 @@ decode(Bin, Type) ->
openssh_decode(Bin, Type).
%%--------------------------------------------------------------------
--spec encode([{public_key(), Attributes::list()}], ssh_file()) ->
+-spec encode([{public_key:public_key(), Attributes::list()}], public_key:ssh_file()) ->
binary().
%%
%% Description: Encodes a list of ssh file entries.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index ceecbcc7f2..a732455aa7 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -49,6 +49,27 @@
pkix_crls_validate/3
]).
+-export_type([public_key/0, private_key/0, pem_entry/0,
+ pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0]).
+
+-type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key().
+-type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key().
+
+-type rsa_public_key() :: #'RSAPublicKey'{}.
+-type rsa_private_key() :: #'RSAPrivateKey'{}.
+-type dsa_private_key() :: #'DSAPrivateKey'{}.
+-type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
+-type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}}.
+-type ec_private_key() :: #'ECPrivateKey'{}.
+-type der_encoded() :: binary().
+-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
+ | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
+ | 'SubjectPublicKeyInfo' | 'CertificationRequest' | 'CertificateList'.
+-type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
+ not_encrypted | {Cipher :: string(), Salt :: binary()}}.
+-type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
+-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
+ auth_keys.
-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
| 'rsa_no_padding'.
-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 7e9d7c984a..ad2a8005b9 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1781,37 +1781,46 @@ otp_10463_upgrade_script_regexp(_Config) ->
ok.
no_dot_erlang(Conf) ->
- PrivDir = priv_dir(Conf),
+ PrivDir = ?config(data_dir,Conf),
{ok, OrigWd} = file:get_cwd(),
try
ok = file:set_cwd(PrivDir),
- Erl = "\"" ++ filename:join([code:root_dir(),"bin","erl"]) ++ "\"",
- Args = " -noinput -run io put_chars \"TESTOK\" -run erlang halt",
+ {ok, Wd} = file:get_cwd(),
+ io:format("Dir ~ts~n", [Wd]),
+
+ Erl0 = filename:join([code:root_dir(),"bin","erl"]),
+ Erl = filename:nativename(Erl0),
+ Quote = "\"",
+ Args = " -noinput -run c pwd -run erlang halt",
ok = file:write_file(".erlang", <<"io:put_chars(\"DOT_ERLANG_READ\\n\").\n">>),
- case os:cmd(Erl ++ Args) of
+ CMD1 = Quote ++ Erl ++ Quote ++ Args ,
+ case os:cmd(CMD1) of
"DOT_ERLANG_READ" ++ _ -> ok;
Other1 ->
- io:format("Failed: ~ts~n",[Erl ++ Args]),
+ io:format("Failed: ~ts~n",[CMD1]),
io:format("Expected: ~s ++ _~n",["DOT_ERLANG_READ "]),
io:format("Got: ~ts~n",[Other1]),
- exit(failed_to_start, test_error)
+ exit({failed_to_start, test_error})
end,
NO_DOT_ERL = " -boot no_dot_erlang",
- case os:cmd(Erl ++ NO_DOT_ERL ++ Args) of
- "TESTOK" ++ _ -> ok;
- Other2 ->
- io:format("Failed: ~ts~n",[Erl ++ Args]),
+ CMD2 = Quote ++ Erl ++ Quote ++ NO_DOT_ERL ++ Args,
+ case lists:prefix(Wd, Other2 = os:cmd(CMD2)) of
+ true -> ok;
+ false ->
+ io:format("Failed: ~ts~n",[CMD2]),
io:format("Expected: ~s~n",["TESTOK"]),
io:format("Got: ~ts~n",[Other2]),
- exit(failed_to_start, no_dot_erlang)
+ exit({failed_to_start, no_dot_erlang})
end
after
_ = file:delete(".erlang"),
- ok = file:set_cwd(OrigWd)
+ ok = file:set_cwd(OrigWd),
+ ok
end.
+
%%%=================================================================
%%% Misceleaneous functions
%%%=================================================================
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 5d5f2e5b91..eaf96d0230 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -367,8 +367,11 @@
</func>
<func>
- <name>stop() -> ok </name>
+ <name>stop() -> ok | {error, Reason}</name>
<fsummary>Stops the SSH application.</fsummary>
+ <type>
+ <v>Reason = term()</v>
+ </type>
<desc>
<p>Stops the SSH application. See also
<seealso marker="kernel:application">application(3)</seealso></p>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 9f571adba2..d50d5a0cb3 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -32,8 +32,8 @@
shell/1, shell/2, shell/3]).
%%--------------------------------------------------------------------
--spec start() -> ok.
--spec start(permanent | transient | temporary) -> ok.
+-spec start() -> ok | {error, term()}.
+-spec start(permanent | transient | temporary) -> ok | {error, term()}.
%%
%% Description: Starts the ssh application. Default type
%% is temporary. see application(3)
@@ -51,7 +51,7 @@ start(Type) ->
application:start(ssh, Type).
%%--------------------------------------------------------------------
--spec stop() -> ok.
+-spec stop() -> ok | {error, term()}.
%%
%% Description: Stops the ssh application.
%%--------------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 3462b98172..070a2db5a8 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -157,7 +157,7 @@ init([Role, Socket, SshOpts]) ->
%%--------------------------------------------------------------------
-spec open_channel(pid(), string(), iodata(), integer(), integer(),
- timeout()) -> {open, channel_id()} | {open_error, term(), string(), string()}.
+ timeout()) -> {open, channel_id()} | {error, term()}.
%%--------------------------------------------------------------------
open_channel(ConnectionHandler, ChannelType, ChannelSpecificData,
InitialWindowSize,
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index 21cdedc156..5692138a8a 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -65,7 +65,7 @@ is_auth_key(Key, User,Opts) ->
%% Used by client
is_host_key(Key, PeerName, Algorithm, Opts) ->
- case lookup_host_key(PeerName, Algorithm, Opts) of
+ case lookup_host_key(Key, PeerName, Algorithm, Opts) of
{ok, Key} ->
true;
_ ->
@@ -121,9 +121,9 @@ decode_ssh_file(Pem, Password) ->
%% return {ok, Key(s)} or {error, not_found}
%%
-lookup_host_key(Host, Alg, Opts) ->
+lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
Host1 = replace_localhost(Host),
- do_lookup_host_key(Host1, Alg, Opts).
+ do_lookup_host_key(KeyToMatch, Host1, Alg, Opts).
add_host_key(Host, Key, Opts) ->
@@ -204,10 +204,10 @@ replace_localhost("localhost") ->
replace_localhost(Host) ->
Host.
-do_lookup_host_key(Host, Alg, Opts) ->
+do_lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
case file:open(file_name(user, "known_hosts", Opts), [read, binary]) of
{ok, Fd} ->
- Res = lookup_host_key_fd(Fd, Host, Alg),
+ Res = lookup_host_key_fd(Fd, KeyToMatch, Host, Alg),
file:close(Fd),
{ok, Res};
{error, enoent} -> {error, not_found};
@@ -228,16 +228,16 @@ identity_pass_phrase('ssh-rsa') ->
identity_pass_phrase("ssh-rsa") ->
rsa_pass_phrase.
-lookup_host_key_fd(Fd, Host, KeyType) ->
+lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType) ->
case io:get_line(Fd, '') of
eof ->
{error, not_found};
Line ->
case ssh_decode_line(Line, known_hosts) of
[{Key, Attributes}] ->
- handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
+ handle_host(Fd, KeyToMatch, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
[] ->
- lookup_host_key_fd(Fd, Host, KeyType)
+ lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType)
end
end.
@@ -248,13 +248,13 @@ ssh_decode_line(Line, Type) ->
[]
end.
-handle_host(Fd, Host, HostList, Key, KeyType) ->
+handle_host(Fd, KeyToMatch, Host, HostList, Key, KeyType) ->
Host1 = host_name(Host),
- case lists:member(Host1, HostList) and key_match(Key, KeyType) of
- true ->
+ case lists:member(Host1, HostList) andalso key_match(Key, KeyType) of
+ true when KeyToMatch == Key ->
Key;
- false ->
- lookup_host_key_fd(Fd, Host, KeyType)
+ _ ->
+ lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType)
end.
host_name(Atom) when is_atom(Atom) ->
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 01a0988718..8d6c77c0ed 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -315,8 +315,8 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?UINT32(Len), Data:Le
recipient_channel = Recipient,
data = Data
};
-decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
- ?UINT32(DataType), Data/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
+ ?UINT32(DataType), ?UINT32(Len), Data:Len/binary>>) ->
#ssh_msg_channel_extended_data{
recipient_channel = Recipient,
data_type_code = DataType,
@@ -380,27 +380,30 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER),
language = Lang
};
+decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
+ ?UINT32(Len1), Inst:Len1/binary, ?UINT32(Len2), Lang:Len2/binary,
+ ?UINT32(NumPromtps), Data/binary>>) ->
+ #ssh_msg_userauth_info_request{
+ name = Name,
+ instruction = Inst,
+ language_tag = Lang,
+ num_prompts = NumPromtps,
+ data = Data};
+
+%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
#ssh_msg_userauth_pk_ok{
algorithm_name = Alg,
key_blob = KeyBlob
};
+%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/binary,
?UINT32(Len1), Lang:Len1/binary>>) ->
#ssh_msg_userauth_passwd_changereq{
prompt = Prompt,
languge = Lang
};
-decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
- ?UINT32(Len1), Inst:Len1/binary, ?UINT32(Len2), Lang:Len2/binary,
- ?UINT32(NumPromtps), Data/binary>>) ->
- #ssh_msg_userauth_info_request{
- name = Name,
- instruction = Inst,
- language_tag = Lang,
- num_prompts = NumPromtps,
- data = Data};
decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
#ssh_msg_userauth_info_response{
@@ -424,8 +427,9 @@ decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?UINT32(N)>>) ->
#ssh_msg_kex_dh_gex_request_old{
n = N
};
-decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP), ?UINT32(Len0), Prime:Len0/big-signed-integer,
- ?UINT32(Len1), Generator:Len1/big-signed-integer>>) ->
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP),
+ ?UINT32(Len0), Prime:Len0/big-signed-integer-unit:8,
+ ?UINT32(Len1), Generator:Len1/big-signed-integer-unit:8>>) ->
#ssh_msg_kex_dh_gex_group{
p = Prime,
g = Generator
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index 213b5c714d..52665635f0 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -673,7 +673,7 @@ resolve_symlinks_2(["." | RestPath], State0, LinkCnt, AccPath) ->
resolve_symlinks_2([".." | RestPath], State0, LinkCnt, AccPath) ->
%% Remove the last path component
AccPathComps0 = filename:split(AccPath),
- Path = case lists:reverse(tl(lists:reverse(AccPathComps0))) of
+ Path = case lists:droplast(AccPathComps0) of
[] ->
"";
AccPathComps ->
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 8c2b84bc1e..c3bdeb1a54 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -626,7 +626,7 @@ handle_options(Opts0, _Role) ->
user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined),
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
- ciphers = handle_option(ciphers, Opts, []),
+ ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), hd(Versions)),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
@@ -712,7 +712,7 @@ validate_option(certfile, undefined = Value) ->
validate_option(certfile, Value) when is_binary(Value) ->
Value;
validate_option(certfile, Value) when is_list(Value) ->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(key, undefined) ->
undefined;
@@ -729,7 +729,7 @@ validate_option(keyfile, undefined) ->
validate_option(keyfile, Value) when is_binary(Value) ->
Value;
validate_option(keyfile, Value) when is_list(Value), Value =/= "" ->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(password, Value) when is_list(Value) ->
Value;
@@ -743,7 +743,7 @@ validate_option(cacertfile, undefined) ->
validate_option(cacertfile, Value) when is_binary(Value) ->
Value;
validate_option(cacertfile, Value) when is_list(Value), Value =/= ""->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(dh, Value) when Value == undefined;
is_binary(Value) ->
Value;
@@ -752,12 +752,12 @@ validate_option(dhfile, undefined = Value) ->
validate_option(dhfile, Value) when is_binary(Value) ->
Value;
validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(psk_identity, undefined) ->
undefined;
validate_option(psk_identity, Identity)
when is_list(Identity), Identity =/= "", length(Identity) =< 65535 ->
- list_to_binary(Identity);
+ binary_filename(Identity);
validate_option(user_lookup_fun, undefined) ->
undefined;
validate_option(user_lookup_fun, {Fun, _} = Value) when is_function(Fun, 3) ->
@@ -766,17 +766,9 @@ validate_option(srp_identity, undefined) ->
undefined;
validate_option(srp_identity, {Username, Password})
when is_list(Username), is_list(Password), Username =/= "", length(Username) =< 255 ->
- {list_to_binary(Username), list_to_binary(Password)};
+ {unicode:characters_to_binary(Username),
+ unicode:characters_to_binary(Password)};
-validate_option(ciphers, Value) when is_list(Value) ->
- Version = tls_record:highest_protocol_version([]),
- try cipher_suites(Version, Value)
- catch
- exit:_ ->
- throw({error, {options, {ciphers, Value}}});
- error:_->
- throw({error, {options, {ciphers, Value}}})
- end;
validate_option(reuse_session, Value) when is_function(Value) ->
Value;
validate_option(reuse_sessions, Value) when is_boolean(Value) ->
@@ -936,16 +928,26 @@ emulated_options([Opt|Opts], Inet, Emulated) ->
emulated_options([], Inet,Emulated) ->
{Inet, Emulated}.
-cipher_suites(Version, []) ->
+handle_cipher_option(Value, Version) when is_list(Value) ->
+ try binary_cipher_suites(Version, Value) of
+ Suites ->
+ Suites
+ catch
+ exit:_ ->
+ throw({error, {options, {ciphers, Value}}});
+ error:_->
+ throw({error, {options, {ciphers, Value}}})
+ end.
+binary_cipher_suites(Version, []) -> %% Defaults to all supported suits
ssl_cipher:suites(Version);
-cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
+binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0],
- cipher_suites(Version, Ciphers);
-cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
+ binary_cipher_suites(Version, Ciphers);
+binary_cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
- cipher_suites(Version, Ciphers);
+ binary_cipher_suites(Version, Ciphers);
-cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
+binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
Supported0 = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites()
++ ssl_cipher:psk_suites(Version)
@@ -953,18 +955,18 @@ cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
Supported = ssl_cipher:filter_suites(Supported0),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of
[] ->
- Supported;
+ Supported; %% Defaults to all supported suits
Ciphers ->
Ciphers
end;
-cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
+binary_cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
%% Format: ["RC4-SHA","RC4-MD5"]
Ciphers = [ssl_cipher:openssl_suite(C) || C <- Ciphers0],
- cipher_suites(Version, Ciphers);
-cipher_suites(Version, Ciphers0) ->
+ binary_cipher_suites(Version, Ciphers);
+binary_cipher_suites(Version, Ciphers0) ->
%% Format: "RC4-SHA:RC4-MD5"
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
- cipher_suites(Version, Ciphers).
+ binary_cipher_suites(Version, Ciphers).
unexpected_format(Error) ->
lists:flatten(io_lib:format("Unexpected error: ~p", [Error])).
@@ -1036,3 +1038,7 @@ connection_sup(tls_connection) ->
tls_connection_sup;
connection_sup(dtls_connection) ->
dtls_connection_sup.
+
+binary_filename(FileName) ->
+ Enc = file:native_name_encoding(),
+ unicode:characters_to_binary(FileName, unicode, Enc).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 102215119d..64b89e9f95 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -82,13 +82,13 @@
validate_extensions_fun,
depth :: integer(),
certfile :: binary(),
- cert :: der_encoded(),
+ cert :: public_key:der_encoded(),
keyfile :: binary(),
- key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', der_encoded()},
+ key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()},
password :: string(),
- cacerts :: [der_encoded()],
+ cacerts :: [public_key:der_encoded()],
cacertfile :: binary(),
- dh :: der_encoded(),
+ dh :: public_key:der_encoded(),
dhfile :: binary(),
user_lookup_fun, % server option, fun to lookup the user
psk_identity :: binary(),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index ddc511c652..2e216b32fa 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -86,6 +86,7 @@ basic_tests() ->
[app,
alerts,
send_close,
+ version_option,
connect_twice,
connect_dist,
clear_pem_cache
@@ -1072,6 +1073,13 @@ send_close(Config) when is_list(Config) ->
{error, _} = ssl:send(SslS, "Hello world").
%%--------------------------------------------------------------------
+version_option() ->
+ [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}].
+version_option(Config) when is_list(Config) ->
+ Versions = proplists:get_value(supported, ssl:versions()),
+ [version_option_test(Config, Version) || Version <- Versions].
+
+%%--------------------------------------------------------------------
close_transport_accept() ->
[{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}].
@@ -3488,3 +3496,28 @@ shutdown_both_result(Socket, client) ->
peername_result(S) ->
ssl:peername(S).
+
+version_option_test(Config, Version) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false}, {versions, [Version]}| ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false}, {versions, [Version]}| ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 68784a175e..a7361755e5 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -248,7 +248,7 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost",
+ " -host localhost" ++ workaround_openssl_s_clinent(),
ct:log("openssl cmd: ~p~n", [Cmd]),
@@ -1383,3 +1383,20 @@ supports_sslv2(Port) ->
true
end.
+workaround_openssl_s_clinent() ->
+ %% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159
+ %% https://bugs.archlinux.org/task/33919
+ %% Bug seems to manifests it self if TLS version is not
+ %% explicitly specified
+ case os:cmd("openssl version") of
+ "OpenSSL 1.0.1c" ++ _ ->
+ " -no_tls1_2 ";
+ "OpenSSL 1.0.1d" ++ _ ->
+ " -no_tls1_2 ";
+ "OpenSSL 1.0.1e" ++ _ ->
+ " -no_tls1_2 ";
+ "OpenSSL 1.0.1f" ++ _ ->
+ " -no_tls1_2 ";
+ _ ->
+ ""
+ end.
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 21cf8e4149..3df24bf688 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -171,6 +171,10 @@
<p>Returns a list of all tables at the node. Named tables are
given by their names, unnamed tables are given by their
table identifiers.</p>
+ <p>There is no guarantee of consistency in the returned list. Tables created
+ or deleted by other processes "during" the ets:all() call may or may
+ not be included in the list. Only tables created/deleted <em>before</em>
+ ets:all() is called are guaranteed to be included/excluded.</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 251a383cf8..ee3c51c62c 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -123,6 +123,14 @@
</desc>
</func>
<func>
+ <name name="droplast" arity="1"/>
+ <fsummary>Drop the last element of a list</fsummary>
+ <desc>
+ <p>Drops the last element of a <c><anno>List</anno></c>. The list should
+ be non-empty, otherwise the function will crash with a <c>function_clause</c></p>
+ </desc>
+ </func>
+ <func>
<name name="dropwhile" arity="2"/>
<fsummary>Drop elements from a list while a predicate is true</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index ee7dd128f1..75505d7d84 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,10 +41,10 @@
future.</p>
<p>The functionality described in EEP10 was implemented in Erlang/OTP
- as of R13A, but that was by no means the end of it. In R14B01 support
+ R13A, but that was by no means the end of it. In Erlang/OTP R14B01 support
for Unicode file names was added, although it was in no way complete
and was by default disabled on platforms where no guarantee was given
- for the file name encoding. With R16A came support for UTF-8 encoded
+ for the file name encoding. With Erlang/OTP R16A came support for UTF-8 encoded
source code, among with enhancements to many of the applications to
support both Unicode encoded file names as well as support for UTF-8
encoded files in several circumstances. Most notable is the support
@@ -52,8 +52,8 @@
for UTF-8 and more support for Unicode character sets in the
I/O-system.</p>
- <p>In 17.0, the encoding default for Erlang source files was
- switched to UTF-8 and in 18.0 Erlang will support atoms in the full
+ <p>In Erlang/OTP 17.0, the encoding default for Erlang source files was
+ switched to UTF-8 and in Erlang/OTP 18.0 Erlang will support atoms in the full
Unicode range, meaning full Unicode function and module
names</p>
@@ -222,7 +222,7 @@
<tag>Representation</tag>
<item>To handle Unicode characters in Erlang, we have to have a
common representation both in lists and binaries. The EEP (10) and
- the subsequent initial implementation in R13A settled a standard
+ the subsequent initial implementation in Erlang/OTP R13A settled a standard
representation of Unicode characters in Erlang.</item>
<tag>Manipulation</tag>
<item>The Unicode characters need to be processed by the Erlang
@@ -274,9 +274,9 @@
(<c>+fnu</c>) on platforms where this is not the default.</item>
<tag>Source code encoding</tag>
<item>When it comes to the Erlang source code, there is support
- for the UTF-8 encoding and bytewise encoding. The default in R16B
- is bytewise (or latin1) encoding. You can control the encoding by
- a comment like:
+ for the UTF-8 encoding and bytewise encoding. The default in
+ Erlang/OTP R16B was bytewise (or latin1) encoding; in Erlang/OTP 17.0
+ it was changed to UTF-8. You can control the encoding by a comment like:
<code>
%% -*- coding: utf-8 -*-
</code>
@@ -290,7 +290,7 @@
<item>Having the source code in UTF-8 also allows you to write
string literals containing Unicode characters with code points &gt;
255, although atoms, module names and function names will be
- restricted to the ISO-Latin-1 range until the 18.0 release. Binary
+ restricted to the ISO-Latin-1 range until the Erlang/OTP 18.0 release. Binary
literals where you use the <c>/utf8</c> type, can also be
expressed using Unicode characters &gt; 255. Having module names
using characters other than 7-bit ASCII can cause trouble on
@@ -304,7 +304,7 @@
<section>
<title>Standard Unicode Representation</title>
<p>In Erlang, strings are actually lists of integers. A string was
- up until R13 defined to be encoded in the ISO-latin-1 (ISO8859-1)
+ up until Erlang/OTP R13 defined to be encoded in the ISO-latin-1 (ISO8859-1)
character set, which is, code point by code point, a sub-range of
the Unicode character set.</p>
<p>The standard list encoding for strings was therefore easily
@@ -321,7 +321,7 @@
encoding has to be decided upon and the string should be converted
to a binary in the preferred encoding using
<c>unicode:characters_to_binary/{1,2,3}</c>. Strings are not
- generally lists of bytes, as they were before R13. They are lists of
+ generally lists of bytes, as they were before Erlang/OTP R13. They are lists of
characters. Characters are not generally bytes, they are Unicode
code points.</p>
@@ -447,8 +447,8 @@ Bin4 = &lt;&lt;"Hello"/utf16&gt;&gt;,</code>
probably will not appreciate). Another way is to keep it backwards
compatible so that only the ISO-Latin-1 character set is used to
detect a string. A third way would be to let the user decide
- exactly what Unicode ranges are to be viewed as characters. In
- R16B you can select either the whole Unicode range or the
+ exactly what Unicode ranges are to be viewed as characters. Since
+ Erlang/OTP R16B you can select either the whole Unicode range or the
ISO-Latin-1 range by supplying the startup flag <c>+pc
</c><i>Range</i>, where <i>Range</i> is either <c>latin1</c> or
<c>unicode</c>. For backwards compatibility, the default is
@@ -685,7 +685,7 @@ Eshell V5.10.1 (abort with ^G)
</item>
</taglist>
- <p>The Unicode file naming support was introduced with OTP release
+ <p>The Unicode file naming support was introduced with Erlang/OTP
R14B01. A VM operating in Unicode file name translation mode can
work with files having names in any language or character set (as
long as it is supported by the underlying OS and file system). The
@@ -709,7 +709,7 @@ Eshell V5.10.1 (abort with ^G)
problem even if it uses transparent file naming. Very few systems
have mixed file name encodings. A consistent UTF-8 named system will
work perfectly in Unicode file name mode. It was still however
- considered experimental in R14B01 and is still not the default on
+ considered experimental in Erlang/OTP R14B01 and is still not the default on
such systems. Unicode file name translation is turned on with the
<c>+fnu</c> switch to the On Linux, a VM started without explicitly
stating the file name translation mode will default to <c>latin1</c>
@@ -757,7 +757,7 @@ Eshell V5.10.1 (abort with ^G)
<title>Notes About Raw File Names</title>
<marker id="notes-about-raw-filenames"/>
<p>Raw file names were introduced together with Unicode file name
- support in erts-5.8.2 (OTP R14B01). The reason &quot;raw file
+ support in erts-5.8.2 (Erlang/OTP R14B01). The reason &quot;raw file
names&quot; was introduced in the system was to be able to
consistently represent file names given in different encodings on
the same system. Having the VM automatically translate a file name
@@ -798,10 +798,10 @@ Eshell V5.10.1 (abort with ^G)
the argument as a binary.</p>
<p>To force Unicode file name translation mode on systems where this
- is not the default was considered experimental in OTP R14B01 due to
+ is not the default was considered experimental in Erlang/OTP R14B01 due to
the fact that the initial implementation did not ignore wrongly
encoded file names, so that raw file names could spread unexpectedly
- throughout the system. Beginning with R16B, the wrongly encoded file
+ throughout the system. Beginning with Erlang/OTP R16B, the wrongly encoded file
names are only retrieved by special functions
(e.g. <c>file:list_dir_all/1</c>), so the impact on existing code is
much lower, why it is now supported. Unicode file name translation
@@ -1032,7 +1032,7 @@ ok
<c>io</c>/<c>io_lib:format</c> with the <c>"~tp"</c> and
<c>~tP</c> formatting instructions, as described above.</p>
<p>You can check this option by calling io:printable_range/0,
- which in R16B will return <c>unicode</c> or <c>latin1</c>. To be
+ which will return <c>unicode</c> or <c>latin1</c>. To be
compatible with future (expected) extensions to the settings,
one should rather use <c>io_lib:printable_list/1</c> to check if
a list is printable according to the setting. That function will
@@ -1070,8 +1070,8 @@ ok
<item>
<p>This function returns the default encoding for Erlang source
files (if no encoding comment is present) in the currently
- running release. For R16 this returns <c>latin1</c> (meaning
- bytewise encoding). In 17.0 and forward it returns
+ running release. In Erlang/OTP R16B <c>latin1</c> was returned (meaning
+ bytewise encoding). In Erlang/OTP 17.0 and forward it returns
<c>utf8</c>.</p>
<p>The encoding of each file can be specified using comments as
described in
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index 516c0aa30b..0033010dce 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -89,7 +89,13 @@ match(Prefix, Alts, Extra) ->
{yes, Remain, []}
end;
{complete, Str} ->
- {yes, nthtail(Len, Str) ++ Extra, []};
+ {_, Arity} = lists:keyfind(Str, 1, Matches),
+ case {Arity, Extra} of
+ {0, "("} ->
+ {yes, nthtail(Len, Str) ++ "()", []};
+ _ ->
+ {yes, nthtail(Len, Str) ++ Extra, []}
+ end;
no ->
{no, [], []}
end.
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 5f96795d92..63e7be4b74 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -241,23 +241,15 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
%% map
-expr({map_field_assoc,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
- {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
- {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
- ret_expr({map_assoc,K,V}, merge_bindings(Bs1,Bs2), RBs);
-expr({map_field_exact,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
- {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
- {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
- ret_expr({map_exact,K,V}, merge_bindings(Bs1,Bs2), RBs);
expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) ->
{value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs),
- {Vs,Bs} = expr_list(Es, Bs1, Lf, Ef),
+ {Vs,Bs} = eval_map_fields(Es, Bs1, Lf, Ef),
ret_expr(lists:foldl(fun
({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
({map_exact,K,V}, Mi) -> maps:update(K,V,Mi)
end, Map0, Vs), Bs, RBs);
expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
- {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
+ {Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef),
ret_expr(lists:foldl(fun
({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi)
end, maps:new(), Vs), Bs, RBs);
@@ -749,6 +741,24 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, Acc) ->
end
end.
+%% eval_map_fields([Field], Bindings, LocalFunctionHandler,
+%% ExternalFuncHandler) ->
+%% {[{map_assoc | map_exact,Key,Value}],Bindings}
+
+eval_map_fields(Fs, Bs, Lf, Ef) ->
+ eval_map_fields(Fs, Bs, Lf, Ef, []).
+
+eval_map_fields([{map_field_assoc,_,K0,V0}|Fs], Bs0, Lf, Ef, Acc) ->
+ {value,K1,Bs1} = expr(K0, Bs0, Lf, Ef, none),
+ {value,V1,Bs2} = expr(V0, Bs1, Lf, Ef, none),
+ eval_map_fields(Fs, Bs2, Lf, Ef, [{map_assoc,K1,V1}|Acc]);
+eval_map_fields([{map_field_exact,_,K0,V0}|Fs], Bs0, Lf, Ef, Acc) ->
+ {value,K1,Bs1} = expr(K0, Bs0, Lf, Ef, none),
+ {value,V1,Bs2} = expr(V0, Bs1, Lf, Ef, none),
+ eval_map_fields(Fs, Bs2, Lf, Ef, [{map_exact,K1,V1}|Acc]);
+eval_map_fields([], Bs, _Lf, _Ef, Acc) ->
+ {lists:reverse(Acc),Bs}.
+
%% RBs is the bindings to return when the evalution of a function
%% (fun) has finished. If RBs =:= none, then the evalution took place
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 4741bef6b9..f53c6e1278 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -135,9 +135,10 @@ pattern({tuple,Line,Ps}, St0) ->
pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
-pattern({map_field_exact,Line,Key,V0}, St0) ->
- {V,St1} = pattern(V0, St0),
- {{map_field_exact,Line,Key,V},St1};
+pattern({map_field_exact,Line,Key0,V0}, St0) ->
+ {Key,St1} = pattern(Key0, St0),
+ {V,St2} = pattern(V0, St1),
+ {{map_field_exact,Line,Key,V},St2};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{struct,Line,Tag,TPs},TPsvs,St1};
@@ -310,9 +311,10 @@ expr({tuple,Line,Es0}, St0) ->
expr({map,Line,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Es1},St1};
-expr({map,Line,Var,Es0}, St0) ->
- {Es1,St1} = expr_list(Es0, St0),
- {{map,Line,Var,Es1},St1};
+expr({map,Line,Arg0,Es0}, St0) ->
+ {Arg1,St1} = expr(Arg0, St0),
+ {Es1,St2} = expr_list(Es0, St1),
+ {{map,Line,Arg1,Es1},St2};
expr({map_field_assoc,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index f630db6032..0c6f41f594 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -100,7 +100,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
compile=[], %Compile flags
records=dict:new() :: dict(), %Record definitions
locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned)
- no_auto=gb_sets:empty() :: gb_set(), %Functions explicitly not autoimported
+ no_auto=gb_sets:empty() :: gb_set() | 'all', %Functions explicitly not autoimported
defined=gb_sets:empty() :: gb_set(), %Defined fuctions
on_load=[] :: [fa()], %On-load function
on_load_line=0 :: line(), %Line for on_load
@@ -3648,15 +3648,22 @@ is_imported_from_erlang(ImportSet,{Func,Arity}) ->
{ok,erlang} -> true;
_ -> false
end.
-%% Build set of functions where auto-import is explicitly supressed
+%% Build set of functions where auto-import is explicitly suppressed
auto_import_suppressed(CompileFlags) ->
- L0 = [ X || {no_auto_import,X} <- CompileFlags ],
- L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ],
- gb_sets:from_list(L1).
-%% Predicate to find out if autoimport is explicitly supressed for a function
+ case lists:member(no_auto_import, CompileFlags) of
+ true ->
+ all;
+ false ->
+ L0 = [ X || {no_auto_import,X} <- CompileFlags ],
+ L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ],
+ gb_sets:from_list(L1)
+ end.
+%% Predicate to find out if autoimport is explicitly suppressed for a function
+is_autoimport_suppressed(all,{_Func,_Arity}) ->
+ true;
is_autoimport_suppressed(NoAutoSet,{Func,Arity}) ->
gb_sets:is_element({Func,Arity},NoAutoSet).
-%% Predicate to find out if a function specific bif-clash supression (old deprecated) is present
+%% Predicate to find out if a function specific bif-clash suppression (old deprecated) is present
bif_clash_specifically_disabled(St,{F,A}) ->
Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile),
lists:member({F,A},Nowarn).
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 8a1d8e0440..9dbe89da91 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -479,6 +479,15 @@ lexpr({record_field, _, Rec, F}, Prec, Opts) ->
{L,P,R} = inop_prec('.'),
El = [lexpr(Rec, L, Opts),$.,lexpr(F, R, Opts)],
maybe_paren(P, Prec, El);
+lexpr({map, _, Fs}, Prec, Opts) ->
+ {P,_R} = preop_prec('#'),
+ El = {first,leaf("#"),map_fields(Fs, Opts)},
+ maybe_paren(P, Prec, El);
+lexpr({map, _, Map, Fs}, Prec, Opts) ->
+ {L,P,_R} = inop_prec('#'),
+ Rl = lexpr(Map, L, Opts),
+ El = {first,[Rl,leaf("#")],map_fields(Fs, Opts)},
+ maybe_paren(P, Prec, El);
lexpr({block,_,Es}, _, Opts) ->
{list,[{step,'begin',body(Es, Opts)},'end']};
lexpr({'if',_,Cs}, _, Opts) ->
@@ -671,6 +680,16 @@ record_field({typed_record_field,Field,Type}, Opts) ->
record_field({record_field,_,F}, Opts) ->
lexpr(F, 0, Opts).
+map_fields(Fs, Opts) ->
+ tuple(Fs, fun map_field/2, Opts).
+
+map_field({map_field_assoc,_,K,V}, Opts) ->
+ Pl = lexpr(K, 0, Opts),
+ {list,[{step,[Pl,leaf(" =>")],lexpr(V, 0, Opts)}]};
+map_field({map_field_exact,_,K,V}, Opts) ->
+ Pl = lexpr(K, 0, Opts),
+ {list,[{step,[Pl,leaf(" :=")],lexpr(V, 0, Opts)}]}.
+
list({cons,_,H,T}, Es, Opts) ->
list(T, [H|Es], Opts);
list({nil,_}, Es, Opts) ->
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index f02a7921f8..9005fede4d 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -25,6 +25,8 @@
-export([print/1,print/2,print/3,print/4,print/5,print/6]).
+-compile(no_native).
+
%%%
%%% Exported functions
%%%
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index d6a9f4645d..6303465d3d 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -22,7 +22,7 @@
-compile({no_auto_import,[min/2]}).
-export([append/2, append/1, subtract/2, reverse/1,
- nth/2, nthtail/2, prefix/2, suffix/2, last/1,
+ nth/2, nthtail/2, prefix/2, suffix/2, droplast/1, last/1,
seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
delete/2,
unzip/1, unzip3/1, zip/2, zip3/3, zipwith/3, zipwith3/4,
@@ -203,6 +203,19 @@ suffix(Suffix, List) ->
Delta = length(List) - length(Suffix),
Delta >= 0 andalso nthtail(Delta, List) =:= Suffix.
+%% droplast(List) returns the list dropping its last element
+
+-spec droplast(List) -> InitList when
+ List :: [T, ...],
+ InitList :: [T],
+ T :: term().
+
+%% This is the simple recursive implementation
+%% reverse(tl(reverse(L))) is faster on average,
+%% but creates more garbage.
+droplast([_T]) -> [];
+droplast([H|T]) -> [H|droplast(T)].
+
%% last(List) returns the last element in a list.
-spec last(List) -> Last when
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 41de174e7d..57b5072639 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -43,6 +43,8 @@
values/1
]).
+-compile(no_native).
+
%% Shadowed by erl_bif_types: maps:get/3
-spec get(Key,Map) -> Value when
Key :: term(),
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index afc63496d0..7f3cd8f592 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,6 @@
-module(re).
-export([grun/3,urun/3,ucompile/2,replace/3,replace/4,split/2,split/3]).
-%-opaque mp() :: {re_pattern, _, _, _, _}.
-type mp() :: {re_pattern, _, _, _, _}.
-type nl_spec() :: cr | crlf | lf | anycrlf | any.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index d18387568d..974583bc28 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -45,10 +45,13 @@
-type restart() :: 'permanent' | 'transient' | 'temporary'.
-type shutdown() :: 'brutal_kill' | timeout().
-type worker() :: 'worker' | 'supervisor'.
--type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}.
+-type sup_name() :: {'local', Name :: atom()}
+ | {'global', Name :: atom()}
+ | {'via', Module :: module(), Name :: any()}.
-type sup_ref() :: (Name :: atom())
| {Name :: atom(), Node :: node()}
| {'global', Name :: atom()}
+ | {'via', Module :: module(), Name :: any()}
| pid().
-type child_spec() :: {Id :: child_id(),
StartFunc :: mfargs(),
diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl
index e8405ab9a4..ff4502f0b9 100644
--- a/lib/stdlib/src/supervisor_bridge.erl
+++ b/lib/stdlib/src/supervisor_bridge.erl
@@ -101,7 +101,16 @@ handle_cast(_, State) ->
{noreply, State}.
handle_info({'EXIT', Pid, Reason}, State) when State#state.pid =:= Pid ->
- report_error(child_terminated, Reason, State),
+ case Reason of
+ normal ->
+ ok;
+ shutdown ->
+ ok;
+ {shutdown, _Term} ->
+ ok;
+ _ ->
+ report_error(child_terminated, Reason, State)
+ end,
{stop, Reason, State#state{pid = undefined}};
handle_info(_, State) ->
{noreply, State}.
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index 0cd2688e2e..2c1c5acf5a 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -76,11 +76,14 @@ normal(Config) when is_list(Config) ->
[{"a_fun_name",1},
{"a_less_fun_name",1},
{"b_comes_after_a",1},
+ {"expand0arity_entirely",0},
{"module_info",0},
{"module_info",1}]} = edlin_expand:expand(lists:reverse("expand_test:")),
?line {yes,[],[{"a_fun_name",1},
{"a_less_fun_name",1}]} = edlin_expand:expand(
lists:reverse("expand_test:a_")),
+ ?line {yes,"arity_entirely()",[]} = edlin_expand:expand(
+ lists:reverse("expand_test:expand0")),
ok.
quoted_fun(doc) ->
@@ -93,7 +96,7 @@ quoted_fun(Config) when is_list(Config) ->
%% should be no colon after test this time
?line {yes, "test", []} = edlin_expand:expand(lists:reverse("expand_")),
?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")),
- ?line {no,[],[{"'#weird-fun-name'",0},
+ ?line {no,[],[{"'#weird-fun-name'",1},
{"'Quoted_fun_name'",0},
{"'Quoted_fun_too'",0},
{"a_fun_name",1},
@@ -108,7 +111,7 @@ quoted_fun(Config) when is_list(Config) ->
{"a_less_fun_name",1}]} = edlin_expand:expand(
lists:reverse("expand_test1:a_")),
?line {yes,[],
- [{"'#weird-fun-name'",0},
+ [{"'#weird-fun-name'",1},
{"'Quoted_fun_name'",0},
{"'Quoted_fun_too'",0}]} = edlin_expand:expand(
lists:reverse("expand_test1:'")),
@@ -172,6 +175,6 @@ quoted_both(Config) when is_list(Config) ->
[{"'Quoted_fun_name'",0},
{"'Quoted_fun_too'",0}]} = edlin_expand:expand(
lists:reverse("'ExpandTestCaps1':'Quoted_fun_")),
- ?line {yes,"weird-fun-name'(",[]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':'#")),
+ ?line {yes,"weird-fun-name'()",[]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':'#")),
ok.
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index c4b6b35e72..b194c7cb41 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -42,7 +42,8 @@
try_catch/1,
eval_expr_5/1,
zero_width/1,
- eep37/1]).
+ eep37/1,
+ eep43/1]).
%%
%% Define to run outside of test server
@@ -82,7 +83,7 @@ all() ->
simple_cases, unary_plus, apply_atom, otp_5269,
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
otp_8133, otp_10622, funs, try_catch, eval_expr_5, zero_width,
- eep37].
+ eep37, eep43].
groups() ->
[].
@@ -1424,6 +1425,20 @@ eep37(Config) when is_list(Config) ->
720),
ok.
+eep43(Config) when is_list(Config) ->
+ check(fun () -> #{} end, " #{}.", #{}),
+ check(fun () -> #{a => b} end, "#{a => b}.", #{a => b}),
+ check(fun () ->
+ Map = #{a => b},
+ {Map#{a := b},Map#{a => c},Map#{d => e}}
+ end,
+ "begin "
+ " Map = #{a => B=b}, "
+ " {Map#{a := B},Map#{a => c},Map#{d => e}} "
+ "end.",
+ {#{a => b},#{a => c},#{a => b,d => e}}),
+ ok.
+
%% Check the string in different contexts: as is; in fun; from compiled code.
check(F, String, Result) ->
check1(F, String, Result),
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 94b4397a9c..43e679f7ed 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -38,7 +38,7 @@
-export([attributes/1, expr/1, guard/1,
init/1, pattern/1, strict/1, update/1,
otp_5915/1, otp_7931/1, otp_5990/1,
- otp_7078/1, otp_7101/1]).
+ otp_7078/1, otp_7101/1, maps/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -56,7 +56,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[attributes, expr, guard, init,
- pattern, strict, update, {group, tickets}].
+ pattern, strict, update, maps, {group, tickets}].
groups() ->
[{tickets, [],
@@ -402,7 +402,22 @@ update(Config) when is_list(Config) ->
],
?line run(Config, Ts),
ok.
-
+
+maps(Config) when is_list(Config) ->
+ Ts = [<<"-record(rr, {a,b,c}).
+ t() ->
+ R0 = id(#rr{a=1,b=2,c=3}),
+ R1 = id(#rr{a=4,b=5,c=6}),
+ [{R0,R1}] =
+ maps:to_list(#{#rr{a=1,b=2,c=3} => #rr{a=4,b=5,c=6}}),
+ #{#rr{a=1,b=2,c=3} := #rr{a=1,b=2,c=3}} =
+ #{#rr{a=1,b=2,c=3} => R1}#{#rr{a=1,b=2,c=3} := R0},
+ ok.
+
+ id(X) -> X.
+ ">>],
+ run(Config, Ts, [strict_record_tests]),
+ ok.
otp_5915(doc) ->
"Strict record tests in guards.";
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 6e9a9dd7bf..48f50d5f43 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -2827,7 +2827,24 @@ bif_clash(Config) when is_list(Config) ->
{6,erl_lint,{illegal_guard_local_call,{is_tuple,1}}},
{7,erl_lint,{illegal_guard_local_call,{is_list,1}}},
{8,erl_lint,{illegal_guard_local_call,{is_record,3}}},
- {9,erl_lint,{illegal_guard_local_call,{is_record,3}}}],[]}}
+ {9,erl_lint,{illegal_guard_local_call,{is_record,3}}}],[]}},
+ %% We can also suppress all auto imports at once
+ {clash22,
+ <<"-export([size/1, binary_part/2]).
+ -compile(no_auto_import).
+ size([]) ->
+ 0;
+ size({N,_}) ->
+ N;
+ size([_|T]) ->
+ 1+size(T).
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ []}
],
?line [] = run(Config, Ts),
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index cc744ee76b..390322a5fa 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -46,6 +46,7 @@
import_export/1, misc_attrs/1, dialyzer_attrs/1,
hook/1,
neg_indent/1,
+ maps_syntax/1,
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1,
@@ -76,7 +77,8 @@ groups() ->
[{expr, [],
[func, call, recs, try_catch, if_then, receive_after,
bits, head_tail, cond1, block, case1, ops,
- messages, old_mnemosyne_syntax]},
+ messages, old_mnemosyne_syntax, maps_syntax
+ ]},
{attributes, [], [misc_attrs, import_export, dialyzer_attrs]},
{tickets, [],
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
@@ -975,6 +977,25 @@ count_atom(L, A) when is_list(L) ->
count_atom(_, _) ->
0.
+maps_syntax(doc) -> "Maps syntax";
+maps_syntax(suite) -> [];
+maps_syntax(Config) when is_list(Config) ->
+ Ts = [{map_fun_1,
+ <<"t() ->\n"
+ " M0 = #{ 1 => hi, hi => 42, 1.0 => {hi,world}},\n"
+ " M1 = M0#{ 1 := hello, new_val => 1337 },\n"
+ " map_fun_2:val(M1).\n">>},
+ {map_fun_2,
+ <<"val(#{ 1 := V1, hi := V2, new_val := V3}) -> {V1,V2,V3}.\n">>}],
+ compile(Config, Ts),
+
+ ok = pp_expr(<<"#{}">>),
+ ok = pp_expr(<<"#{ a => 1, <<\"hi\">> => \"world\", 33 => 1.0 }">>),
+ ok = pp_expr(<<"#{ a := V1, <<\"hi\">> := V2 } = M">>),
+ ok = pp_expr(<<"M#{ a => V1, <<\"hi\">> := V2 }">>),
+ ok.
+
+
otp_8567(doc) ->
"OTP_8567. Avoid duplicated 'undefined' in record field types.";
otp_8567(suite) -> [];
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 82c3e7ecaf..8dc8b2c291 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -75,6 +75,7 @@
-export([otp_9932/1]).
-export([otp_9423/1]).
-export([otp_10182/1]).
+-export([ets_all/1]).
-export([memory_check_summary/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -151,6 +152,7 @@ all() ->
otp_10182,
otp_9932,
otp_9423,
+ ets_all,
memory_check_summary]. % MUST BE LAST
@@ -5565,7 +5567,19 @@ otp_10182(Config) when is_list(Config) ->
ets:delete(Db),
In = Out.
-
+%% Test that ets:all include/exclude tables that we know are created/deleted
+ets_all(Config) when is_list(Config) ->
+ Pids = [spawn_link(fun() -> ets_all_run() end) || _ <- [1,2]],
+ receive after 3*1000 -> ok end,
+ [begin unlink(P), exit(P,kill) end || P <- Pids],
+ ok.
+
+ets_all_run() ->
+ Table = ets:new(undefined, []),
+ true = lists:member(Table, ets:all()),
+ ets:delete(Table),
+ false = lists:member(Table, ets:all()),
+ ets_all_run().
%
diff --git a/lib/stdlib/test/expand_test.erl b/lib/stdlib/test/expand_test.erl
index 63e4bc3aa0..b9db32c352 100644
--- a/lib/stdlib/test/expand_test.erl
+++ b/lib/stdlib/test/expand_test.erl
@@ -20,7 +20,8 @@
-export([a_fun_name/1,
a_less_fun_name/1,
- b_comes_after_a/1]).
+ b_comes_after_a/1,
+ expand0arity_entirely/0]).
a_fun_name(X) ->
X.
@@ -30,3 +31,6 @@ a_less_fun_name(X) ->
b_comes_after_a(X) ->
X.
+
+expand0arity_entirely () ->
+ ok.
diff --git a/lib/stdlib/test/expand_test1.erl b/lib/stdlib/test/expand_test1.erl
index 11b6fec0f3..1d375e5677 100644
--- a/lib/stdlib/test/expand_test1.erl
+++ b/lib/stdlib/test/expand_test1.erl
@@ -23,7 +23,7 @@
b_comes_after_a/1,
'Quoted_fun_name'/0,
'Quoted_fun_too'/0,
- '#weird-fun-name'/0]).
+ '#weird-fun-name'/1]).
a_fun_name(X) ->
X.
@@ -40,5 +40,5 @@ b_comes_after_a(X) ->
'Quoted_fun_too'() ->
too.
-'#weird-fun-name'() ->
+'#weird-fun-name'(_) ->
weird.
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 92253ef5b9..f4589a8e24 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -61,7 +61,7 @@
zip_unzip/1, zip_unzip3/1, zipwith/1, zipwith3/1,
filter_partition/1,
otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1,
- suffix/1, subtract/1]).
+ suffix/1, subtract/1, droplast/1]).
%% Sort randomized lists until stopped.
%%
@@ -2641,4 +2641,12 @@ sub_non_matching(A, B) ->
sub(A, B) ->
Res = A -- B,
Res = lists:subtract(A, B).
-
+
+%% Test lists:droplast/1
+droplast(Config) when is_list(Config) ->
+ ?line [] = lists:droplast([x]),
+ ?line [x] = lists:droplast([x, y]),
+ ?line {'EXIT', {function_clause, _}} = (catch lists:droplast([])),
+ ?line {'EXIT', {function_clause, _}} = (catch lists:droplast(x)),
+
+ ok.
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 1ffcf31134..877675772f 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -637,6 +637,14 @@ lay_2(Node, Ctxt) ->
sep([follow(text("fun"), D, Ctxt1#ctxt.sub_indent),
text("end")]);
+ named_fun_expr ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:named_fun_expr_name(Node), Ctxt1),
+ D = lay_clauses(erl_syntax:named_fun_expr_clauses(Node),
+ {function,D1}, Ctxt1),
+ sep([follow(text("fun"), D, Ctxt1#ctxt.sub_indent),
+ text("end")]);
+
module_qualifier ->
{PrecL, _Prec, PrecR} = inop_prec(':'),
D1 = lay(erl_syntax:module_qualifier_argument(Node),
@@ -892,6 +900,32 @@ lay_2(Node, Ctxt) ->
beside(floating(text(".")), D2)),
maybe_parentheses(D3, Prec, Ctxt);
+ map_expr ->
+ {PrecL, Prec, _} = inop_prec('#'),
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = par(seq(erl_syntax:map_expr_fields(Node),
+ floating(text(",")), Ctxt1, fun lay/2)),
+ D2 = beside(text("#{"), beside(D1, floating(text("}")))),
+ D3 = case erl_syntax:map_expr_argument(Node) of
+ none ->
+ D2;
+ A ->
+ beside(lay(A, set_prec(Ctxt, PrecL)), D2)
+ end,
+ maybe_parentheses(D3, Prec, Ctxt);
+
+ map_field_assoc ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_field_assoc_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_field_assoc_value(Node), Ctxt1),
+ par([D1, floating(text("=>")), D2], Ctxt1#ctxt.break_indent);
+
+ map_field_exact ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_field_exact_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_field_exact_value(Node), Ctxt1),
+ par([D1, floating(text(":=")), D2], Ctxt1#ctxt.break_indent);
+
rule ->
%% Comments on the name will be repeated; cf.
%% `function'.
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 78a37445ed..2f4c9ac309 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -220,6 +220,16 @@
macro/2,
macro_arguments/1,
macro_name/1,
+ map_expr/1,
+ map_expr/2,
+ map_expr_argument/1,
+ map_expr_fields/1,
+ map_field_assoc/2,
+ map_field_assoc_name/1,
+ map_field_assoc_value/1,
+ map_field_exact/2,
+ map_field_exact_name/1,
+ map_field_exact_value/1,
match_expr/2,
match_expr_body/1,
match_expr_pattern/1,
@@ -580,11 +590,12 @@ type(Node) ->
{lc, _, _, _} -> list_comp;
{bc, _, _, _} -> binary_comp;
{match, _, _, _} -> match_expr;
+ {map, _, _, _} -> map_expr;
+ {map, _, _} -> map_expr;
+ {map_field_assoc, _, _, _} -> map_field_assoc;
+ {map_field_exact, _, _, _} -> map_field_exact;
{op, _, _, _, _} -> infix_expr;
{op, _, _, _} -> prefix_expr;
- {map,_,_} -> map;
- {map_field_assoc,_,_,_} -> map_field_assoc;
- {map_field_exact,_,_,_} -> map_field_exact;
{record, _, _, _, _} -> record_expr;
{record, _, _, _} -> record_expr;
{record_field, _, _, _, _} -> record_access;
@@ -1913,26 +1924,206 @@ atom_literal(Node) ->
%% =====================================================================
+%% @equiv map_expr(none, Fields)
-map_elements(Node) ->
+-spec map_expr([syntaxTree()]) -> syntaxTree().
+
+map_expr(Fields) ->
+ map_expr(none, Fields).
+
+
+%% =====================================================================
+%% @doc Creates an abstract map expression. If `Fields' is
+%% `[F1, ..., Fn]', then if `Argument' is `none', the result represents
+%% "<code>#{<em>F1</em>, ..., <em>Fn</em>}</code>",
+%% otherwise it represents
+%% "<code><em>Argument</em>#{<em>F1</em>, ..., <em>Fn</em>}</code>".
+%%
+%% @see map_expr/1
+%% @see map_expr_argument/1
+%% @see map_expr_fields/1
+%% @see map_field_assoc/2
+%% @see map_field_exact/2
+
+-record(map_expr, {argument :: 'none' | syntaxTree(),
+ fields :: [syntaxTree()]}).
+
+%% `erl_parse' representation:
+%%
+%% {map, Pos, Fields}
+%% {map, Pos, Argument, Fields}
+
+-spec map_expr('none' | syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+map_expr(Argument, Fields) ->
+ tree(map_expr, #map_expr{argument = Argument, fields = Fields}).
+
+revert_map_expr(Node) ->
+ Pos = get_pos(Node),
+ Argument = map_expr_argument(Node),
+ Fields = map_expr_fields(Node),
+ case Argument of
+ none ->
+ {map, Pos, Fields};
+ _ ->
+ {map, Pos, Argument, Fields}
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the argument subtree of a `map_expr' node, if any. If `Node'
+%% represents "<code>#{...}</code>", `none' is returned.
+%% Otherwise, if `Node' represents "<code><em>Argument</em>#{...}</code>",
+%% `Argument' is returned.
+%%
+%% @see map_expr/3
+
+-spec map_expr_argument(syntaxTree()) -> 'none' | syntaxTree().
+
+map_expr_argument(Node) ->
case unwrap(Node) of
- {map, _, List} ->
- List;
- Node1 ->
- data(Node1)
+ {map, _, _} ->
+ none;
+ {map, _, Argument, _} ->
+ Argument;
+ Node1 ->
+ (data(Node1))#map_expr.argument
end.
-map_field_elements({_,_,K,V}) ->
- [K,V].
-map(List) ->
- tree(map, List).
+%% =====================================================================
+%% @doc Returns the list of field subtrees of a `map_expr' node.
+%%
+%% @see map_expr/3
+
+-spec map_expr_fields(syntaxTree()) -> [syntaxTree()].
-map_field_assoc(List) ->
- tree(map_field_assoc, List).
+map_expr_fields(Node) ->
+ case unwrap(Node) of
+ {map, _, Fields} ->
+ Fields;
+ {map, _, _, Fields} ->
+ Fields;
+ Node1 ->
+ (data(Node1))#map_expr.fields
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract map assoc field. The result represents
+%% "<code><em>Name</em> => <em>Value</em></code>".
+%%
+%% @see map_field_assoc_name/1
+%% @see map_field_assoc_value/1
+%% @see map_expr/3
+
+-record(map_field_assoc, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {map_field_assoc, Pos, Name, Value}
+
+-spec map_field_assoc(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_field_assoc(Name, Value) ->
+ tree(map_field_assoc, #map_field_assoc{name = Name, value = Value}).
+
+revert_map_field_assoc(Node) ->
+ Pos = get_pos(Node),
+ Name = map_field_assoc_name(Node),
+ Value = map_field_assoc_value(Node),
+ {map_field_assoc, Pos, Name, Value}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_field_assoc' node.
+%%
+%% @see map_field_assoc/2
+
+-spec map_field_assoc_name(syntaxTree()) -> syntaxTree().
+
+map_field_assoc_name(Node) ->
+ case Node of
+ {map_field_assoc, _, Name, _} ->
+ Name;
+ _ ->
+ (data(Node))#map_field_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_field_assoc' node.
+%%
+%% @see map_field_assoc/2
+
+-spec map_field_assoc_value(syntaxTree()) -> syntaxTree().
+
+map_field_assoc_value(Node) ->
+ case Node of
+ {map_field_assoc, _, _, Value} ->
+ Value;
+ _ ->
+ (data(Node))#map_field_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract map exact field. The result represents
+%% "<code><em>Name</em> := <em>Value</em></code>".
+%%
+%% @see map_field_exact_name/1
+%% @see map_field_exact_value/1
+%% @see map_expr/3
+
+-record(map_field_exact, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {map_field_exact, Pos, Name, Value}
+
+-spec map_field_exact(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_field_exact(Name, Value) ->
+ tree(map_field_exact, #map_field_exact{name = Name, value = Value}).
+
+revert_map_field_exact(Node) ->
+ Pos = get_pos(Node),
+ Name = map_field_exact_name(Node),
+ Value = map_field_exact_value(Node),
+ {map_field_exact, Pos, Name, Value}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_field_exact' node.
+%%
+%% @see map_field_exact/2
+
+-spec map_field_exact_name(syntaxTree()) -> syntaxTree().
+
+map_field_exact_name(Node) ->
+ case Node of
+ {map_field_exact, _, Name, _} ->
+ Name;
+ _ ->
+ (data(Node))#map_field_exact.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_field_exact' node.
+%%
+%% @see map_field_exact/2
+
+-spec map_field_exact_value(syntaxTree()) -> syntaxTree().
+
+map_field_exact_value(Node) ->
+ case Node of
+ {map_field_exact, _, _, Value} ->
+ Value;
+ _ ->
+ (data(Node))#map_field_exact.name
+ end.
-map_field_exact(List) ->
- tree(map_field_exact, List).
%% =====================================================================
%% @doc Creates an abstract tuple. If `Elements' is
@@ -5691,15 +5882,15 @@ fun_expr_arity(Node) ->
-spec named_fun_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree().
named_fun_expr(Name, Clauses) ->
- tree(fun_expr, #named_fun_expr{name = Name, clauses = Clauses}).
+ tree(named_fun_expr, #named_fun_expr{name = Name, clauses = Clauses}).
revert_named_fun_expr(Node) ->
Pos = get_pos(Node),
Name = named_fun_expr_name(Node),
Clauses = [revert_clause(C) || C <- named_fun_expr_clauses(Node)],
case type(Name) of
- var ->
- {named_fun, Pos, concrete(Name), Clauses};
+ variable ->
+ {named_fun, Pos, variable_name(Name), Clauses};
_ ->
Node
end.
@@ -5715,7 +5906,7 @@ revert_named_fun_expr(Node) ->
named_fun_expr_name(Node) ->
case unwrap(Node) of
{named_fun, Pos, Name, _} ->
- set_pos(atom(Name), Pos);
+ set_pos(variable(Name), Pos);
Node1 ->
(data(Node1))#named_fun_expr.name
end.
@@ -6117,6 +6308,12 @@ revert_root(Node) ->
revert_list(Node);
list_comp ->
revert_list_comp(Node);
+ map_expr ->
+ revert_map_expr(Node);
+ map_field_assoc ->
+ revert_map_field_assoc(Node);
+ map_field_exact ->
+ revert_map_field_exact(Node);
match_expr ->
revert_match_expr(Node);
module_qualifier ->
@@ -6358,6 +6555,19 @@ subtrees(T) ->
As ->
[[macro_name(T)], As]
end;
+ map_expr ->
+ case map_expr_argument(T) of
+ none ->
+ [map_expr_fields(T)];
+ V ->
+ [[V], map_expr_fields(T)]
+ end;
+ map_field_assoc ->
+ [[map_field_assoc_name(T)],
+ [map_field_assoc_value(T)]];
+ map_field_exact ->
+ [[map_field_exact_name(T)],
+ [map_field_exact_value(T)]];
match_expr ->
[[match_expr_pattern(T)],
[match_expr_body(T)]];
@@ -6421,12 +6631,6 @@ subtrees(T) ->
try_expr_clauses(T),
try_expr_handlers(T),
try_expr_after(T)];
- map ->
- [map_elements(T)];
- map_field_assoc ->
- [map_field_elements(T)];
- map_field_exact ->
- [map_field_elements(T)];
tuple ->
[tuple_elements(T)]
end
@@ -6502,6 +6706,10 @@ make_tree(list, [P, [S]]) -> list(P, S);
make_tree(list_comp, [[T], B]) -> list_comp(T, B);
make_tree(macro, [[N]]) -> macro(N);
make_tree(macro, [[N], A]) -> macro(N, A);
+make_tree(map_expr, [Fs]) -> map_expr(Fs);
+make_tree(map_expr, [[E], Fs]) -> map_expr(E, Fs);
+make_tree(map_field_assoc, [[K], [V]]) -> map_field_assoc(K, V);
+make_tree(map_field_exact, [[K], [V]]) -> map_field_exact(K, V);
make_tree(match_expr, [[P], [E]]) -> match_expr(P, E);
make_tree(named_fun_expr, [[N], C]) -> named_fun_expr(N, C);
make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N);
@@ -6522,10 +6730,7 @@ make_tree(record_index_expr, [[T], [F]]) ->
make_tree(rule, [[N], C]) -> rule(N, C);
make_tree(size_qualifier, [[N], [A]]) -> size_qualifier(N, A);
make_tree(try_expr, [B, C, H, A]) -> try_expr(B, C, H, A);
-make_tree(tuple, [E]) -> tuple(E);
-make_tree(map, [E]) -> map(E);
-make_tree(map_field_assoc, [E]) -> map_field_assoc(E);
-make_tree(map_field_exact, [E]) -> map_field_exact(E).
+make_tree(tuple, [E]) -> tuple(E).
%% =====================================================================
diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in
index 067663feb4..cd723bcd4d 100644
--- a/lib/test_server/src/configure.in
+++ b/lib/test_server/src/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script for Erlang.
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1997-2013. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2014. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -170,7 +170,30 @@ case $system in
fi
SHLIB_EXTRACT_ALL=""
;;
- *-netbsd*|*-freebsd*|*-openbsd*|*-dragonfly*)
+ *-openbsd*)
+ # Not available on all versions: check for include file.
+ AC_CHECK_HEADER(dlfcn.h, [
+ SHLIB_CFLAGS="-fpic"
+ SHLIB_LD="${CC}"
+ SHLIB_LDFLAGS="$LDFLAGS -shared"
+ SHLIB_SUFFIX=".so"
+ if test X${enable_m64_build} = Xyes; then
+ AC_MSG_ERROR(don't know how to link 64-bit dynamic drivers)
+ fi
+ if test X${enable_m32_build} = Xyes; then
+ AC_MSG_ERROR(don't know how to link 32-bit dynamic drivers)
+ fi
+ ], [
+ # No dynamic loading.
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld"
+ SHLIB_LDFLAGS=""
+ SHLIB_SUFFIX=""
+ AC_MSG_ERROR(don't know how to compile and link dynamic drivers)
+ ])
+ SHLIB_EXTRACT_ALL=""
+ ;;
+ *-netbsd*|*-freebsd*|*-dragonfly*)
# Not available on all versions: check for include file.
AC_CHECK_HEADER(dlfcn.h, [
SHLIB_CFLAGS="-fpic"
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 54be6d4c72..d367e7b5d6 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -176,8 +176,6 @@ module_names(Beams) ->
do_cover_compile(Modules) ->
do_cover_compile1(lists:usort(Modules)). % remove duplicates
-do_cover_compile1([Dont|Rest]) when Dont=:=cover ->
- do_cover_compile1(Rest);
do_cover_compile1([M|Rest]) ->
case {code:is_sticky(M),code:is_loaded(M)} of
{true,_} ->
@@ -405,6 +403,7 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,
ref :: reference(),
pid :: pid(),
mf :: {atom(),atom()},
+ last_known_loc :: term(),
status :: tc_status() | 'undefined',
ret_val :: term(),
comment :: list(char()),
@@ -436,8 +435,9 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
LogOpts, TCCallback)
end),
put(test_server_detected_fail, []),
- St = #st{ref=Ref,pid=Pid,mf={Mod,Func},status=starting,ret_val=[],
- comment="",timeout=infinity,config=hd(Args)},
+ St = #st{ref=Ref,pid=Pid,mf={Mod,Func},last_known_loc=unknown,
+ status=starting,ret_val=[],comment="",timeout=infinity,
+ config=hd(Args)},
run_test_case_msgloop(St).
%% Ugly bug (pre R5A):
@@ -537,7 +537,22 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
St = setup_termination(RetVal, St0#st{config=undefined}),
run_test_case_msgloop(St);
{'EXIT',Pid,Reason} ->
- St = handle_tc_exit(Reason, St0),
+ %% This exit typically happens when an unknown external process
+ %% has caused a test case process to terminate (e.g. if a linked
+ %% process has crashed).
+ St =
+ case Reason of
+ {What,[Loc0={_M,_F,A,[{file,_}|_]}|_]} when
+ is_integer(A) ->
+ Loc = rewrite_loc_item(Loc0),
+ handle_tc_exit(What, St0#st{last_known_loc=[Loc]});
+ {What,[Details,Loc0={_M,_F,A,[{file,_}|_]}|_]} when
+ is_integer(A) ->
+ Loc = rewrite_loc_item(Loc0),
+ handle_tc_exit({What,Details}, St0#st{last_known_loc=[Loc]});
+ _ ->
+ handle_tc_exit(Reason, St0)
+ end,
run_test_case_msgloop(St);
{EndConfPid0,{call_end_conf,Data,_Result}} ->
#st{mf={Mod,Func},config=CurrConf} = St0,
@@ -695,7 +710,7 @@ handle_tc_exit(Reason, #st{config=Config,mf={Mod,Func0},pid=Pid,
{testcase_aborted=E,AbortReason,Loc0} ->
{{E,AbortReason},Loc0};
Other ->
- {Other,unknown}
+ {Other,St#st.last_known_loc}
end,
Func = case Status of
init_per_testcase=F -> {F,Func0};
@@ -837,9 +852,13 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
spawn_link(FwCall);
spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
+ Func1 = case Func of
+ {_InitOrEndPerTC,F} -> F;
+ F -> F
+ end,
FwCall =
fun() ->
- case catch fw_error_notify(Mod,Func,[],
+ case catch fw_error_notify(Mod,Func1,[],
Error,Loc) of
{'EXIT',FwErrorNotifyErr} ->
exit({fw_notify_done,error_notification,
@@ -847,8 +866,8 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
_ ->
ok
end,
- Conf = [{tc_status,{failed,timetrap_timeout}}|CurrConf],
- case catch do_end_tc_call(Mod,Func,
+ Conf = [{tc_status,{failed,Error}}|CurrConf],
+ case catch do_end_tc_call(Mod,Func1,
{Pid,Error,[Conf]},Error) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index e24d6ceacb..dcf905db24 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -487,6 +487,7 @@ init([]) ->
ok
end,
test_server_sup:cleanup_crash_dumps(),
+ test_server_sup:util_start(),
State = #state{jobs=[],finish=false},
TI0 = test_server:init_target_info(),
TargetHost = test_server_sup:hoststr(),
@@ -1055,6 +1056,7 @@ handle_info(_, State) ->
%% test suites (if any) and any possible remainting slave node
terminate(_Reason, State) ->
+ test_server_sup:util_stop(),
case State#state.trc of
false -> ok;
Sock -> test_server_node:stop_tracer_node(Sock)
@@ -1725,30 +1727,33 @@ make_html_link(LinkName, Target, Explanation) ->
ok = write_html_file(LinkName, H).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% start_minor_log_file(Mod, Func) -> AbsName
+%% start_minor_log_file(Mod, Func, ParallelTC) -> AbsName
%% Mod = atom()
%% Func = atom()
+%% ParallelTC = bool()
%% AbsName = string()
%%
%% Create a minor log file for the test case Mod,Func,Args. The log file
-%% will be stored in the log directory under the name <Mod>.<Func>.log.
-%% Some header info will also be inserted into the log file.
+%% will be stored in the log directory under the name <Mod>.<Func>.html.
+%% Some header info will also be inserted into the log file. If the test
+%% case runs in a parallel group, then to avoid clashing file names if the
+%% case is executed more than once, the name <Mod>.<Func>.<Timestamp>.html
+%% is used.
-start_minor_log_file(Mod, Func) ->
+start_minor_log_file(Mod, Func, ParallelTC) ->
MFA = {Mod,Func,1},
LogDir = get(test_server_log_dir_base),
Name0 = lists:flatten(io_lib:format("~w.~w~ts", [Mod,Func,?html_ext])),
Name = downcase(Name0),
AbsName = filename:join(LogDir, Name),
- case file:read_file_info(AbsName) of
- {error,_} -> %% normal case, unique name
+ case (ParallelTC orelse (element(1,file:read_file_info(AbsName))==ok)) of
+ false -> %% normal case, unique name
start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA);
- {ok,_} -> %% special case, duplicate names
- {_,S,Us} = now(),
+ true -> %% special case, duplicate names
+ Tag = test_server_sup:unique_name(),
Name1_0 =
- lists:flatten(io_lib:format("~w.~w.~w.~w~ts", [Mod,Func,S,
- trunc(Us/1000),
- ?html_ext])),
+ lists:flatten(io_lib:format("~w.~w.~ts~ts", [Mod,Func,Tag,
+ ?html_ext])),
Name1 = downcase(Name1_0),
AbsName1 = filename:join(LogDir, Name1),
start_minor_log_file1(Mod, Func, LogDir, AbsName1, MFA)
@@ -3631,7 +3636,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
TSDir = get(test_server_dir),
print(major, "=case ~w:~w", [Mod, Func]),
- MinorName = start_minor_log_file(Mod, Func),
+ MinorName = start_minor_log_file(Mod, Func, self() /= Main),
print(minor, "<a name=\"top\"></a>", [], internal_raw),
MinorBase = filename:basename(MinorName),
print(major, "=logfile ~ts", [filename:basename(MinorName)]),
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 377aa21018..70ead42b1b 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -29,10 +29,12 @@
hostatom/0, hostatom/1, hoststr/0, hoststr/1,
framework_call/2,framework_call/3,framework_call/4,
format_loc/1,
+ util_start/0, util_stop/0, unique_name/0,
call_trace/1]).
-include("test_server_internal.hrl").
-define(crash_dump_tar,"crash_dumps.tar.gz").
-define(src_listing_ext, ".src.html").
+-record(util_state, {starter, latest_name}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% timetrap(Timeout,Scale,Pid) -> Handle
@@ -583,6 +585,69 @@ downcase([], Result) ->
lists:reverse(Result).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% util_start() -> ok
+%%
+%% Start local utility process
+util_start() ->
+ Starter = self(),
+ case whereis(?MODULE) of
+ undefined ->
+ spawn_link(fun() ->
+ register(?MODULE, self()),
+ util_loop(#util_state{starter=Starter})
+ end);
+ _Pid ->
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% util_stop() -> ok
+%%
+%% Stop local utility process
+util_stop() ->
+ try (?MODULE ! {self(),stop}) of
+ _ ->
+ receive {?MODULE,stopped} -> ok
+ after 5000 -> exit(whereis(?MODULE), kill)
+ end
+ catch
+ _:_ ->
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% unique_name() -> string()
+%%
+unique_name() ->
+ ?MODULE ! {self(),unique_name},
+ receive {?MODULE,Name} -> Name
+ after 5000 -> exit({?MODULE,no_util_process})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% util_loop(State) -> ok
+%%
+util_loop(State) ->
+ receive
+ {From,unique_name} ->
+ {_,S,Us} = now(),
+ Ms = trunc(Us/1000),
+ Name = lists:flatten(io_lib:format("~w.~w", [S,Ms])),
+ if Name == State#util_state.latest_name ->
+ timer:sleep(1),
+ self() ! {From,unique_name},
+ util_loop(State);
+ true ->
+ From ! {?MODULE,Name},
+ util_loop(State#util_state{latest_name = Name})
+ end;
+ {From,stop} ->
+ catch unlink(State#util_state.starter),
+ From ! {?MODULE,stopped},
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% call_trace(TraceSpecFile) -> ok
%%
%% Read terms on format {m,Mod} | {f,Mod,Func}
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 189a71a8ce..11d6f7af4d 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -212,6 +212,12 @@ run_all(_Vars) ->
run_some([], _Opts) ->
ok;
+run_some([{Spec,Mod}|Specs], Opts) ->
+ case run(Spec, Mod, Opts) of
+ ok -> ok;
+ Error -> io:format("~p: ~p~n",[{Spec,Mod},Error])
+ end,
+ run_some(Specs, Opts);
run_some([Spec|Specs], Opts) ->
case run(Spec, Opts) of
ok -> ok;
@@ -263,8 +269,17 @@ run(List, Opts) when is_list(List), is_list(Opts) ->
run_some(List, Opts);
%% run/2
-%% Runs one test spec with Options
-run(Testspec, Config) when is_atom(Testspec), is_list(Config) ->
+%% Runs one test spec with list of suites or with options
+run(Testspec, ModsOrConfig) when is_atom(Testspec),
+ is_list(ModsOrConfig) ->
+ case is_list_of_suites(ModsOrConfig) of
+ false ->
+ run(Testspec, {config_list,ModsOrConfig});
+ true ->
+ run_some([{Testspec,M} || M <- ModsOrConfig],
+ [batch])
+ end;
+run(Testspec, {config_list,Config}) ->
Options=check_test_get_opts(Testspec, Config),
IsSmoke=proplists:get_value(smoke,Config),
File=atom_to_list(Testspec),
@@ -310,34 +325,85 @@ run(Testspec, Config) when is_atom(Testspec), is_list(Config) ->
run_test(File, [{spec,[Spec]}], Options);
%% Runs one module in a spec (interactive)
run(Testspec, Mod) when is_atom(Testspec), is_atom(Mod) ->
- run_test({atom_to_list(Testspec), Mod},
+ run_test({atom_to_list(Testspec),Mod},
[{suite,Mod}],
[interactive]).
%% run/3
%% Run one module in a spec with Config
-run(Testspec,Mod,Config) when is_atom(Testspec), is_atom(Mod), is_list(Config) ->
+run(Testspec, Mod, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
- run_test({atom_to_list(Testspec), Mod},
- [{suite,Mod}],
- Options);
-
-%% Runs one testcase in a module.
-run(Testspec, Mod, Case) when is_atom(Testspec), is_atom(Mod), is_atom(Case) ->
+ run_test({atom_to_list(Testspec),Mod},
+ [{suite,Mod}], Options);
+%% Run multiple modules with Config
+run(Testspec, Mods, Config) when is_atom(Testspec),
+ is_list(Mods),
+ is_list(Config) ->
+ run_some([{Testspec,M} || M <- Mods], Config);
+%% Runs one test case in a module.
+run(Testspec, Mod, Case) when is_atom(Testspec),
+ is_atom(Mod),
+ is_atom(Case) ->
Options=check_test_get_opts(Testspec, []),
- Args = [{suite,atom_to_list(Mod)},{testcase,atom_to_list(Case)}],
+ Args = [{suite,Mod},{testcase,Case}],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Runs one or more groups in a module.
+run(Testspec, Mod, Grs={group,_Groups}) when is_atom(Testspec),
+ is_atom(Mod) ->
+ Options=check_test_get_opts(Testspec, []),
+ Args = [{suite,Mod},Grs],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Runs one or more test cases in a module.
+run(Testspec, Mod, TCs={testcase,_Cases}) when is_atom(Testspec),
+ is_atom(Mod) ->
+ Options=check_test_get_opts(Testspec, []),
+ Args = [{suite,Mod},TCs],
run_test(atom_to_list(Testspec), Args, Options).
%% run/4
-%% Run one testcase in a module with Options.
+%% Run one test case in a module with Options.
run(Testspec, Mod, Case, Config) when is_atom(Testspec),
is_atom(Mod),
is_atom(Case),
is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
- Args = [{suite,atom_to_list(Mod)}, {testcase,atom_to_list(Case)}],
+ Args = [{suite,Mod},{testcase,Case}],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Run one or more test cases in a module with Options.
+run(Testspec, Mod, {testcase,Cases}, Config) when is_atom(Testspec),
+ is_atom(Mod) ->
+ run(Testspec, Mod, Cases, Config);
+run(Testspec, Mod, Cases, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_list(Cases),
+ is_list(Config) ->
+ Options=check_test_get_opts(Testspec, Config),
+ Args = [{suite,Mod},Cases],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Run one or more groups in a module with Options.
+run(Testspec, Mod, Grs={group,_Groups}, Config) when is_atom(Testspec),
+ is_atom(Mod) ->
+ Options=check_test_get_opts(Testspec, Config),
+ Args = [{suite,Mod},Grs],
run_test(atom_to_list(Testspec), Args, Options).
+
+is_list_of_suites(List) ->
+ lists:all(fun(Suite) ->
+ S = if is_atom(Suite) -> atom_to_list(Suite);
+ true -> Suite
+ end,
+ try lists:last(string:tokens(S,"_")) of
+ "SUITE" -> true;
+ "suite" -> true;
+ _ -> false
+ catch
+ _:_ -> false
+ end
+ end, List).
+
%% Create a spec to skip all SUITES, this is used when the application
%% to be tested is not part of the OTP release to be tested.
create_skip_spec(Testspec, SuitesToSkip) ->
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 6aeb251ac2..a24d70ed92 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -89,8 +89,9 @@
flush/1,
stop/0, stop/1]).
-export([remote_start/1,get_main_node/0]).
-%-export([bump/5]).
--export([transform/4]). % for test purposes
+
+%% Used internally to ensure we upgrade the code to the latest version.
+-export([main_process_loop/1,remote_process_loop/1]).
-record(main_state, {compiled=[], % [{Module,File}]
imported=[], % [{Module,File,ImportFile}]
@@ -110,7 +111,6 @@
-define(BUMP_REC_NAME,bump).
-record(vars, {module, % atom() Module name
- vsn, % atom()
init_info=[], % [{M,F,A,C,L}]
@@ -230,25 +230,9 @@ compile_directory(Dir) when is_list(Dir) ->
compile_directory(Dir, Options) when is_list(Dir), is_list(Options) ->
case file:list_dir(Dir) of
{ok, Files} ->
-
- %% Filter out all erl files (except cover.erl)
- ErlFileNames =
- lists:filter(fun("cover.erl") ->
- false;
- (File) ->
- case filename:extension(File) of
- ".erl" -> true;
- _ -> false
- end
- end,
- Files),
-
- %% Create a list of .erl file names (incl path) and call
- %% compile_modules/2 with the list of file names.
- ErlFiles = lists:map(fun(ErlFileName) ->
- filename:join(Dir, ErlFileName)
- end,
- ErlFileNames),
+ ErlFiles = [filename:join(Dir, File) ||
+ File <- Files,
+ filename:extension(File) =:= ".erl"],
compile_modules(ErlFiles, Options);
Error ->
Error
@@ -262,7 +246,7 @@ compile_modules([File|Files], Options, Result) ->
R = call({compile, File, Options}),
compile_modules(Files,Options,[R|Result]);
compile_modules([],_Opts,Result) ->
- reverse(Result).
+ lists:reverse(Result).
filter_options(Options) ->
lists:filter(fun(Option) ->
@@ -320,25 +304,9 @@ compile_beam_directory() ->
compile_beam_directory(Dir) when is_list(Dir) ->
case file:list_dir(Dir) of
{ok, Files} ->
-
- %% Filter out all beam files (except cover.beam)
- BeamFileNames =
- lists:filter(fun("cover.beam") ->
- false;
- (File) ->
- case filename:extension(File) of
- ".beam" -> true;
- _ -> false
- end
- end,
- Files),
-
- %% Create a list of .beam file names (incl path) and call
- %% compile_beam/1 for each such file name
- BeamFiles = lists:map(fun(BeamFileName) ->
- filename:join(Dir, BeamFileName)
- end,
- BeamFileNames),
+ BeamFiles = [filename:join(Dir, File) ||
+ File <- Files,
+ filename:extension(File) =:= ".beam"],
compile_beams(BeamFiles);
Error ->
Error
@@ -350,7 +318,7 @@ compile_beams([File|Files],Result) ->
R = compile_beam(File),
compile_beams(Files,[R|Result]);
compile_beams([],Result) ->
- reverse(Result).
+ lists:reverse(Result).
%% analyse(Module) ->
@@ -613,8 +581,11 @@ main_process_loop(State) ->
Compiled = add_compiled(Module, File,
State#main_state.compiled),
Imported = remove_imported(Module,State#main_state.imported),
- main_process_loop(State#main_state{compiled = Compiled,
- imported = Imported});
+ NewState = State#main_state{compiled = Compiled,
+ imported = Imported},
+ %% This module (cover) could have been reloaded. Make
+ %% sure we run the new code.
+ ?MODULE:main_process_loop(NewState);
error ->
reply(From, {error, File}),
main_process_loop(State)
@@ -639,8 +610,11 @@ main_process_loop(State) ->
end,
reply(From,Reply),
Imported = remove_imported(Module,State#main_state.imported),
- main_process_loop(State#main_state{compiled = Compiled,
- imported = Imported});
+ NewState = State#main_state{compiled = Compiled,
+ imported = Imported},
+ %% This module (cover) could have been reloaded. Make
+ %% sure we run the new code.
+ ?MODULE:main_process_loop(NewState);
{error,no_beam} ->
%% The module has first been compiled from .erl, and now
%% someone tries to compile it from .beam
@@ -857,7 +831,7 @@ remote_process_loop(State) ->
{remote,load_compiled,Compiled} ->
Compiled1 = load_compiled(Compiled,State#remote_state.compiled),
remote_reply(State#remote_state.main_node, ok),
- remote_process_loop(State#remote_state{compiled=Compiled1});
+ ?MODULE:remote_process_loop(State#remote_state{compiled=Compiled1});
{remote,unload,UnloadedModules} ->
unload(UnloadedModules),
@@ -1257,12 +1231,12 @@ add_imported(M, F1, ImportFile, [{M,_F2,ImportFiles}|Imported], Acc) ->
dont_import;
false ->
NewEntry = {M, F1, [ImportFile | ImportFiles]},
- {ok, reverse([NewEntry | Acc]) ++ Imported}
+ {ok, lists:reverse([NewEntry | Acc]) ++ Imported}
end;
add_imported(M, F, ImportFile, [H|Imported], Acc) ->
add_imported(M, F, ImportFile, Imported, [H|Acc]);
add_imported(M, F, ImportFile, [], Acc) ->
- {ok, reverse([{M, F, [ImportFile]} | Acc])}.
+ {ok, lists:reverse([{M, F, [ImportFile]} | Acc])}.
%% Removes a module from the list of imported modules and writes a warning
%% This is done when a module is compiled.
@@ -1383,9 +1357,9 @@ do_compile_beam(Module,Beam,UserOptions) ->
{error,E};
encrypted_abstract_code=E ->
{error,E};
- {Vsn,Code} ->
+ {raw_abstract_v1,Code} ->
Forms0 = epp:interpret_file_attribute(Code),
- {Forms,Vars} = transform(Vsn, Forms0, Module, Beam),
+ {Forms,Vars} = transform(Forms0, Module),
%% We need to recover the source from the compilation
%% info otherwise the newly compiled module will have
@@ -1400,7 +1374,7 @@ do_compile_beam(Module,Beam,UserOptions) ->
{module, Module} ->
%% Store info about all function clauses in database
- InitInfo = reverse(Vars#vars.init_info),
+ InitInfo = lists:reverse(Vars#vars.init_info),
ets:insert(?COVER_CLAUSE_TABLE, {Module, InitInfo}),
%% Store binary code so it can be loaded on remote nodes
@@ -1411,7 +1385,11 @@ do_compile_beam(Module,Beam,UserOptions) ->
_Error ->
do_clear(Module),
error
- end
+ end;
+ {_VSN,_Code} ->
+ %% Wrong version of abstract code. Just report that there
+ %% is no abstract code.
+ {error,no_abstract_code}
end.
get_abstract_code(Module, Beam) ->
@@ -1445,28 +1423,9 @@ get_compile_info(Module, Beam) ->
[]
end.
-transform(Vsn, Code, Module, Beam) when Vsn=:=abstract_v1; Vsn=:=abstract_v2 ->
- Vars0 = #vars{module=Module, vsn=Vsn},
- MainFile=find_main_filename(Code),
- {ok, MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),
-
- %% Add module and export information to the munged forms
- %% Information about module_info must be removed as this function
- %% is added at compilation
- {ok, {Module, [{exports,Exports1}]}} = beam_lib:chunks(Beam, [exports]),
- Exports2 = lists:filter(fun(Export) ->
- case Export of
- {module_info,_} -> false;
- _ -> true
- end
- end,
- Exports1),
- Forms = [{attribute,1,module,Module},
- {attribute,2,export,Exports2}]++ MungedForms,
- {Forms,Vars};
-transform(Vsn=raw_abstract_v1, Code, Module, _Beam) ->
+transform(Code, Module) ->
MainFile=find_main_filename(Code),
- Vars0 = #vars{module=Module, vsn=Vsn},
+ Vars0 = #vars{module=Module},
{ok,MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),
{MungedForms,Vars}.
@@ -1486,7 +1445,7 @@ transform_2([Form0|Forms],MungedForms,Vars,MainFile,Switch) ->
transform_2(Forms,[MungedForm|MungedForms],Vars2,MainFile,NewSwitch)
end;
transform_2([],MungedForms,Vars,_,_) ->
- {ok, reverse(MungedForms), Vars}.
+ {ok, lists:reverse(MungedForms), Vars}.
%% Expand short-circuit Boolean expressions.
expand(Expr) ->
@@ -1553,14 +1512,9 @@ aux_var(Vars, N) ->
end.
%% This code traverses the abstract code, stored as the abstract_code
-%% chunk in the BEAM file, as described in absform(3) for Erlang/OTP R8B
-%% (Vsn=abstract_v2).
-%% The abstract format after preprocessing differs slightly from the abstract
-%% format given eg using epp:parse_form, this has been noted in comments.
-%% The switch is turned off when we encounter other files then the main file.
+%% chunk in the BEAM file, as described in absform(3).
+%% The switch is turned off when we encounter other files than the main file.
%% This way we will be able to exclude functions defined in include files.
-munge({function,0,module_info,_Arity,_Clauses},_Vars,_MainFile,_Switch) ->
- ignore; % module_info will be added again when the forms are recompiled
munge({function,Line,Function,Arity,Clauses},Vars,_MainFile,on) ->
Vars2 = Vars#vars{function=Function,
arity=Arity,
@@ -1618,7 +1572,7 @@ munge_clauses([Clause|Clauses], Vars, Lines, MClauses) ->
MClauses])
end;
munge_clauses([], Vars, Lines, MungedClauses) ->
- {reverse(MungedClauses), Vars#vars{lines = Lines}}.
+ {lists:reverse(MungedClauses), Vars#vars{lines = Lines}}.
munge_body(Expr, Vars) ->
munge_body(Expr, Vars, [], []).
@@ -1662,7 +1616,7 @@ munge_body([Expr|Body], Vars, MungedBody, LastExprBumpLines) ->
munge_body(Body, Vars3, MungedExprs1, NewBumps)
end;
munge_body([], Vars, MungedBody, _LastExprBumpLines) ->
- {reverse(MungedBody), Vars}.
+ {lists:reverse(MungedBody), Vars}.
%%% Fix last expression (OTP-8188). A typical example:
%%%
@@ -1800,16 +1754,16 @@ munge_expr({match,Line,ExprL,ExprR}, Vars) ->
munge_expr({tuple,Line,Exprs}, Vars) ->
{MungedExprs, Vars2} = munge_exprs(Exprs, Vars, []),
{{tuple,Line,MungedExprs}, Vars2};
-munge_expr({record,Line,Expr,Exprs}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
- {MungedExprName, Vars2} = munge_expr(Expr, Vars),
+munge_expr({record,Line,Name,Exprs}, Vars) ->
+ {MungedExprFields, Vars2} = munge_exprs(Exprs, Vars, []),
+ {{record,Line,Name,MungedExprFields}, Vars2};
+munge_expr({record,Line,Arg,Name,Exprs}, Vars) ->
+ {MungedArg, Vars2} = munge_expr(Arg, Vars),
{MungedExprFields, Vars3} = munge_exprs(Exprs, Vars2, []),
- {{record,Line,MungedExprName,MungedExprFields}, Vars3};
+ {{record,Line,MungedArg,Name,MungedExprFields}, Vars3};
munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
- {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
- {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
- {{record_field,Line,MungedExprL,MungedExprR}, Vars3};
+ {MungedExprR, Vars2} = munge_expr(ExprR, Vars),
+ {{record_field,Line,ExprL,MungedExprR}, Vars2};
munge_expr({cons,Line,ExprH,ExprT}, Vars) ->
{MungedExprH, Vars2} = munge_expr(ExprH, Vars),
{MungedExprT, Vars3} = munge_expr(ExprT, Vars2),
@@ -1871,23 +1825,10 @@ munge_expr({'try',Line,Body,Clauses,CatchClauses,After}, Vars) ->
{MungedAfter, Vars4} = munge_body(After, Vars3),
{{'try',Line,MungedBody,MungedClauses,MungedCatchClauses,MungedAfter},
Vars4};
-%% Difference in abstract format after preprocessing: Funs get an extra
-%% element Extra.
-%% NOT NECESSARY FOR Vsn=raw_abstract_v1
-munge_expr({'fun',Line,{function,Name,Arity},_Extra}, Vars) ->
- {{'fun',Line,{function,Name,Arity}}, Vars};
-munge_expr({'fun',Line,{clauses,Clauses},_Extra}, Vars) ->
- {MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
- {{'fun',Line,{clauses,MungedClauses}}, Vars2};
munge_expr({'fun',Line,{clauses,Clauses}}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
{MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
{{'fun',Line,{clauses,MungedClauses}}, Vars2};
-munge_expr({named_fun,Line,Name,Clauses,_Extra}, Vars) ->
- {MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
- {{named_fun,Line,Name,MungedClauses}, Vars2};
munge_expr({named_fun,Line,Name,Clauses}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
{MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
{{named_fun,Line,Name,MungedClauses}, Vars2};
munge_expr({bin,Line,BinElements}, Vars) ->
@@ -1908,7 +1849,7 @@ munge_exprs([Expr|Exprs], Vars, MungedExprs) ->
{MungedExpr, Vars2} = munge_expr(Expr, Vars),
munge_exprs(Exprs, Vars2, [MungedExpr|MungedExprs]);
munge_exprs([], Vars, MungedExprs) ->
- {reverse(MungedExprs), Vars}.
+ {lists:reverse(MungedExprs), Vars}.
%% Every qualifier is decorated with a counter.
munge_qualifiers(Qualifiers, Vars) ->
@@ -1927,7 +1868,7 @@ munge_qs([Expr|Qs], Vars, MQs) ->
{MungedExpr, Vars2} = munge_expr(Expr, Vars),
munge_qs1(Qs, L, MungedExpr, Vars, Vars2, MQs);
munge_qs([], Vars, MQs) ->
- {reverse(MQs), Vars}.
+ {lists:reverse(MQs), Vars}.
munge_qs1(Qs, Line, NQ, Vars, Vars2, MQs) ->
case new_bumps(Vars2, Vars) of
@@ -2120,7 +2061,7 @@ merge_clauses([{{M,F,A,_C1},R1},{{M,F,A,C2},R2}|Clauses], MFun, Result) ->
merge_clauses([{{M,F,A,_C},R}|Clauses], MFun, Result) ->
merge_clauses(Clauses, MFun, [{{M,F,A},R}|Result]);
merge_clauses([], _Fun, Result) ->
- reverse(Result).
+ lists:reverse(Result).
merge_functions([{_MFA,R}|Functions], MFun) ->
merge_functions(Functions, MFun, R);
@@ -2441,14 +2382,6 @@ not_loaded(_Module,_Else, State) ->
%%%--Div-----------------------------------------------------------------
-reverse(List) ->
- reverse(List,[]).
-reverse([H|T],Acc) ->
- reverse(T,[H|Acc]);
-reverse([],Acc) ->
- Acc.
-
-
escape_lt_and_gt(Rawline,HTML) when HTML =/= true ->
Rawline;
escape_lt_and_gt(Rawline,_HTML) ->
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index bd71218474..ec61c57cec 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -22,7 +22,8 @@
suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([start/1, compile/1, analyse/1, misc/1, stop/1,
+-export([coverage/1, coverage_analysis/1,
+ start/1, compile/1, analyse/1, misc/1, stop/1,
distribution/1, reconnect/1, die_and_reconnect/1,
dont_reconnect_after_stop/1, stop_node_after_disconnect/1,
export_import/1,
@@ -30,6 +31,8 @@
otp_8188/1, otp_8270/1, otp_8273/1, otp_8340/1,
otp_10979_hanging_node/1, compile_beam_opts/1, eep37/1]).
+-export([do_coverage/1]).
+
-include_lib("test_server/include/test_server.hrl").
%%----------------------------------------------------------------------
@@ -46,25 +49,25 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ NoStartStop = [eif,otp_5305,otp_5418,otp_7095,otp_8273,
+ otp_8340,otp_8188,compile_beam_opts,eep37],
+ StartStop = [start, compile, analyse, misc, stop,
+ distribution, reconnect, die_and_reconnect,
+ dont_reconnect_after_stop, stop_node_after_disconnect,
+ export_import, otp_5031, otp_6115,
+ otp_8270, otp_10979_hanging_node],
case whereis(cover_server) of
undefined ->
- [start, compile, analyse, misc, stop,
- distribution, reconnect, die_and_reconnect,
- dont_reconnect_after_stop, stop_node_after_disconnect,
- export_import, otp_5031, eif, otp_5305, otp_5418,
- otp_6115, otp_7095, otp_8188, otp_8270, otp_8273,
- otp_8340, otp_10979_hanging_node, compile_beam_opts, eep37];
+ [coverage,StartStop ++ NoStartStop];
_pid ->
- {skip,
- "It looks like the test server is running "
- "cover. Can't run cover test."}
+ [coverage|NoStartStop++[coverage_analysis]]
end.
groups() ->
[].
init_per_suite(Config) ->
- Config.
+ [{ct_is_running_cover,whereis(cover_server) =/= undefined}|Config].
end_per_suite(_Config) ->
ok.
@@ -90,13 +93,64 @@ init_per_testcase(TC, Config) when TC =:= misc;
init_per_testcase(_TestCase, Config) ->
Config.
-end_per_testcase(TestCase, _Config) ->
- case lists:member(TestCase,[start,compile,analyse,misc]) of
+end_per_testcase(TestCase, Config) ->
+ NoStop = [start,compile,analyse,misc],
+ DontStop = proplists:get_bool(ct_is_running_cover, Config) orelse
+ lists:member(TestCase, NoStop),
+ case DontStop of
true -> ok;
false -> cover:stop()
end,
ok.
+coverage(Config) when is_list(Config) ->
+ {ok,?MODULE} = cover:compile_beam(?MODULE),
+ ?MODULE:do_coverage(Config).
+
+do_coverage(Config) ->
+ Outdir = ?config(priv_dir, Config),
+ ExportFile = filename:join(Outdir, "export"),
+ ok = cover:export(ExportFile, ?MODULE),
+ {error,{already_started,_}} = cover:start(),
+ {error,_} = cover:compile_beam(non_existing_module),
+ _ = cover:which_nodes(),
+ _ = cover:modules(),
+ _ = cover:imported(),
+ {error,{not_cover_compiled,lists}} = cover:analyze(lists),
+
+ %% Cover escaping of '&' in HTML files.
+
+ case proplists:get_bool(ct_is_running_cover, Config) of
+ false ->
+ %% Cover server was implicitly started when this module
+ %% was cover-compiled. We must stop the cover server, but
+ %% we must ensure that this module is not on the call
+ %% stack when it is unloaded. Therefore, the call that
+ %% follows MUST be tail-recursive.
+ cover:stop();
+ true ->
+ %% Cover server was started by common_test; don't stop it.
+ ok
+ end.
+
+%% This test case will only be run when common_test is running cover.
+coverage_analysis(Config) when is_list(Config) ->
+ {ok,Analysis1} = cover:analyze(?MODULE),
+ io:format("~p\n", [Analysis1]),
+ {ok,Analysis2} = cover:analyze(?MODULE, calls),
+ io:format("~p\n", [Analysis2]),
+ {ok,_Analysis3} = cover:analyze(?MODULE, calls, line),
+
+ Outdir = ?config(priv_dir, Config),
+ Outfile = filename:join(Outdir, ?MODULE),
+
+ {ok,Outfile} = cover:analyze_to_file(?MODULE, Outfile),
+ {ok,Contents} = file:read_file(Outfile),
+ ok = file:delete(Outfile),
+ ok = io:put_chars(Contents),
+ {ok,Outfile} = cover:analyze_to_file(?MODULE, Outfile, [html]),
+ ok.
+
start(suite) -> [];
start(Config) when is_list(Config) ->
?line ok = file:set_cwd(?config(data_dir, Config)),
@@ -759,7 +813,6 @@ eif(Config) when is_list(Config) ->
%% in cover_inc.beam - not the ones from the included file.
?line cover_inc:func(),
?line {ok, [_, _]} = cover:analyse(cover_inc, line),
- ?line cover:stop(),
ok.
otp_5305(suite) -> [];
@@ -775,7 +828,6 @@ otp_5305(Config) when is_list(Config) ->
">>,
?line ok = file:write_file(File, Test),
?line {ok, t} = cover:compile(File),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -790,7 +842,6 @@ otp_5418(Config) when is_list(Config) ->
?line ok = file:write_file(File, Test),
?line {ok, t} = cover:compile(File),
?line {ok,{t,{0,0}}} = cover:analyse(t, module),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -952,7 +1003,6 @@ otp_7095(Config) when is_list(Config) ->
{{t,67},1},{{t,69},1},{{t,71},1},{{t,74},1},
{{t,76},0},{{t,78},1},
{{t,82},2}]} = cover:analyse(t, calls, line),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -1028,7 +1078,6 @@ otp_8273(Config) when is_list(Config) ->
">>,
?line File = cc_mod(t, Test, Config),
?line ok = t:t(),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -1066,7 +1115,6 @@ otp_8188(Config) when is_list(Config) ->
?line File = cc_mod(t, Test, Config),
?line false = t:test(nok),
?line {ok,[{{t,11},1},{{t,12},1}]} = cover:analyse(t, calls, line),
- ?line cover:stop(),
?line ok = file:delete(File),
%% Bit string comprehensions are now traversed;
@@ -1433,6 +1481,8 @@ compile_beam_opts(Config) when is_list(Config) ->
export_all,
debug_info,
return_errors]),
+ code:purge(t),
+ code:delete(t),
Exports =
[{func1,0},
{macro, 0},
@@ -1443,7 +1493,6 @@ compile_beam_opts(Config) when is_list(Config) ->
Exports = t:module_info(exports),
{ok, t} = cover:compile_beam("t"),
Exports = t:module_info(exports),
- cover:stop(),
ok = file:delete("t.beam"),
ok = file:set_cwd(Cwd),
ok.
diff --git a/lib/wx/api_gen/Makefile b/lib/wx/api_gen/Makefile
index 8adb485ba9..3e41ac7bc5 100644
--- a/lib/wx/api_gen/Makefile
+++ b/lib/wx/api_gen/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2012. All Rights Reserved.
+# Copyright Ericsson AB 2008-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -60,7 +60,7 @@ $(GL): glxml_generated $(GL_COMP_T) glapi.conf
erl -noshell -run gl_gen code && touch gl_code_generated
%.beam: %.erl wx_gen.hrl gl_gen.hrl
- $(ERLC) -W $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) $< -o$(EBIN)
+ $(ERLC) -W $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
# TODO split cleans into separate targets?
complete_clean:
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
index 9c5f46b253..5d20019d8f 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
@@ -1,17 +1,5 @@
-case 98: { // wxeEvtListener::wxeEvtListener
- wxeEvtListener *Result = new wxeEvtListener(Ecmd.port);
- rt.addRef(getRef((void *)Result,memenv), "wxeEvtListener");
- break;
-}
-case 99: { // wxeEvtListener::destroy
- wxObject *This = (wxObject *) getPtr(bp,memenv);
- rt.addAtom("ok");
- delete This;
- break;
-}
-case 100: { // wxEvtHandler::Connect
- wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
+case 100: { // wxEvtHandler::Connect
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
int * lastId = (int *) bp; bp += 4;
@@ -22,20 +10,22 @@ case 100: { // wxEvtHandler::Connect
int * eventTypeLen = (int *) bp; bp += 4;
int * class_nameLen = (int *) bp; bp += 4;
- if(*haveUserData) {
+ if(*haveUserData) {
userData = new wxeErlTerm(Ecmd.bin[0]);
}
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
char *class_name = bp; bp+= *class_nameLen;
if(eventType > 0 ) {
- wxeCallbackData * Evt_cb = new wxeCallbackData(Ecmd.caller,getRef(This, memenv),
- class_name,*fun_cb,
- *skip, userData, Listener);
+ wxeEvtListener * Evt_cb = new wxeEvtListener(Ecmd.caller,getRef(This, memenv),
+ class_name,*fun_cb,
+ *skip, userData, Ecmd.port);
This->Connect((int) *winid,(int) *lastId,eventType,
(wxObjectEventFunction)(wxEventFunction) &wxeEvtListener::forward,
- Evt_cb, Listener);
+ Evt_cb, Evt_cb);
rt.addAtom("ok");
+ rt.addRef(getRef((void *)Evt_cb,memenv), "wxeEvtListener");
+ rt.addTupleCount(2);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
@@ -43,7 +33,7 @@ case 100: { // wxEvtHandler::Connect
}
break;
}
-case 101: { // wxEvtHandler::Disconnect
+case 101: { // wxEvtHandler::Disconnect
wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
@@ -53,14 +43,14 @@ case 101: { // wxEvtHandler::Disconnect
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
if(eventType > 0) {
bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
- (wxObjectEventFunction)(wxEventFunction)
- &wxeEvtListener::forward,
- NULL, Listener);
+ (wxObjectEventFunction)(wxEventFunction)
+ &wxeEvtListener::forward,
+ NULL, Listener);
rt.addBool(Result);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
- rt.addTupleCount(2);
+ rt.addTupleCount(2);
}
break;
}
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
index c5802af679..c9726fd475 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
@@ -27,15 +27,11 @@
-export([connect/2, connect/3, disconnect/1, disconnect/2, disconnect/3]).
%% internal exports
--export([connect_impl/3, disconnect_impl/2, disconnect_impl/3,
- new_evt_listener/0, destroy_evt_listener/1,
- get_callback/1, replace_fun_with_id/2]).
+-export([connect_impl/2, disconnect_impl/2]).
-export_type([wxEvtHandler/0, wx/0, event/0]).
-type wxEvtHandler() :: wx:wx_object().
--record(evh, {et=null,id=?wxID_ANY,lastId=?wxID_ANY,skip=undefined,userdata=[],cb=0}).
-
%% @doc Equivalent to {@link connect/3. connect(This, EventType, [])}
-spec connect(This::wxEvtHandler(), EventType::wxEventType()) -> ok.
connect(This, EventType) ->
@@ -130,54 +126,34 @@ disconnect(This=#wx_ref{type=ThisT,ref=_ThisRef}, EventType, Opts) ->
%% @hidden
-connect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType,
- skip=Skip, userdata=Userdata, cb=FunID})
+connect_impl(#wx_ref{type=ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ skip=Skip, userdata=Userdata, cb=FunID})
when is_integer(FunID)->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
ThisTypeBin = list_to_binary([atom_to_list(ThisT)|[0]]),
UD = if Userdata =:= [] -> 0;
- true ->
+ true ->
wxe_util:send_bin(term_to_binary(Userdata)),
1
end,
- wxe_util:call(100, <<EvtList:32/?UI,ThisRef:32/?UI,
+ wxe_util:call(100, <<ThisRef:32/?UI,
Winid:32/?UI,LastId:32/?UI,
(wxe_util:from_bool(Skip)):32/?UI,
UD:32/?UI,
FunID:32/?UI,
(size(EventTypeBin)):32/?UI,
- (size(ThisTypeBin)):32/?UI,
+ (size(ThisTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary,ThisTypeBin/binary>>).
%% @hidden
-disconnect_impl(Listener, Object) ->
- disconnect_impl(Listener, Object, #evh{}).
-%% @hidden
-disconnect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=_ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType}) ->
+disconnect_impl(#wx_ref{type=_ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ handler=#wx_ref{type=wxeEvtListener,ref=EvtList}}) ->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
- wxe_util:call(101, <<EvtList:32/?UI,
+ wxe_util:call(101, <<EvtList:32/?UI,
ThisRef:32/?UI,Winid:32/?UI,LastId:32/?UI,
(size(EventTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary>>).
-
-%% @hidden
-new_evt_listener() ->
- wxe_util:call(98, <<>>).
-
-%% @hidden
-destroy_evt_listener(#wx_ref{type=wxeEvtListener,ref=EvtList}) ->
- wxe_util:call(99, <<EvtList:32/?UI>>).
-
-%% @hidden
-get_callback(#evh{cb=Callback}) ->
- Callback.
-
-%% @hidden
-replace_fun_with_id(Evh, Id) ->
- Evh#evh{cb=Id}.
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index 3ca8cd7d14..0f28b3dd5e 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -25,7 +25,7 @@
-include_lib("xmerl/include/xmerl.hrl").
--import(lists, [foldl/3,foldr/3,reverse/1, keysearch/3, map/2, filter/2]).
+-import(lists, [foldl/3,foldr/3,reverse/1,keysearch/3,map/2,filter/2,droplast/1]).
-import(proplists, [get_value/2,get_value/3]).
-compile(export_all).
@@ -69,7 +69,7 @@ gen_code() ->
gen_xml() ->
%% {ok, Defs} = file:consult("wxapi.conf"),
-%% Rel = reverse(tl(reverse(os:cmd("wx-config --release")))),
+%% Rel = droplast(os:cmd("wx-config --release")),
%% Dir = " /usr/include/wx-" ++ Rel ++ "/wx/",
%% Files0 = [Dir ++ File || {class, File, _, _, _} <- Defs],
%% Files1 = [Dir ++ File || {doxygen, File} <- Defs],
@@ -172,7 +172,7 @@ parse_defs([], Acc) -> reverse(Acc).
meta_info(C=#class{name=CName,methods=Ms0}) ->
Ms = lists:append(Ms0),
HaveConstructor = lists:keymember(constructor, #method.method_type, Ms),
- case lists:keysearch(destructor, #method.method_type, Ms) of
+ case keysearch(destructor, #method.method_type, Ms) of
false when HaveConstructor ->
Dest = #method{name = "destroy", id = next_id(func_id),
method_type = destructor, params = [this(CName)]},
@@ -288,7 +288,7 @@ parse_attr1([{{attr,_}, #xmlElement{content=C, attributes=Attrs}}|R], AttrList0,
parse_attr1([{_Id,_}|R],AttrList,Info, Res) ->
parse_attr1(R,AttrList,Info, Res);
parse_attr1([],Left,_, Res) ->
- {lists:reverse(Res), Left}.
+ {reverse(Res), Left}.
attr_acc(#param{name=N}, List) ->
Name = list_to_atom(N),
@@ -994,7 +994,7 @@ erl_skip_opt2([F={_,{N,In,_},M=#method{where=Where}}|Ms],Acc1,Acc2,Check) ->
[] ->
erl_skip_opt2(Ms,[F|Acc1],[M#method{where=erl_no_opt}|Acc2],[]);
_ ->
- Skipped = reverse(tl(reverse(In))),
+ Skipped = droplast(In),
T = fun({_,{_,Args,_},_}) -> true =:= types_differ(Skipped,Args) end,
case lists:all(T, Check) of
true ->
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 5ac57e4929..ea5d89be72 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -1190,15 +1190,6 @@ find_id(OtherClass) ->
encode_events(Evs) ->
?WTC("encode_events"),
- w("void wxeEvtListener::forward(wxEvent& event)~n"
- "{~n"
- "#ifdef DEBUG~n"
- " if(!sendevent(&event, port))~n"
- " fprintf(stderr, \"Couldn't send event!\\r\\n\");~n"
- "#else~n"
- "sendevent(&event, port);~n"
- "#endif~n"
- "}~n~n"),
w("int getRef(void* ptr, wxeMemEnv* memenv)~n"
"{~n"
" WxeApp * app = (WxeApp *) wxTheApp;~n"
@@ -1209,7 +1200,7 @@ encode_events(Evs) ->
" char * evClass = NULL;~n"
" wxMBConvUTF32 UTFconverter;~n"
" wxeEtype *Etype = etmap[event->GetEventType()];~n"
- " wxeCallbackData *cb = (wxeCallbackData *)event->m_callbackUserData;~n"
+ " wxeEvtListener *cb = (wxeEvtListener *)event->m_callbackUserData;~n"
" WxeApp * app = (WxeApp *) wxTheApp;~n"
" wxeMemEnv *memenv = app->getMemEnv(port);~n"
" if(!memenv) return 0;~n~n"
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index c87187edc5..a4b03d3fd1 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -137,15 +137,27 @@ gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
w("%% inherited exports~n",[]),
Done0 = ["Destroy", "New", "Create", "destroy", "new", "create"],
Done = gb_sets:from_list(Done0 ++ [M|| #method{name=M} <- lists:append(Ms)]),
- {_, InExported} = gen_inherited(Parents, Done, []),
- w("-export([~s]).~n~n", [args(fun(EF) -> EF end, ",",
- lists:usort(["parent_class/1"|InExported]),
+ {_, InExported0} = gen_inherited(Parents, Done, []),
+ InExported = lists:ukeysort(2, [{?MODULE,{"parent_class","1"},false}|InExported0]),
+ w("-export([~s]).~n~n", [args(fun({_M,{F,A},_Dep}) -> F ++ "/" ++ A end, ",",
+ InExported,
60)]),
w("-export_type([~s/0]).~n", [Name]),
case lists:filter(fun({_F,Depr}) -> Depr end, ExportList) of
[] -> ok;
Depr -> w("-deprecated([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", Depr, 60)])
end,
+ case lists:filter(fun({_,_,Depr}) -> Depr end, InExported) of
+ [] -> ok;
+ NoWDepr -> w("-compile([~s]).~n~n",
+ [args(fun({M,{F,A},_}) ->
+ DStr=io_lib:format("{nowarn_deprecated_function, {~s,~s,~s}}",
+ [M,F,A]),
+ lists:flatten(DStr)
+ end, ",", NoWDepr, 60)])
+ end,
+
+
w("%% @hidden~n", []),
parents_check(Parents),
w("-type ~s() :: wx:wx_object().~n", [Name]),
@@ -375,7 +387,7 @@ gen_inherited([Parent|Ps], Done0, Exported0) ->
{Done,Exported} = gen_inherited_ms(Ms, Class, Done0, gb_sets:empty(), Exported0),
gen_inherited(Ps, gb_sets:union(Done,Done0), Exported).
-gen_inherited_ms([[#method{name=Name,alias=A,params=Ps0,where=W,method_type=MT}|_]|R],
+gen_inherited_ms([[M=#method{name=Name,alias=A,params=Ps0,where=W,method_type=MT}|_]|R],
Class,Skip,Done, Exported)
when W =/= merged_c ->
case gb_sets:is_member(Name,Skip) of
@@ -399,8 +411,10 @@ gen_inherited_ms([[#method{name=Name,alias=A,params=Ps0,where=W,method_type=MT}|
_ when W =:= erl_no_opt -> 0;
_ -> 1
end,
- Export = erl_func_name(Name,A) ++ "/" ++ integer_to_list(length(Args) + OptLen),
- gen_inherited_ms(R,Class,Skip, gb_sets:add(Name,Done), [Export|Exported]);
+ {_, Depr} = deprecated(M,ignore),
+ Export = {Class,{erl_func_name(Name,A),integer_to_list(length(Args) + OptLen)}, Depr},
+ gen_inherited_ms(R,Class,Skip, gb_sets:add(Name,Done),
+ [Export|Exported]);
_ ->
gen_inherited_ms(R,Class, Skip, Done, Exported)
end;
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index a1b4d090b3..1bd17366a2 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -316,16 +316,6 @@ void initEventTable()
}
}
-void wxeEvtListener::forward(wxEvent& event)
-{
-#ifdef DEBUG
- if(!sendevent(&event, port))
- fprintf(stderr, "Couldn't send event!\r\n");
-#else
-sendevent(&event, port);
-#endif
-}
-
int getRef(void* ptr, wxeMemEnv* memenv)
{
WxeApp * app = (WxeApp *) wxTheApp;
@@ -338,7 +328,7 @@ bool sendevent(wxEvent *event, ErlDrvTermData port)
char * evClass = NULL;
wxMBConvUTF32 UTFconverter;
wxeEtype *Etype = etmap[event->GetEventType()];
- wxeCallbackData *cb = (wxeCallbackData *)event->m_callbackUserData;
+ wxeEvtListener *cb = (wxeEvtListener *)event->m_callbackUserData;
WxeApp * app = (WxeApp *) wxTheApp;
wxeMemEnv *memenv = app->getMemEnv(port);
if(!memenv) return 0;
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 82dd414911..3f5cb4c0f5 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -67,20 +67,8 @@ void WxeApp::wxe_dispatch(wxeCommand& Ecmd)
case WXE_INIT_OPENGL:
wxe_initOpenGL(rt, bp);
break;
-case 98: { // wxeEvtListener::wxeEvtListener
- wxeEvtListener *Result = new wxeEvtListener(Ecmd.port);
- rt.addRef(getRef((void *)Result,memenv), "wxeEvtListener");
- break;
-}
-case 99: { // wxeEvtListener::destroy
- wxObject *This = (wxObject *) getPtr(bp,memenv);
- rt.addAtom("ok");
- delete This;
- break;
-}
-case 100: { // wxEvtHandler::Connect
- wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
+case 100: { // wxEvtHandler::Connect
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
int * lastId = (int *) bp; bp += 4;
@@ -91,20 +79,22 @@ case 100: { // wxEvtHandler::Connect
int * eventTypeLen = (int *) bp; bp += 4;
int * class_nameLen = (int *) bp; bp += 4;
- if(*haveUserData) {
+ if(*haveUserData) {
userData = new wxeErlTerm(Ecmd.bin[0]);
}
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
char *class_name = bp; bp+= *class_nameLen;
if(eventType > 0 ) {
- wxeCallbackData * Evt_cb = new wxeCallbackData(Ecmd.caller,getRef(This, memenv),
- class_name,*fun_cb,
- *skip, userData, Listener);
+ wxeEvtListener * Evt_cb = new wxeEvtListener(Ecmd.caller,getRef(This, memenv),
+ class_name,*fun_cb,
+ *skip, userData, Ecmd.port);
This->Connect((int) *winid,(int) *lastId,eventType,
(wxObjectEventFunction)(wxEventFunction) &wxeEvtListener::forward,
- Evt_cb, Listener);
+ Evt_cb, Evt_cb);
rt.addAtom("ok");
+ rt.addRef(getRef((void *)Evt_cb,memenv), "wxeEvtListener");
+ rt.addTupleCount(2);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
@@ -112,7 +102,7 @@ case 100: { // wxEvtHandler::Connect
}
break;
}
-case 101: { // wxEvtHandler::Disconnect
+case 101: { // wxEvtHandler::Disconnect
wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
@@ -122,14 +112,14 @@ case 101: { // wxEvtHandler::Disconnect
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
if(eventType > 0) {
bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
- (wxObjectEventFunction)(wxEventFunction)
- &wxeEvtListener::forward,
- NULL, Listener);
+ (wxObjectEventFunction)(wxEventFunction)
+ &wxeEvtListener::forward,
+ NULL, Listener);
rt.addBool(Result);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
- rt.addTupleCount(2);
+ rt.addTupleCount(2);
}
break;
}
diff --git a/lib/wx/c_src/wxe_callback_impl.cpp b/lib/wx/c_src/wxe_callback_impl.cpp
index 8ba2557d82..e06f68dcbf 100644
--- a/lib/wx/c_src/wxe_callback_impl.cpp
+++ b/lib/wx/c_src/wxe_callback_impl.cpp
@@ -30,27 +30,27 @@
* CallbackData *
* ****************************************************************************/
-wxeCallbackData::wxeCallbackData(ErlDrvTermData caller, int req, char *req_type,
- int funcb, int skip_ev, wxeErlTerm * userData,
- wxeEvtListener *handler_cb)
- : wxObject()
+wxeEvtListener::wxeEvtListener(ErlDrvTermData caller, int req, char *req_type,
+ int funcb, int skip_ev, wxeErlTerm * userData,
+ ErlDrvTermData from_port)
+ : wxEvtHandler()
{
+ port=from_port;
listener = caller;
obj = req;
fun_id = funcb;
strcpy(class_name, req_type);
skip = skip_ev;
user_data = userData;
- handler = handler_cb;
}
-wxeCallbackData::~wxeCallbackData() {
- // fprintf(stderr, "CBD Deleteing %p %s\r\n", this, class_name); fflush(stderr);
+wxeEvtListener::~wxeEvtListener() {
+ // fprintf(stderr, "CBD Deleteing %p %s\r\n", this, class_name); fflush(stderr);
if(user_data) {
delete user_data;
}
ptrMap::iterator it;
- it = ((WxeApp *)wxTheApp)->ptr2ref.find(handler);
+ it = ((WxeApp *)wxTheApp)->ptr2ref.find(this);
if(it != ((WxeApp *)wxTheApp)->ptr2ref.end()) {
wxeRefData *refd = it->second;
wxeReturn rt = wxeReturn(WXE_DRV_PORT, refd->memenv->owner, false);
@@ -61,6 +61,17 @@ wxeCallbackData::~wxeCallbackData() {
rt.addTupleCount(4);
rt.send();
}
+ ((WxeApp *)wxTheApp)->clearPtr(this);
+}
+
+void wxeEvtListener::forward(wxEvent& event)
+{
+#ifdef DEBUG
+ if(!sendevent(&event, port))
+ fprintf(stderr, "Couldn't send event!\r\n");
+#else
+sendevent(&event, port);
+#endif
}
/* *****************************************************************/
diff --git a/lib/wx/c_src/wxe_callback_impl.h b/lib/wx/c_src/wxe_callback_impl.h
index c7243e23a4..1c355e4d38 100644
--- a/lib/wx/c_src/wxe_callback_impl.h
+++ b/lib/wx/c_src/wxe_callback_impl.h
@@ -23,6 +23,24 @@
void pre_callback();
void handle_event_callback(ErlDrvPort port, ErlDrvTermData process);
+/* Fun Callback id */
+class wxeEvtListener : public wxEvtHandler
+{
+public:
+ wxeEvtListener(ErlDrvTermData caller, int req, char *req_type,
+ int funcb, int skip_ev, wxeErlTerm * userData,
+ ErlDrvTermData Thisport);
+ ~wxeEvtListener();
+ void forward(wxEvent& event);
+ ErlDrvTermData port;
+ ErlDrvTermData listener;
+ int fun_id;
+ int obj;
+ char class_name[40];
+ int skip;
+ wxeErlTerm * user_data;
+};
+
class wxEPrintout : public wxPrintout
{
public:
diff --git a/lib/wx/c_src/wxe_events.h b/lib/wx/c_src/wxe_events.h
index 22a737d854..93b5551123 100644
--- a/lib/wx/c_src/wxe_events.h
+++ b/lib/wx/c_src/wxe_events.h
@@ -32,38 +32,7 @@ public:
int cID;
};
-/* One EvtListener per listening erlang process */
-/* If callbacks are used the receiver is wxe_master process */
-/* and a wxeEvtListener pre callback is registered */
-class wxeEvtListener : public wxEvtHandler
-{
- public:
- wxeEvtListener(ErlDrvTermData Thisport) : port(Thisport)
- {}
- // {fprintf(stderr, "Creating %x\r\n", (unsigned int) this); fflush(stderr);}
- ~wxeEvtListener() {}
- void forward(wxEvent& event);
- ErlDrvTermData port;
-};
-
void initEventTable();
int wxeEventTypeFromAtom(char *etype_atom);
-/* Fun Callback id */
-class wxeCallbackData : public wxObject
-{
-public:
- wxeCallbackData(ErlDrvTermData caller, int req, char *req_type,
- int funcb, int skip_ev, wxeErlTerm * userData,
- wxeEvtListener *handler_cb);
- ~wxeCallbackData();
- wxeEvtListener * handler;
- ErlDrvTermData listener;
- int fun_id;
- int obj;
- char class_name[40];
- int skip;
- wxeErlTerm * user_data;
-};
-
#endif
diff --git a/lib/wx/src/gen/wxBufferedDC.erl b/lib/wx/src/gen/wxBufferedDC.erl
index c69a426d7f..e2504bbaaa 100644
--- a/lib/wx/src/gen/wxBufferedDC.erl
+++ b/lib/wx/src/gen/wxBufferedDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -54,6 +54,8 @@
setUserScale/3,startDoc/2,startPage/1]).
-export_type([wxBufferedDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxMemoryDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxBufferedPaintDC.erl b/lib/wx/src/gen/wxBufferedPaintDC.erl
index 0e11826da0..c3fa80703c 100644
--- a/lib/wx/src/gen/wxBufferedPaintDC.erl
+++ b/lib/wx/src/gen/wxBufferedPaintDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -56,6 +56,8 @@
startPage/1]).
-export_type([wxBufferedPaintDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxBufferedDC) -> true;
parent_class(wxMemoryDC) -> true;
diff --git a/lib/wx/src/gen/wxClientDC.erl b/lib/wx/src/gen/wxClientDC.erl
index 45909859ce..ae16196774 100644
--- a/lib/wx/src/gen/wxClientDC.erl
+++ b/lib/wx/src/gen/wxClientDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -56,6 +56,8 @@
-export_type([wxClientDC/0]).
-deprecated([new/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxWindowDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxEvtHandler.erl b/lib/wx/src/gen/wxEvtHandler.erl
index 22c203392c..44b7254cfb 100644
--- a/lib/wx/src/gen/wxEvtHandler.erl
+++ b/lib/wx/src/gen/wxEvtHandler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,15 +46,11 @@
-export([connect/2, connect/3, disconnect/1, disconnect/2, disconnect/3]).
%% internal exports
--export([connect_impl/3, disconnect_impl/2, disconnect_impl/3,
- new_evt_listener/0, destroy_evt_listener/1,
- get_callback/1, replace_fun_with_id/2]).
+-export([connect_impl/2, disconnect_impl/2]).
-export_type([wxEvtHandler/0, wx/0, event/0]).
-type wxEvtHandler() :: wx:wx_object().
--record(evh, {et=null,id=?wxID_ANY,lastId=?wxID_ANY,skip=undefined,userdata=[],cb=0}).
-
%% @doc Equivalent to {@link connect/3. connect(This, EventType, [])}
-spec connect(This::wxEvtHandler(), EventType::wxEventType()) -> ok.
connect(This, EventType) ->
@@ -149,55 +145,35 @@ disconnect(This=#wx_ref{type=ThisT,ref=_ThisRef}, EventType, Opts) ->
%% @hidden
-connect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType,
- skip=Skip, userdata=Userdata, cb=FunID})
+connect_impl(#wx_ref{type=ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ skip=Skip, userdata=Userdata, cb=FunID})
when is_integer(FunID)->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
ThisTypeBin = list_to_binary([atom_to_list(ThisT)|[0]]),
UD = if Userdata =:= [] -> 0;
- true ->
+ true ->
wxe_util:send_bin(term_to_binary(Userdata)),
1
end,
- wxe_util:call(100, <<EvtList:32/?UI,ThisRef:32/?UI,
+ wxe_util:call(100, <<ThisRef:32/?UI,
Winid:32/?UI,LastId:32/?UI,
(wxe_util:from_bool(Skip)):32/?UI,
UD:32/?UI,
FunID:32/?UI,
(size(EventTypeBin)):32/?UI,
- (size(ThisTypeBin)):32/?UI,
+ (size(ThisTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary,ThisTypeBin/binary>>).
%% @hidden
-disconnect_impl(Listener, Object) ->
- disconnect_impl(Listener, Object, #evh{}).
-%% @hidden
-disconnect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=_ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType}) ->
+disconnect_impl(#wx_ref{type=_ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ handler=#wx_ref{type=wxeEvtListener,ref=EvtList}}) ->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
- wxe_util:call(101, <<EvtList:32/?UI,
+ wxe_util:call(101, <<EvtList:32/?UI,
ThisRef:32/?UI,Winid:32/?UI,LastId:32/?UI,
(size(EventTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary>>).
-%% @hidden
-new_evt_listener() ->
- wxe_util:call(98, <<>>).
-
-%% @hidden
-destroy_evt_listener(#wx_ref{type=wxeEvtListener,ref=EvtList}) ->
- wxe_util:call(99, <<EvtList:32/?UI>>).
-
-%% @hidden
-get_callback(#evh{cb=Callback}) ->
- Callback.
-
-%% @hidden
-replace_fun_with_id(Evh, Id) ->
- Evh#evh{cb=Id}.
-
diff --git a/lib/wx/src/gen/wxGridCellBoolEditor.erl b/lib/wx/src/gen/wxGridCellBoolEditor.erl
index bf7e21d11d..c4d6d92618 100644
--- a/lib/wx/src/gen/wxGridCellBoolEditor.erl
+++ b/lib/wx/src/gen/wxGridCellBoolEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellBoolEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellChoiceEditor.erl b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
index 08c5f9e147..a49680ffda 100644
--- a/lib/wx/src/gen/wxGridCellChoiceEditor.erl
+++ b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellChoiceEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellFloatEditor.erl b/lib/wx/src/gen/wxGridCellFloatEditor.erl
index 4b6b3b46e1..5cd0bd6cb5 100644
--- a/lib/wx/src/gen/wxGridCellFloatEditor.erl
+++ b/lib/wx/src/gen/wxGridCellFloatEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellFloatEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellNumberEditor.erl b/lib/wx/src/gen/wxGridCellNumberEditor.erl
index 04214be6b8..7cc682a10e 100644
--- a/lib/wx/src/gen/wxGridCellNumberEditor.erl
+++ b/lib/wx/src/gen/wxGridCellNumberEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellNumberEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellTextEditor) -> true;
parent_class(wxGridCellEditor) -> true;
diff --git a/lib/wx/src/gen/wxGridCellTextEditor.erl b/lib/wx/src/gen/wxGridCellTextEditor.erl
index 5755be8638..a024da56c4 100644
--- a/lib/wx/src/gen/wxGridCellTextEditor.erl
+++ b/lib/wx/src/gen/wxGridCellTextEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellTextEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxMemoryDC.erl b/lib/wx/src/gen/wxMemoryDC.erl
index c123f0e35d..8de412bdc7 100644
--- a/lib/wx/src/gen/wxMemoryDC.erl
+++ b/lib/wx/src/gen/wxMemoryDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxMemoryDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxMirrorDC.erl b/lib/wx/src/gen/wxMirrorDC.erl
index cfae34cb36..9a681bff2e 100644
--- a/lib/wx/src/gen/wxMirrorDC.erl
+++ b/lib/wx/src/gen/wxMirrorDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxMirrorDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxPaintDC.erl b/lib/wx/src/gen/wxPaintDC.erl
index 3c9b321496..0ff27a8c7a 100644
--- a/lib/wx/src/gen/wxPaintDC.erl
+++ b/lib/wx/src/gen/wxPaintDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -56,6 +56,8 @@
-export_type([wxPaintDC/0]).
-deprecated([new/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxWindowDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxPostScriptDC.erl b/lib/wx/src/gen/wxPostScriptDC.erl
index e0b22c87eb..e7e498efa1 100644
--- a/lib/wx/src/gen/wxPostScriptDC.erl
+++ b/lib/wx/src/gen/wxPostScriptDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -55,6 +55,8 @@
-export_type([wxPostScriptDC/0]).
-deprecated([getResolution/0,setResolution/1]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxScreenDC.erl b/lib/wx/src/gen/wxScreenDC.erl
index 54bdc2d351..21ca4bacfc 100644
--- a/lib/wx/src/gen/wxScreenDC.erl
+++ b/lib/wx/src/gen/wxScreenDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxScreenDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxWindowDC.erl b/lib/wx/src/gen/wxWindowDC.erl
index f5c482d8ca..6bb303cfe6 100644
--- a/lib/wx/src/gen/wxWindowDC.erl
+++ b/lib/wx/src/gen/wxWindowDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -55,6 +55,8 @@
-export_type([wxWindowDC/0]).
-deprecated([new/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/wxe.hrl b/lib/wx/src/wxe.hrl
index bd34b13385..66ec9ac45e 100644
--- a/lib/wx/src/wxe.hrl
+++ b/lib/wx/src/wxe.hrl
@@ -29,6 +29,11 @@
-record(wx_mem, {bin, size}).
+-record(evh, {et=null,id=-1,lastId=-1,cb=0,
+ skip=undefined,userdata=[], % temp
+ handler=undefined % added after connect
+ }).
+
-define(CLASS(Type,Class), ((Type) =:= Class) orelse (Type):parent_class(Class)).
-define(CLASS_T(Type,Class),
diff --git a/lib/wx/src/wxe_server.erl b/lib/wx/src/wxe_server.erl
index 04e0d0faf4..aed9dca7ce 100644
--- a/lib/wx/src/wxe_server.erl
+++ b/lib/wx/src/wxe_server.erl
@@ -36,8 +36,8 @@
terminate/2, code_change/3]).
-record(state, {port,cb_port,users,cleaners=[],cb,cb_cnt}).
--record(user, {events=[], evt_handler}).
--record(event, {object, callback, cb_handler}).
+-record(user, {events=[]}).
+%%-record(event, {object, callback, cb_handler}).
-define(APPLICATION, wxe).
-define(log(S,A), log(?MODULE,?LINE,S,A)).
@@ -119,7 +119,7 @@ handle_call(stop,{_From,_},State = #state{users=Users0, cleaners=Cs0}) ->
Env = get(?WXE_IDENTIFIER),
Users = gb_trees:to_list(Users0),
Cs = lists:map(fun({_Pid,User}) ->
- spawn_link(fun() -> cleanup(Env,[User], false) end)
+ spawn_link(fun() -> cleanup(Env,[User]) end)
end, Users),
{noreply, State#state{users=gb_trees:empty(), cleaners=Cs ++ Cs0}};
@@ -178,13 +178,13 @@ handle_info({'DOWN',_,process,Pid,_}, State=#state{users=Users0,cleaners=Cs}) ->
Users = gb_trees:delete(Pid,Users0),
Env = wx:get_env(),
case User of
- #user{events=[], evt_handler=undefined} -> %% No need to spawn
+ #user{events=[]} -> %% No need to spawn
case Cs =:= [] andalso gb_trees:is_empty(Users) of
- true -> {stop, normal, State#state{cleaners=Cs}};
- false -> {noreply, State#state{users=Users,cleaners=Cs}}
+ true -> {stop, normal, State#state{users=Users}};
+ false -> {noreply, State#state{users=Users}}
end;
_ ->
- Cleaner = spawn_link(fun() -> cleanup(Env,[User],true) end),
+ Cleaner = spawn_link(fun() -> cleanup(Env,[User]) end),
{noreply, State#state{users=Users,cleaners=[Cleaner|Cs]}}
end
catch _E:_R ->
@@ -222,36 +222,26 @@ code_change(_OldVsn, State, _Extra) ->
log(Mod,Line,Str,Args) ->
error_logger:format("~p:~p: " ++ Str, [Mod,Line|Args]).
-handle_connect(Object, EvData, From, State0 = #state{users=Users}) ->
- User0 = #user{events=Evs0,evt_handler=Handler0} = gb_trees:get(From, Users),
- Callback = wxEvtHandler:get_callback(EvData),
- case Handler0 of
- #wx_ref{} when Callback =:= 0 ->
- CBHandler = Handler0,
- Handler = Handler0;
- undefined when Callback =:= 0 ->
- Handler = new_evt_listener(State0, From),
- CBHandler = Handler;
- _ ->
- CBHandler = new_evt_listener(State0, From),
- Handler = Handler0
- end,
- Evs = [#event{object=Object,callback=Callback, cb_handler=CBHandler}|Evs0],
- User = User0#user{events=Evs, evt_handler=Handler},
- State1 = State0#state{users=gb_trees:update(From, User, Users)},
- if is_function(Callback) orelse is_pid(Callback) ->
- {FunId, State} = attach_fun(Callback,State1),
- Res = wxEvtHandler:connect_impl(CBHandler,Object,
- wxEvtHandler:replace_fun_with_id(EvData,FunId)),
- case Res of
- ok -> {reply,Res,State};
- _Error -> {reply,Res,State0}
- end;
-
- true ->
- Res = {call_impl, connect_cb, CBHandler},
- {reply, Res, State1}
- end.
+handle_connect(Object, #evh{handler=undefined, cb=Callback} = EvData0,
+ From, State0) ->
+ %% Callback let this process listen to the events
+ {FunId, State} = attach_fun(Callback,State0),
+ EvData1 = EvData0#evh{cb=FunId},
+ case wxEvtHandler:connect_impl(Object,EvData1) of
+ {ok, Handler} ->
+ EvData = EvData1#evh{handler=Handler,userdata=undefined},
+ handle_connect(Object, EvData, From, State);
+ Error ->
+ {reply, Error, State0}
+ end;
+handle_connect(Object, EvData=#evh{handler=Handler},
+ From, State0 = #state{users=Users}) ->
+ %% Correct process is already listening just register it
+ put(Handler, From),
+ User0 = #user{events=Listeners0} = gb_trees:get(From, Users),
+ User = User0#user{events=[{Object,EvData}|Listeners0]},
+ State = State0#state{users=gb_trees:update(From, User, Users)},
+ {reply, ok, State}.
invoke_cb({{Ev=#wx{}, Ref=#wx_ref{}}, FunId,_}, _S) ->
%% Event callbacks
@@ -329,20 +319,6 @@ get_wx_object_state(Pid) ->
_ -> ignore
end.
-new_evt_listener(State, Owner) ->
- #wx_env{port=Port} = wx:get_env(),
- _ = erlang:port_control(Port,98,<<>>),
- Listener = get_result(State),
- put(Listener, Owner),
- Listener.
-
-get_result(_State) ->
- receive
- {'_wxe_result_', Res} -> Res;
- {'_wxe_error_', Op, Error} ->
- erlang:error({Error, {wxEvtHandler, {internal_installer, Op}}})
- end.
-
attach_fun(Fun, S = #state{cb=CB,cb_cnt=Next}) ->
case gb_trees:lookup(Fun,CB) of
{value, ID} ->
@@ -367,90 +343,72 @@ delete_fun(FunId, State = #state{cb=CB}) ->
State
end.
-cleanup_evt_listener(U=#user{events=Evs0,evt_handler=Handler}, EvtListener, Object) ->
- {Evs, Del} = lists:foldl(fun(#event{object=Obj,cb_handler=CBH}, {Acc, Delete})
- when CBH =:= EvtListener, Obj =:= Object ->
- {Acc, Delete};
- (Event = #event{cb_handler=CBH}, {Acc, _Delete})
- when CBH =:= EvtListener ->
- {[Event|Acc], false};
- (Event, {Acc, Delete}) ->
- {[Event|Acc], Delete}
- end, {[], true}, Evs0),
- Del andalso wxEvtHandler:destroy_evt_listener(EvtListener),
- case Del andalso Handler =:= EvtListener of
- true ->
- U#user{events=Evs, evt_handler=undefined};
- false ->
- U#user{events=Evs}
- end.
+cleanup_evt_listener(U=#user{events=Evs0}, EvtListener, Object) ->
+ Filter = fun({Obj,#evh{handler=Evl}}) ->
+ not (Object =:= Obj andalso Evl =:= EvtListener)
+ end,
+ U#user{events=lists:filter(Filter, Evs0)}.
-handle_disconnect(Object, Evh, From, State0 = #state{users=Users0}) ->
+handle_disconnect(Object, Evh = #evh{cb=Fun}, From,
+ State0 = #state{users=Users0, cb=Callbacks}) ->
#user{events=Evs0} = gb_trees:get(From, Users0),
- Fun = wxEvtHandler:get_callback(Evh),
- case find_handler(Evs0, Object, Fun) of
- [] -> {reply, false, State0};
+ FunId = gb_trees:lookup(Fun, Callbacks),
+ case find_handler(Evs0, Object, Evh#evh{cb=FunId}) of
+ [] ->
+ {reply, false, State0};
Handlers ->
- case disconnect(Object,Evh, Handlers) of
- #event{} ->
- {reply, true, State0};
- Result ->
- {reply, Result, State0}
+ case disconnect(Object,Handlers) of
+ #evh{} -> {reply, true, State0};
+ Result -> {reply, Result, State0}
end
end.
-disconnect(Object,Evh,[Ev=#event{cb_handler=Handler}|Evs]) ->
- try wxEvtHandler:disconnect_impl(Handler,Object,Evh) of
+disconnect(Object,[Ev|Evs]) ->
+ try wxEvtHandler:disconnect_impl(Object,Ev) of
true -> Ev;
- false -> disconnect(Object, Evh, Evs);
+ false -> disconnect(Object, Evs);
Error -> Error
catch _:_ ->
false
end;
-disconnect(_, _, []) -> false.
-
-find_handler(Evs, Object, Fun) ->
- find_handler(Evs, Object, Fun, []).
-
-find_handler([Ev =#event{object=Object,callback=FunReg}|Evs],Object,Search,Acc) ->
- case FunReg =:= Search of
- true -> find_handler(Evs,Object,Search,[Ev|Acc]);
- false when is_function(FunReg), Search =:= 0 ->
- find_handler(Evs,Object,Search,[Ev|Acc]);
- _ ->
- find_handler(Evs,Object,Search,Acc)
+disconnect(_, []) -> false.
+
+find_handler([{Object,Evh}|Evs], Object, Match) ->
+ case match_handler(Match, Evh) of
+ false -> find_handler(Evs, Object, Match);
+ Res -> [Res|find_handler(Evs,Object,Match)]
end;
-find_handler([_|Evs],Object,Fun,Res) ->
- find_handler(Evs,Object,Fun,Res);
-find_handler([],_Object,_Fun,Res) ->
- Res.
+find_handler([_|Evs], Object, Match) ->
+ find_handler(Evs, Object, Match);
+find_handler([], _, _) -> [].
+
+match_handler(M=#evh{et=MET, cb=MCB},
+ #evh{et=ET, cb=CB, handler=Handler}) ->
+ %% Let wxWidgets handle the id matching
+ Match = match_et(MET, ET)
+ andalso match_cb(MCB, CB),
+ Match andalso M#evh{handler=Handler}.
+match_et(null, _) -> true;
+match_et(Met, Et) -> Met =:= Et.
+
+match_cb(none, _) -> true;
+match_cb({value,MId}, Id) -> MId =:= Id.
%% Cleanup
%% The server handles callbacks from driver so every other wx call must
%% be called from another process, therefore the cleaning must be spawned.
%%
-%% Using Disconnect when we terminate can crash, it is timing releated
-%% but it seems that disconnect on windows that are being deleted are bad.
-%% Since we are terminating the data will be cleaned up anyway.
-cleanup(Env, Data, Disconnect) ->
+cleanup(Env, Data) ->
put(?WXE_IDENTIFIER, Env),
- lists:foreach(fun(User) -> cleanup(User, Disconnect) end, Data),
+ Disconnect = fun({Object, Ev}) ->
+ try wxEvtHandler:disconnect_impl(Object,Ev)
+ catch _:_ -> ok
+ end
+ end,
+
+ lists:foreach(fun(#user{events=Evs}) ->
+ [Disconnect(Ev) || Ev <- Evs]
+ end, Data),
gen_server:cast(Env#wx_env.sv, {cleaned, self()}),
normal.
-
-cleanup(#user{events=Evs, evt_handler=Handler}, Disconnect) ->
- lists:foreach(fun(#event{object=O, callback=CB, cb_handler=CbH}) ->
- Disconnect andalso (catch wxEvtHandler:disconnect_impl(CbH,O)),
- case is_function(CB) of
- true ->
- wxEvtHandler:destroy_evt_listener(CbH);
- false ->
- ignore
- end
- end, Evs),
- case Handler of
- undefined -> ignore;
- _ -> wxEvtHandler:destroy_evt_listener(Handler)
- end,
- ok.
diff --git a/lib/wx/src/wxe_util.erl b/lib/wx/src/wxe_util.erl
index a31c3e30b8..02ac4ddfa6 100644
--- a/lib/wx/src/wxe_util.erl
+++ b/lib/wx/src/wxe_util.erl
@@ -74,7 +74,7 @@ call(Op, Args) ->
true ->
debug_call(Dbg band 15, Op, Args, Port)
end.
-
+
rec(Op) ->
receive
{'_wxe_result_', Res} -> Res;
@@ -108,21 +108,26 @@ send_bin(Bin) when is_binary(Bin) ->
get_cbId(Fun) ->
gen_server:call((wx:get_env())#wx_env.sv,{register_cb, Fun}, infinity).
-connect_cb(Object,EvData) ->
- handle_listener(connect_cb, Object, EvData).
-
-disconnect_cb(Object,EvData) ->
- handle_listener(disconnect_cb, Object, EvData).
-
-handle_listener(Op,Object,EvData) ->
- Listener = gen_server:call((wx:get_env())#wx_env.sv, {Op,Object,EvData}, infinity),
- case Listener of
- {call_impl, connect_cb, EvtList} ->
- wxEvtHandler:connect_impl(EvtList,Object,EvData);
- Res ->
- Res
+connect_cb(Object,EvData0 = #evh{cb=Callback}) ->
+ Server = (wx:get_env())#wx_env.sv,
+ case Callback of
+ 0 -> %% Message api connect from this process
+ case wxEvtHandler:connect_impl(Object,EvData0) of
+ {ok, Listener} ->
+ EvData = EvData0#evh{handler=Listener, userdata=undefined},
+ gen_server:call(Server, {connect_cb,Object,EvData}, infinity);
+ Error ->
+ Error
+ end;
+ _ -> %% callback, fun or pid (pid for wx_object:sync_events masked callbacks)
+ %% let the server do the connect
+ gen_server:call(Server, {connect_cb,Object,EvData0}, infinity)
end.
+disconnect_cb(Object,EvData) ->
+ Server = (wx:get_env())#wx_env.sv,
+ gen_server:call(Server, {disconnect_cb,Object,EvData}, infinity).
+
debug_cast(1, Op, _Args, _Port) ->
check_previous(),
case ets:lookup(wx_debug_info,Op) of
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index 79dbea0575..7bdbd4594c 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -98,8 +98,23 @@ several_apps(Config) ->
Pids = [spawn_link(fun() -> several_apps(Parent, N, Config) end)
|| N <- lists:seq(1,4)],
process_flag(trap_exit,true),
- ?m_multi_receive([{complete,Pid} || Pid <- Pids]),
+ Wait = fun(Pid,Acc) ->
+ receive {complete, Pid} -> Acc
+ after 20000 -> [Pid|Acc]
+ end
+ end,
+ Res = lists:foldl(Wait, [], Pids),
[Pid ! quit || Pid <- Pids],
+
+ Dbg = fun(Pid) ->
+ io:format("Stack ~p~n",[erlang:process_info(Pid, current_stacktrace)]),
+ io:format("Stack ~p~n",[erlang:process_info(Pid)])
+ end,
+ case Res of
+ [] -> ok;
+ Failed ->
+ [Dbg(Pid)|| Pid <- Failed]
+ end,
case wx_test_lib:user_available(Config) of
true ->
receive {'EXIT',_,foo} -> ok end;
@@ -216,7 +231,7 @@ create_menus(Frame) ->
%% Test the wx_misc.erl api functionality.
wx_misc(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
-wx_misc(Config) ->
+wx_misc(_Config) ->
wx:new([{debug, trace}]),
put(wx_test_verbose, true),
?m(ok, wx_misc:bell()),
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index b9c2fafe0e..076f16ba16 100644
--- a/lib/wx/test/wx_event_SUITE.erl
+++ b/lib/wx/test/wx_event_SUITE.erl
@@ -149,7 +149,7 @@ disconnect(Config) ->
timer:sleep(1000),
wx_test_lib:flush(),
- ?m({'EXIT', {{badarg,_},_}}, wxEvtHandler:disconnect(Panel, non_existing_event_type)),
+ ?m(false, wxEvtHandler:disconnect(Panel, non_existing_event_type)),
?m(true, wxEvtHandler:disconnect(Panel, size)),
?m(ok, wxWindow:setSize(Panel, {200,102})),
timer:sleep(1000),
@@ -499,14 +499,14 @@ callback_clean(Config) ->
end
end),
timer:sleep(500), %% Give it time to remove it
- ?m({[{Pid,_,_}],[_],[_]}, white_box_check_event_handlers()),
+ ?m({[{Pid,_}],[_],[_]}, white_box_check_event_handlers()),
Pid ! remove,
timer:sleep(500), %% Give it time to remove it
?m({[],[],[]}, white_box_check_event_handlers()),
SetupEventHandlers(),
- ?m({[{_,_,_}],[_],[_]}, white_box_check_event_handlers()),
+ ?m({[{_,_}],[_],[_]}, white_box_check_event_handlers()),
wxDialog:show(Dlg),
wx_test_lib:wx_close(Dlg, Config),
@@ -526,9 +526,9 @@ white_box_check_event_handlers() ->
{status, _, _, [Env, _, _, _, Data]} = sys:get_status(Server),
[_H, _data, {data, [{_, Record}]}] = Data,
{state, _Port1, _Port2, Users, [], CBs, _Next} = Record,
- {[{Pid, Evs, Listener} ||
- {Pid, {user, Evs, Listener}} <- gb_trees:to_list(Users),
- (Evs =/= [] orelse Listener =/= undefined)], %% Ignore empty
+ {[{Pid, Evs} ||
+ {Pid, {user, Evs}} <- gb_trees:to_list(Users),
+ Evs =/= []], %% Ignore empty
gb_trees:to_list(CBs),
[Funs || Funs = {Id, {Fun,_}} <- Env, is_integer(Id), is_function(Fun)]
}.