aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/src/asn1_db.erl4
-rw-r--r--lib/asn1/src/asn1ct.erl3
-rw-r--r--lib/asn1/test/asn1_test_lib.erl53
-rw-r--r--lib/common_test/src/ct_gen_conn.erl10
-rw-r--r--lib/common_test/src/ct_logs.erl17
-rw-r--r--lib/common_test/src/ct_telnet.erl88
-rw-r--r--lib/compiler/src/beam_block.erl98
-rw-r--r--lib/compiler/src/beam_jump.erl22
-rw-r--r--lib/compiler/test/beam_block_SUITE.erl27
-rw-r--r--lib/compiler/test/beam_jump_SUITE.erl19
-rw-r--r--lib/crypto/c_src/crypto.c2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/dialyzer/src/dialyzer.erl2
-rw-r--r--lib/diameter/src/Makefile2
-rw-r--r--lib/gs/doc/src/Makefile2
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/inets/doc/src/httpd.xml2
-rw-r--r--lib/inets/doc/src/mod_esi.xml2
-rw-r--r--lib/inets/doc/src/notes.xml17
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl12
-rw-r--r--lib/inets/src/http_server/httpd_example.erl11
-rw-r--r--lib/inets/src/http_server/mod_esi.erl66
-rw-r--r--lib/inets/src/inets_app/inets.appup.src6
-rw-r--r--lib/inets/test/httpd_SUITE.erl60
-rw-r--r--lib/inets/test/inets_SUITE.erl14
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl38
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/Makefile2
-rw-r--r--lib/kernel/doc/src/inet.xml9
-rw-r--r--lib/kernel/doc/src/kernel_app.xml2
-rw-r--r--lib/kernel/doc/src/net_kernel.xml36
-rw-r--r--lib/kernel/doc/src/notes.xml35
-rw-r--r--lib/kernel/include/dist_util.hrl8
-rw-r--r--lib/kernel/src/code_server.erl26
-rw-r--r--lib/kernel/src/dist_util.erl71
-rw-r--r--lib/kernel/src/erl_epmd.erl6
-rw-r--r--lib/kernel/src/inet.erl1
-rw-r--r--lib/kernel/src/inet6_tcp_dist.erl7
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl29
-rw-r--r--lib/kernel/src/net_kernel.erl128
-rw-r--r--lib/kernel/src/os.erl64
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl163
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl148
-rw-r--r--lib/kernel/test/os_SUITE.erl40
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/odbc/configure.in2
-rw-r--r--lib/ssl/doc/src/notes.xml20
-rw-r--r--lib/ssl/src/ssl.appup.src10
-rw-r--r--lib/ssl/src/ssl_handshake.erl38
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl2
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl9
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/syntax_tools/src/merl_tests.erl15
-rw-r--r--lib/syntax_tools/src/merl_transform.erl13
-rw-r--r--lib/syntax_tools/test/merl_SUITE.erl21
-rw-r--r--lib/tools/src/fprof.erl11
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.erl62
-rw-r--r--lib/wx/api_gen/wx_extra/wxXmlResource.erl6
-rw-r--r--lib/wx/c_src/wxe_main.cpp6
-rw-r--r--lib/wx/include/wx.hrl434
-rw-r--r--lib/wx/src/gen/wxListCtrl.erl62
-rw-r--r--lib/wx/src/gen/wxXmlResource.erl6
-rw-r--r--lib/wx/src/wx_object.erl84
63 files changed, 1533 insertions, 630 deletions
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index 557eca0ffd..869ea310aa 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table,
loop(State);
{save, OutFile, Mod} ->
[{_,Mtab}] = ets:lookup(Table, Mod),
- ok = ets:tab2file(Mtab, OutFile),
+ TempFile = OutFile ++ ".#temp",
+ ok = ets:tab2file(Mtab, TempFile),
+ ok = file:rename(TempFile, OutFile),
loop(State);
{From, {new, Mod, Erule}} ->
[] = ets:lookup(Table, Mod), %Assertion.
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index dd269f095d..8783b5418d 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -221,9 +221,8 @@ check_pass(#st{code=M,file=File,includes=Includes,
{error,St#st{error=Reason}}
end.
-save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) ->
+save_pass(#st{code=M,erule=Erule}=St) ->
ok = asn1ct_check:storeindb(#state{erule=Erule}, M),
- asn1_db:dbsave(DbFile,M#module.name),
{ok,St}.
parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index 1b4c3b3c77..dc614db4f2 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -34,12 +34,16 @@ run_dialyzer() ->
compile(File, Config, Options) -> compile_all([File], Config, Options).
-compile_all(Files, Config, Options) ->
+compile_all(Files, Config, Options0) ->
DataDir = proplists:get_value(data_dir, Config),
CaseDir = proplists:get_value(case_dir, Config),
- [compile_file(filename:join(DataDir, F), [{outdir, CaseDir},
- debug_info|Options])
- || F <- Files],
+ Options = [{outdir,CaseDir},debug_info|Options0],
+
+ Comp = fun(F) ->
+ compile_file(filename:join(DataDir, F), Options)
+ end,
+ p_run(Comp, Files),
+
dialyze(Files, Options),
ok.
@@ -94,9 +98,9 @@ compile_file(File, Options) ->
try
ok = asn1ct:compile(File, [warnings_as_errors|Options])
catch
- Class:Reason ->
- ct:print("Failed to compile ~s\n", [File]),
- erlang:error({compile_failed, {File, Options}, {Class, Reason}})
+ _:Reason ->
+ ct:print("Failed to compile ~s\n~p", [File,Reason]),
+ error
end.
compile_erlang(Mod, Config, Options) ->
@@ -219,3 +223,38 @@ ber_get_len(<<0:1,L:7,T/binary>>) ->
ber_get_len(<<1:1,Octets:7,T0/binary>>) ->
<<L:Octets/unit:8,T/binary>> = T0,
{L,T}.
+
+%% p_run(fun(Data) -> ok|error, List) -> ok
+%% Will fail the test case if there were any errors.
+
+p_run(Test, List) ->
+ S = erlang:system_info(schedulers),
+ N = case test_server:is_cover() of
+ false ->
+ S + 1;
+ true ->
+ %% Cover is running. Using too many processes
+ %% could slow us down.
+ min(S, 4)
+ end,
+ %%io:format("p_run: ~p parallel processes\n", [N]),
+ p_run_loop(Test, List, N, [], 0).
+
+p_run_loop(_, [], _, [], Errors) ->
+ case Errors of
+ 0 -> ok;
+ N -> ct:fail({N,errors})
+ end;
+p_run_loop(Test, [H|T], N, Refs, Errors) when length(Refs) < N ->
+ {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end),
+ p_run_loop(Test, T, N, [Ref|Refs], Errors);
+p_run_loop(Test, List, N, Refs0, Errors0) ->
+ receive
+ {'DOWN',Ref,process,_,Res} ->
+ Errors = case Res of
+ ok -> Errors0;
+ error -> Errors0+1
+ end,
+ Refs = Refs0 -- [Ref],
+ p_run_loop(Test, List, N, Refs, Errors)
+ end.
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 23ba1ab981..8b59d3ab23 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -27,7 +27,7 @@
-export([start/4, stop/1, get_conn_pid/1, check_opts/1]).
-export([call/2, call/3, return/2, do_within_time/2]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
%%----------------------------------------------------------------------
%% Exported types
@@ -175,6 +175,14 @@ cont_log(Format,Args) ->
log(cont_log,[Format,Args]).
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Log activities on the current connection (tool-internal use only).
+%%% @see ct_logs:cont_log/2
+cont_log_no_timestamp(Format,Args) ->
+ log(cont_log_no_timestamp,[Format,Args]).
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 53245c596a..9282a9f81d 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -32,7 +32,7 @@
-export([init/2, close/2, init_tc/1, end_tc/1]).
-export([register_groupleader/2, unregister_groupleader/1]).
-export([get_log_dir/0, get_log_dir/1]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
-export([set_stylesheet/2, clear_stylesheet/1]).
-export([add_external_logs/1, add_link/3]).
-export([make_last_run_index/0]).
@@ -373,6 +373,20 @@ cont_log(Format,Args) ->
ok.
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Adds information about an activity (tool-internal use only).
+%%%
+%%% @see start_log/1
+%%% @see end_log/0
+cont_log_no_timestamp([],[]) ->
+ ok;
+cont_log_no_timestamp(Format,Args) ->
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{Format,Args}],true}),
+ ok.
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Ends the logging of an activity (tool-internal use only).
@@ -595,7 +609,6 @@ div_header(Class,Printer) ->
div_footer() ->
"</pre></div>\n<pre>".
-
maybe_log_timestamp() ->
{MS,S,US} = ?now,
case get(log_timestamp) of
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 8fb411ec4f..34d27ed5f4 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -954,7 +954,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
true ->
ok;
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end;
ForcePrint == true ->
@@ -965,7 +965,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
%% called
ct_gen_conn:log(heading(Action,Name1),String,Args);
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end
end
end.
@@ -1224,7 +1224,6 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
EOMod = if TotalTO /= infinity -> EO#eo{total_timeout=trunc(TotalTO)};
true -> EO
end,
-
ExpectFun = case EOMod#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EOMod)
@@ -1247,38 +1246,34 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
true ->
IdleTO
end,
+ {PatOrPats1,Acc1,Rest1} = case NotFinished of
+ {nomatch,Rest0} ->
+ %% one expect
+ {Pattern,[],Rest0};
+ {continue,Pats0,Acc0,Rest0} ->
+ %% sequence
+ {Pats0,Acc0,Rest0}
+ end,
case timer:tc(ct_gen_conn, do_within_time, [Fun,BreakAfter]) of
- {_,{error,Reason}} ->
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
%% is idle for EO#eo.idle_timeout milliseconds.
+ if Rest1 /= [] ->
+ log(name_or_pid(Name,Pid)," ~ts",[Rest1]);
+ true ->
+ ok
+ end,
{error,Reason};
{_,{ok,Data1}} when TotalTO == infinity ->
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EOMod);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EOMod)
- end;
+ teln_expect1(Name,Pid,Rest1++Data1,PatOrPats1,Acc1,EOMod);
{Elapsed,{ok,Data1}} ->
TVal = TotalTO - (Elapsed/1000),
if TVal =< 0 ->
{error,timeout};
true ->
EO1 = EO#eo{total_timeout = TVal},
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EO1);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EO1)
- end
+ teln_expect1(Name,Pid,Rest1++Data1,
+ PatOrPats1,Acc1,EO1)
end
end
end.
@@ -1416,14 +1411,14 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
case one_line(Data,[]) of
{noline,Rest} when FoundPrompt=/=false ->
%% This is the line including the prompt
- case match_line(Name,Pid,Rest,Patterns,FoundPrompt,EO) of
+ case match_line(Name,Pid,Rest,Patterns,FoundPrompt,false,EO) of
nomatch ->
{nomatch,prompt};
{Tag,Match} ->
{Tag,Match,[]}
end;
{noline,Rest} when EO#eo.prompt_check==false ->
- case match_line(Name,Pid,Rest,Patterns,false,EO) of
+ case match_line(Name,Pid,Rest,Patterns,false,false,EO) of
nomatch ->
{nomatch,Rest};
{Tag,Match} ->
@@ -1432,7 +1427,7 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
{noline,Rest} ->
{nomatch,Rest};
{Line,Rest} ->
- case match_line(Name,Pid,Line,Patterns,false,EO) of
+ case match_line(Name,Pid,Line,Patterns,false,true,EO) of
nomatch ->
match_lines(Name,Pid,Rest,Patterns,EO);
{Tag,Match} ->
@@ -1440,45 +1435,50 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
end
end.
-
%% For one line, match each pattern
-match_line(Name,Pid,Line,Patterns,FoundPrompt,EO) ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,match).
+match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO,match).
-match_line(Name,Pid,Line,[prompt|Patterns],false,EO,RetTag) ->
- match_line(Name,Pid,Line,Patterns,false,EO,RetTag);
-match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) ->
+match_line(Name,Pid,Line,[prompt|Patterns],false,Term,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,false,Term,EO,RetTag);
+match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_Term,_EO,RetTag) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag)
- when PromptType==FoundPrompt ->
+match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_Term,
+ _EO,RetTag) when PromptType==FoundPrompt ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag)
+match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
+ EO,RetTag)
when PromptType=/=FoundPrompt ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
-match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
+match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,{Tag,Match}}
end;
-match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) ->
+match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,Match}
end;
-match_line(Name,Pid,Line,[],FoundPrompt,EO,match) ->
- match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,EO,halt);
-match_line(Name,Pid,Line,[],_FoundPrompt,_EO,halt) ->
+match_line(Name,Pid,Line,[],FoundPrompt,Term,EO,match) ->
+ match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,Term,EO,halt);
+%% print any terminated line that can not be matched
+match_line(Name,Pid,Line,[],_FoundPrompt,true,_EO,halt) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
+ nomatch;
+%% if there's no line termination, Line is saved as Rest (above) and will
+%% be printed later
+match_line(_Name,_Pid,_Line,[],_FoundPrompt,false,_EO,halt) ->
nomatch.
one_line([$\n|Rest],Line) ->
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 85d332c56e..ec41925beb 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -159,14 +159,43 @@ find_fixpoint(OptFun, Is0) ->
end.
%% move_allocates(Is0) -> Is
-%% Move allocate instructions upwards in the instruction stream, in the
-%% hope of getting more possibilities for optimizing away moves later.
+%% Move allocate instructions upwards in the instruction stream
+%% (within the same block), in the hope of getting more possibilities
+%% for optimizing away moves later.
%%
-%% NOTE: Moving allocation instructions is only safe because it is done
-%% immediately after code generation so that we KNOW that if {x,X} is
-%% initialized, all x registers with lower numbers are also initialized.
-%% That assumption may not be true after other optimizations, such as
-%% the beam_utils:live_opt/1 optimization.
+%% For example, we can transform the following instructions:
+%%
+%% get_tuple_element x(1) Element => x(2)
+%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 2 %% x(0) and x(1) are live
+%% get_tuple_element x(1) Element => x(2)
+%%
+%% NOTE: Since the beam_reorder pass has been run, it is no longer
+%% safe to assume that if x(N) is initialized, then all lower-numbered
+%% x registers are also initialized.
+%%
+%% For example, in general it is not safe to transform the following
+%% instructions:
+%%
+%% get_tuple_element x(0) Element => x(1)
+%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 3
+%% get_tuple_element x(0) Element => x(1)
+%%
+%% The transformation is safe if and only if x(1) has been
+%% initialized previously. Unfortunately, beam_reorder may have moved
+%% a get_tuple_element instruction so that x(1) is not always
+%% initialized when this code is reached. To find whether or not x(1)
+%% is initialized, we would need to analyze all code preceding these
+%% two instructions (across branches). Since we currently don't have
+%% any practical mechanism for doing that, we will have to
+%% conservatively assume that the transformation is unsafe.
move_allocates([{block,Bl0}|Is]) ->
Bl = move_allocates_1(reverse(Bl0), []),
@@ -175,27 +204,19 @@ move_allocates([I|Is]) ->
[I|move_allocates(Is)];
move_allocates([]) -> [].
-move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) ->
- {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0),
- move_allocates_1(Is, Acc);
+move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
+ case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of
+ {false,_} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,not_possible} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,Live} when is_integer(Live) ->
+ A = {set,[],[],{alloc,Live,Info}},
+ move_allocates_1(Is, [A,I|Acc])
+ end;
move_allocates_1([I|Is], Acc) ->
move_allocates_1(Is, [I|Acc]);
-move_allocates_1([], Is) -> Is.
-
-move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) ->
- Live = Live0, % Assertion.
- Alloc = {alloc,Live,combine_alloc(Info0, Info)},
- move_allocates_2(Alloc, Is, Acc);
-move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) ->
- case alloc_may_pass(I) of
- false ->
- {Is0,[{set,[],[],Alloc0}|Acc]};
- true ->
- Alloc = {alloc,alloc_live_regs(I, Live),Info},
- move_allocates_2(Alloc, Is, [I|Acc])
- end;
-move_allocates_2(Alloc, [], Acc) ->
- {[],[{set,[],[],Alloc}|Acc]}.
+move_allocates_1([], Acc) -> Acc.
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
@@ -204,9 +225,6 @@ alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
-combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
- {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
-
%% opt([Instruction]) -> [Instruction]
%% Optimize the instruction stream inside a basic block.
@@ -393,10 +411,19 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
%% opt_alloc(Instructions) -> Instructions'
%% Optimises all allocate instructions.
+opt_alloc([{set,[],[],{alloc,Live0,Info0}},
+ {set,[],[],{alloc,Live,Info}}|Is]) ->
+ Live = Live0, %Assertion.
+ Alloc = combine_alloc(Info0, Info),
+ I = {set,[],[],{alloc,Live,Alloc}},
+ opt_alloc([I|Is]);
opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) ->
[{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is];
opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
opt_alloc([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
%% Generates the optimal sequence of instructions for
@@ -445,13 +472,14 @@ count_ones(Bits, Acc) ->
alloc_live_regs({set,Ds,Ss,_}, Regs0) ->
Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
- live_regs(Rset).
+ live_regs(0, Rset).
-live_regs(Regs) ->
- live_regs_1(0, Regs).
-
-live_regs_1(N, 0) -> N;
-live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+live_regs(N, 0) ->
+ N;
+live_regs(N, Regs) when Regs band 1 =:= 1 ->
+ live_regs(N+1, Regs bsr 1);
+live_regs(_, _) ->
+ not_possible.
x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 09cd3aa2d4..48b5a32814 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
end;
share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
reverse(Is, [I|Acc]);
+share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
+share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([I|Is], Dict, Seq, Acc) ->
case is_unreachable_after(I) of
false ->
@@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) ->
end.
clean_non_sharable(Dict) ->
- %% We are passing in or out of a 'try' block. Remove
- %% sequences that should not shared over the boundaries
- %% of a 'try' block. Since the end of the sequence must match,
- %% the only possible match between a sequence outside and
- %% a sequence inside the 'try' block is a sequence that ends
- %% with an instruction that causes an exception. Any sequence
- %% that causes an exception must contain a line/1 instruction.
+ %% We are passing in or out of a 'catch' or 'try' block. Remove
+ %% sequences that should not be shared over the boundaries of the
+ %% block. Since the end of the sequence must match, the only
+ %% possible match between a sequence outside and a sequence inside
+ %% the 'catch'/'try' block is a sequence that ends with an
+ %% instruction that causes an exception. Any sequence that causes
+ %% an exception must contain a line/1 instruction.
maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
sharable_with_try([{line,_}|_]) ->
%% This sequence may cause an exception and may potentially
- %% match a sequence on the other side of the 'try' block
+ %% match a sequence on the other side of the 'catch'/'try' block
%% boundary.
false;
sharable_with_try([_|Is]) ->
diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl
index 4bcb252833..9fcb6e497d 100644
--- a/lib/compiler/test/beam_block_SUITE.erl
+++ b/lib/compiler/test/beam_block_SUITE.erl
@@ -21,7 +21,8 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1]).
+ get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1,
+ erl_202/1]).
%% The only test for the following functions is that
%% the code compiles and is accepted by beam_validator.
@@ -37,7 +38,8 @@ groups() ->
[{p,[parallel],
[get_map_elements,
otp_7345,
- move_opt_across_gc_bif
+ move_opt_across_gc_bif,
+ erl_202
]}].
init_per_suite(Config) ->
@@ -135,6 +137,27 @@ positive(speaking) ->
paris([], P) -> P + 1.
+
+%% See https://bugs.erlang.org/browse/ERL-202.
+%% Test that move_allocates/1 in beam_block doesn't move allocate
+%% when it would not be safe.
+
+-record(erl_202_r1, {y}).
+-record(erl_202_r2, {x}).
+
+erl_202(_Config) ->
+ Ref = make_ref(),
+ Ref = erl_202({{1,2},Ref}, 42),
+
+ {Ref} = erl_202({7,8}, #erl_202_r1{y=#erl_202_r2{x=Ref}}),
+
+ ok.
+
+erl_202({{_, _},X}, _) ->
+ X;
+erl_202({_, _}, #erl_202_r1{y=R2}) ->
+ {R2#erl_202_r2.x}.
+
%%%
%%% The only test of the following code is that it compiles.
%%%
diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl
index 0b13adaff2..088f63606c 100644
--- a/lib/compiler/test/beam_jump_SUITE.erl
+++ b/lib/compiler/test/beam_jump_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- undefined_label/1]).
+ undefined_label/1,ambiguous_catch_try_state/1]).
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -32,7 +32,8 @@ all() ->
groups() ->
[{p,[parallel],
- [undefined_label
+ [undefined_label,
+ ambiguous_catch_try_state
]}].
init_per_suite(Config) ->
@@ -57,3 +58,17 @@ flights(0, [], []) when [], 0; 0.0, [], false ->
clark;
flights(_, Reproduction, introduction) when false, Reproduction ->
responsible.
+
+%% [ERL-209] beam_jump would share 'catch' blocks, causing an
+%% ambiguous_catch_try_state error in beam_validator.
+
+ambiguous_catch_try_state(_Config) ->
+ {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} =
+ checks(42),
+ ok.
+
+river() -> song.
+
+checks(Wanted) ->
+ %% Must be one line to cause the unsafe optimization.
+ {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}.
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 7183c395ae..d0044fe723 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -749,7 +749,7 @@ static ERL_NIF_TERM algo_hash[8]; /* increase when extending the list */
static int algo_pubkey_cnt;
static ERL_NIF_TERM algo_pubkey[7]; /* increase when extending the list */
static int algo_cipher_cnt;
-static ERL_NIF_TERM algo_cipher[20]; /* increase when extending the list */
+static ERL_NIF_TERM algo_cipher[21]; /* increase when extending the list */
static void init_algorithms_types(ErlNifEnv* env)
{
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 6c9617ca69..0f724b6f17 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -114,7 +114,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index bcac8afe64..7f86520c06 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -282,7 +282,7 @@ cl_check_log(none) ->
cl_check_log(Output) ->
io:format(" Check output file `~s' for details\n", [Output]).
--spec format_warning(raw_warning()) -> string().
+-spec format_warning(raw_warning() | dial_warning()) -> string().
format_warning(W) ->
format_warning(W, basename).
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 7f61620fc1..6bf748a727 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -123,7 +123,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
$(dia_verbose) \
- ../bin/diameterc -o gen -i $(EBIN) $<
+ escript ../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile
index b270bc84fe..a24ec77e31 100644
--- a/lib/gs/doc/src/Makefile
+++ b/lib/gs/doc/src/Makefile
@@ -148,7 +148,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index c9691df7af..19f12ac6b9 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -225,7 +225,7 @@ release_docs_spec: docs
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 62b92b8356..b05ae72983 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -455,7 +455,7 @@ text/plain asc txt</pre>
directory. Several files can be given, in which case the server
returns the first it finds, for example:</p>
- <code>{directory_index, ["index.hml", "welcome.html"]}</code>
+ <code>{directory_index, ["index.html", "welcome.html"]}</code>
<p>Access to http://your.server.org/docs/ would return
http://your.server.org/docs/index.html or
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index 8279fdc824..006fca1bdf 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -61,7 +61,7 @@
<tag><c>{server_port, integer()}</c></tag>
<item><p>Servers port number.</p></item>
- <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag>
+ <tag><c>{request_method, "GET | "PUT" | "DELETE" | "POST" | "PATCH"}</c></tag>
<item><p>HTTP request method.</p></item>
<tag><c>{remote_adress, inet:ip_address()} </c></tag>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 2f071f049f..caa5a083a3 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,22 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.3.1</title>
+ <section><title>Inets 6.3.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ PUT and DELETE support has been added to mod_esi</p>
+ <p>
+ Own Id: OTP-13688 Aux Id: seq13149 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index e5182ca23c..9e54f2b2c5 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -395,7 +395,8 @@ validate_properties(Properties) ->
%% That is, if property A depends on property B.
%% The only sunch preperty at this time is bind_address that depends
%% on ipfamily.
-validate_properties2(Properties) ->
+validate_properties2(Properties0) ->
+ Properties = fix_ipfamily(Properties0),
case proplists:get_value(bind_address, Properties) of
undefined ->
case proplists:get_value(sock_type, Properties, ip_comm) of
@@ -422,6 +423,15 @@ validate_properties2(Properties) ->
end
end.
+fix_ipfamily(Properties) ->
+ case proplists:get_value(ipfamily, Properties) of
+ undefined ->
+ Properties;
+ IpFamily ->
+ NewProps = proplists:delete(ipfamily, Properties),
+ [{ipfamily, validate_ipfamily(IpFamily)} | NewProps]
+ end.
+
add_inet_defaults(Properties) ->
case proplists:get_value(ipfamily, Properties) of
undefined ->
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 424d269859..c893b10dca 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -20,7 +20,7 @@
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
+-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
-export([newformat/3]).
%% These are used by the inets test-suite
@@ -59,6 +59,11 @@ get(_Env,[]) ->
get(Env,Input) ->
default(Env,Input).
+put(Env,{Input,_Body}) ->
+ default(Env,Input);
+put(Env,Input) ->
+ default(Env,Input).
+
get_bin(_Env,_Input) ->
[list_to_binary(header()),
list_to_binary(top("GET Example")),
@@ -94,7 +99,7 @@ default(Env,Input) ->
io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
footer()].
-peer(Env, Input) ->
+peer(Env, _Input) ->
Header =
case proplists:get_value(peer_cert, Env) of
undefined ->
@@ -161,7 +166,7 @@ sleep(T) -> receive after T -> ok end.
%% ------------------------------------------------------
-chunk_timeout(SessionID, _, StrInt) ->
+chunk_timeout(SessionID, _, _StrInt) ->
mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"),
mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
timer:sleep(20000),
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 2800250727..b21af1418c 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -241,7 +241,7 @@ alias_match_str(Alias, eval_script_alias) ->
%%------------------------ Erl mechanism --------------------------------
erl(#mod{method = Method} = ModData, ESIBody, Modules)
- when (Method =:= "GET") orelse (Method =:= "HEAD") ->
+ when (Method =:= "GET") orelse (Method =:= "HEAD") orelse (Method =:= "DELETE") ->
?hdrt("erl", [{method, Method}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok, [ModuleName, FuncAndInput]} ->
@@ -264,35 +264,32 @@ erl(#mod{method = Method} = ModData, ESIBody, Modules)
{proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
end;
-erl(#mod{request_uri = ReqUri,
- method = "PUT",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, put}]),
- {proceed, [{status,{501,{"PUT", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PUT")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "DELETE",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, delete}]),
- {proceed,[{status,{501,{"DELETE", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method DELETE")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "PATCH",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, patch}]),
- {proceed, [{status,{501,{"PATCH", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PATCH")}}|
- Data]};
+erl(#mod{method = "PUT", entity_body = Body} = ModData,
+ ESIBody, Modules) ->
+ case httpd_util:split(ESIBody,":|%3A|/",2) of
+ {ok, [ModuleName, FuncAndInput]} ->
+ case httpd_util:split(FuncAndInput,"[\?/]",2) of
+ {ok, [FunctionName, Input]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {Input,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, Input)]);
+ {ok, [FunctionName]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {undefined,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, "")]);
+ {ok, BadRequest} ->
+ {proceed,[{status,{400,none, BadRequest}} |
+ ModData#mod.data]}
+ end;
+ {ok, BadRequest} ->
+ {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
+ end;
-erl(#mod{method = "POST",
- entity_body = Body} = ModData, ESIBody, Modules) ->
+erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) ->
?hdrt("erl", [{method, post}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok,[ModuleName, Function]} ->
@@ -301,7 +298,16 @@ erl(#mod{method = "POST",
Function, Body, [{entity_body, Body}]);
{ok, BadRequest} ->
{proceed,[{status, {400, none, BadRequest}} | ModData#mod.data]}
- end.
+ end;
+
+erl(#mod{request_uri = ReqUri,
+ method = "PATCH",
+ http_version = Version,
+ data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, patch}]),
+ {proceed, [{status,{501,{"PATCH", ReqUri, Version},
+ ?NICE("Erl mechanism doesn't support method PATCH")}}|
+ Data]}.
generate_webpage(ModData, ESIBody, [all], Module, FunctionName,
Input, ScriptElements) ->
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 3a31daeb20..d4d21f6774 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,10 +18,16 @@
%% %CopyrightEnd%
{"%VSN%",
[
+ {<<"6.3.1">>,
+ [{load_module, mod_esi, soft_purge, soft_purge, []}
+ ]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
+ {<<"6.3.1">>,
+ [{load_module, mod_esi, soft_purge, soft_purge, []}
+ ]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 3194b5ad3d..28e77151f2 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -119,8 +119,10 @@ groups() ->
]},
{htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
{security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
- {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
- trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()},
+ {http_1_1, [],
+ [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since, mod_esi_chunk_timeout,
+ esi_put] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()}
].
@@ -283,20 +285,50 @@ init_per_testcase(Case, Config) when Case == host; Case == trace ->
http_1_1 ->
httpd_1_1
end,
- [{version_cb, Cb} | proplists:delete(version_cb, Config)];
+ dbg(
+ Case,
+ [{version_cb, Cb} | proplists:delete(version_cb, Config)],
+ init);
init_per_testcase(range, Config) ->
ct:timetrap({seconds, 20}),
DocRoot = proplists:get_value(doc_root, Config),
create_range_data(DocRoot),
- Config;
+ dbg(range, Config, init);
-init_per_testcase(_, Config) ->
+init_per_testcase(Case, Config) ->
ct:timetrap({seconds, 20}),
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
+ dbg(Case, Config, init).
+
+end_per_testcase(Case, Config) ->
+ dbg(Case, Config, 'end').
+
+
+dbg(Case, Config, Status) ->
+ Cases = [esi_put],
+ case lists:member(Case, Cases) of
+ true ->
+ case Status of
+ init ->
+ dbg:tracer(),
+ dbg:p(all, c),
+ dbg:tpl(httpd_example, cx),
+ dbg:tpl(mod_esi, generate_webpage, cx),
+ io:format("dbg: started~n"),
+ Config;
+ 'end' ->
+ io:format("dbg: stopped~n"),
+ dbg:stop_clear(),
+ ok
+ end;
+ false ->
+ case Status of
+ init ->
+ Config;
+ 'end' ->
+ ok
+ end
+ end.
%%-------------------------------------------------------------------------
%% Test cases starts here.
@@ -765,6 +797,14 @@ esi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/erl/httpd_example:peer ",
Config, [{statuscode, 200},
{header, "peer-cert-exist", peer(Config)}]).
+
+%%-------------------------------------------------------------------------
+esi_put() ->
+ [{doc, "Test mod_esi PUT"}].
+
+esi_put(Config) when is_list(Config) ->
+ ok = http_status("PUT /cgi-bin/erl/httpd_example/put/123342234123 ",
+ Config, [{statuscode, 200}]).
%%-------------------------------------------------------------------------
mod_esi_chunk_timeout(Config) when is_list(Config) ->
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 5eaf3a28a0..38b8229389 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -212,11 +212,19 @@ start_httpd(Config) when is_list(Config) ->
Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
-
inets:stop(httpd, Pid0),
ct:sleep(500),
+ Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid0, Pids1),
+ {ok, Pid0b} =
+ inets:start(httpd, [{port, 0}, {ipfamily, inet6fb4} | HttpdConf]),
+ Pids0b = [ServicePid || {_, ServicePid} <- inets:services()],
+ true = lists:member(Pid0b, Pids0b),
+ [_|_] = inets:services_info(),
+ inets:stop(httpd, Pid0b),
+ ct:sleep(500),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
- false = lists:member(Pid0, Pids1),
+ false = lists:member(Pid0b, Pids1),
{ok, Pid1} =
inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf],
stand_alone),
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 5b8b1463c8..1e664337e6 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -33,7 +33,7 @@ suite() ->
all() ->
[default_tree, ftpc_worker, tftpd_worker,
- httpd_subtree, httpd_subtree_profile,
+ httpd_config, httpd_subtree, httpd_subtree_profile,
httpc_subtree].
groups() ->
@@ -52,9 +52,32 @@ end_per_suite(_) ->
inets:stop(),
ok.
-init_per_testcase(httpd_subtree, Config) ->
+init_per_testcase(httpd_config = TC, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, TC),
+ ok = file:make_dir(Dir),
+
+ FallbackConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, Dir},
+ {document_root, Dir},
+ {bind_address, any},
+ {ipfamily, inet6fb4}],
+ try
+ inets:stop(),
+ inets:start(),
+ inets:start(httpd, FallbackConfig),
+ Config
+ catch
+ _:Reason ->
+ inets:stop(),
+ exit({failed_starting_inets, Reason})
+ end;
+
+init_per_testcase(httpd_subtree = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -75,9 +98,9 @@ init_per_testcase(httpd_subtree, Config) ->
exit({failed_starting_inets, Reason})
end;
-init_per_testcase(httpd_subtree_profile, Config) ->
+init_per_testcase(httpd_subtree_profile = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -193,6 +216,11 @@ tftpd_worker(Config) when is_list(Config) ->
[] = supervisor:which_children(tftp_sup),
ok.
+httpd_config() ->
+ [{doc, "Makes sure the httpd config works for inet6fb4."}].
+httpd_config(Config) when is_list(Config) ->
+ do_httpd_subtree(Config, default).
+
httpd_subtree() ->
[{doc, "Makes sure the httpd sub tree is correct."}].
httpd_subtree(Config) when is_list(Config) ->
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 3408c3b128..747724a86b 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.3.1
+INETS_VSN = 6.3.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index cd1e61a795..508c8e01b5 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -166,7 +166,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- (/bin/cp -rf ../html "$(RELSYSDIR)/doc")
+ ($(CP) -rf ../html "$(RELSYSDIR)/doc")
# $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
# "$(RELSYSDIR)/doc/html"
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index c0dce2f50c..8cc21bf3e2 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -151,6 +151,12 @@ fe80::204:acff:fe17:bf38
<name name="socket_address"/>
</datatype>
<datatype>
+ <name name="socket_getopt"/>
+ </datatype>
+ <datatype>
+ <name name="socket_setopt"/>
+ </datatype>
+ <datatype>
<name name="returned_non_ip_address"/>
<desc>
<p>
@@ -327,8 +333,6 @@ fe80::204:acff:fe17:bf38
<func>
<name name="getopts" arity="2"/>
<fsummary>Get one or more options for a socket.</fsummary>
- <type name="socket_getopt"/>
- <type name="socket_setopt"/>
<desc>
<p>Gets one or more options for a socket. For a list of available
options, see
@@ -580,7 +584,6 @@ get_tcpi_sacked(Sock) ->
<func>
<name name="setopts" arity="2"/>
<fsummary>Set one or more options for a socket.</fsummary>
- <type name="socket_setopt"/>
<desc>
<p>Sets one or more options for a socket.</p>
<p>The following options are available:</p>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 9e6fb60bb7..dc0291babe 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -215,12 +215,14 @@
<p>Defines the <c>First..Last</c> port range for the listener
socket of a distributed Erlang node.</p>
</item>
+ <marker id="inet_dist_listen_options"></marker>
<tag><c>{inet_dist_listen_options, Opts}</c></tag>
<item>
<p>Defines a list of extra socket options to be used when opening the
listening socket for a distributed Erlang node.
See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p>
</item>
+ <marker id="inet_dist_connect_options"></marker>
<tag><c>{inet_dist_connect_options, Opts}</c></tag>
<item>
<p>Defines a list of extra socket options to be used when connecting to
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index f48a534d4f..3c1b3d5190 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -116,6 +116,21 @@ $ <input>erl -sname foobar</input></pre>
</func>
<func>
+ <name name="getopts" arity="2"/>
+ <fsummary>Get distribution socket options.</fsummary>
+ <desc>
+ <p>Get one or more options for the distribution socket
+ connected to <c><anno>Node</anno></c>.</p>
+ <p>If <c><anno>Node</anno></c> is a connected node
+ the return value is the same as from
+ <seealso marker="inet#getopts/2"><c>inet:getopts(Sock, Options)</c></seealso>
+ where <c>Sock</c> is the distribution socket for <c><anno>Node</anno></c>.</p>
+ <p>Returns <c>ignored</c> if the local node is not alive or
+ <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not connected.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="monitor_nodes" arity="1"/>
<name name="monitor_nodes" arity="2"/>
<fsummary>Subscribe to node status change messages.</fsummary>
@@ -289,6 +304,27 @@ $ <input>erl -sname foobar</input></pre>
</func>
<func>
+ <name name="setopts" arity="2"/>
+ <fsummary>Set distribution socket options.</fsummary>
+ <desc>
+ <p>Set one or more options for distribution sockets.
+ Argument <c><anno>Node</anno></c> can be either one node name
+ or the atom <c>new</c> to affect the distribution sockets of all
+ future connected nodes.</p>
+ <p>The return value is the same as from
+ <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>
+ or <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not
+ a connected node or <c>new</c>.</p>
+ <p>If <c><anno>Node</anno></c> is <c>new</c> the <c><anno>Options</anno></c>
+ will then also be added to kernel configration parameters
+ <seealso marker="kernel:kernel_app#inet_dist_listen_options">inet_dist_listen_options</seealso>
+ and
+ <seealso marker="kernel:kernel_app#inet_dist_connect_options">inet_dist_connect_options</seealso>.</p>
+ <p>Returns <c>ignored</c> if the local node is not alive.</p>
+ </desc>
+ </func>
+
+ <func>
<name>start([Name]) -> {ok, pid()} | {error, Reason}</name>
<name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name>
<name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index f37433110c..3d35f6f57f 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When calling os:cmd from a process that has set trap_exit
+ to true an 'EXIT' message would be left in the message
+ queue. This bug was introduced in kernel vsn 5.0.1.</p>
+ <p>
+ Own Id: OTP-13813</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a os:cmd bug where creating a background job using
+ &amp; would cause os:cmd to hang until the background job
+ terminated or closed its stdout and stderr file
+ descriptors. This bug has existed from kernel 5.0.</p>
+ <p>
+ Own Id: OTP-13741</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl
index 43e50d4325..320e916c04 100644
--- a/lib/kernel/include/dist_util.hrl
+++ b/lib/kernel/include/dist_util.hrl
@@ -63,7 +63,7 @@
f_getll, %% Get low level port or pid.
f_address, %% The address of the "socket",
%% generated from Socket,Node
- %% These two are used in the tick loop,
+ %% These three are used in the tick loop,
%% so they are not fun's to avoid holding old code.
mf_tick, %% Takes the socket as parameters and
%% sends a tick, this is no fun, it
@@ -74,7 +74,11 @@
%% {ok, RecvCnt, SendCnt, SendPend} for
%% a given socket. This is a {M,F},
%% returning {error, Reason on failure}
- request_type = normal
+ request_type = normal,
+
+ %% New in kernel-5.1 (OTP 19.1):
+ mf_setopts, %% netkernel:setopts on active connection
+ mf_getopts %% netkernel:getopts on active connection
}).
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 6174136507..48541ec500 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) ->
-spec call(term()) -> term().
call(Req) ->
+ Ref = erlang:monitor(process, ?MODULE),
?MODULE ! {code_call, self(), Req},
receive
{?MODULE, Reply} ->
- Reply
+ erlang:demonitor(Ref,[flush]),
+ Reply;
+ {'DOWN',Ref,process,_,_} ->
+ exit({'DOWN',code_server,Req})
end.
reply(Pid, Res) ->
@@ -933,14 +937,20 @@ del_ebin(Dir) ->
filename:join(del_ebin_1(filename:split(Dir))).
del_ebin_1([Parent,App,"ebin"]) ->
- Ext = archive_extension(),
- case filename:basename(Parent, Ext) of
- Parent ->
- %% Plain directory.
+ case filename:basename(Parent) of
+ [] ->
+ %% Parent is the root directory
[Parent,App];
- Archive ->
- %% Archive.
- [Archive]
+ _ ->
+ Ext = archive_extension(),
+ case filename:basename(Parent, Ext) of
+ Parent ->
+ %% Plain directory.
+ [Parent,App];
+ Archive ->
+ %% Archive.
+ [Archive]
+ end
end;
del_ebin_1([H|T]) ->
[H|del_ebin_1(T)];
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index 47d0c1b861..8d2fc4d4b7 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -143,7 +143,11 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie),
send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)),
?debug({dist_util, self(), accept_connection, Node}),
- connection(HSData).
+ connection(HSData);
+
+handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_other_started(convert_old_hsdata(OldHsData)).
+
%%
%% check if connecting node is allowed to connect
@@ -330,7 +334,20 @@ handshake_we_started(#hs_data{request_type=ReqType,
gen_digest(ChallengeA,HisCookie)),
reset_timer(NewHSData#hs_data.timer),
recv_challenge_ack(NewHSData, MyChallenge, MyCookie),
- connection(NewHSData).
+ connection(NewHSData);
+
+handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_we_started(convert_old_hsdata(OldHsData)).
+
+convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR,
+ FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) ->
+ #hs_data{
+ kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T,
+ this_flags = TF, allowed = A, other_version = OV, other_flags = OF,
+ other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE,
+ f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA,
+ mf_tick = MFT, mf_getstat = MFG, request_type = RT}.
+
%% --------------------------------------------------------------
%% The connection has been established.
@@ -350,15 +367,15 @@ connection(#hs_data{other_node = Node,
mark_nodeup(HSData,Address),
case FPostNodeup(Socket) of
ok ->
- con_loop(HSData#hs_data.kernel_pid,
- Node,
- Socket,
- Address,
- HSData#hs_data.this_node,
- PType,
- #tick{},
- HSData#hs_data.mf_tick,
- HSData#hs_data.mf_getstat);
+ con_loop({HSData#hs_data.kernel_pid,
+ Node,
+ Socket,
+ PType,
+ HSData#hs_data.mf_tick,
+ HSData#hs_data.mf_getstat,
+ HSData#hs_data.mf_setopts,
+ HSData#hs_data.mf_getopts},
+ #tick{});
_ ->
?shutdown2(Node, connection_setup_failed)
end;
@@ -454,8 +471,8 @@ mark_nodeup(#hs_data{kernel_pid = Kernel,
?shutdown(Node)
end.
-con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, Tick, MFTick, MFGetstat) ->
+con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData,
+ Tick) ->
receive
{tcp_closed, Socket} ->
?shutdown2(Node, connection_closed);
@@ -468,15 +485,12 @@ con_loop(Kernel, Node, Socket, TcpAddress,
_ ->
ignore_it
end,
- con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type,
- Tick, MFTick, MFGetstat);
+ con_loop(ConData, Tick);
{Kernel, tick} ->
case send_tick(Socket, Tick, Type,
MFTick, MFGetstat) of
{ok, NewTick} ->
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, NewTick, MFTick,
- MFGetstat);
+ con_loop(ConData, NewTick);
{error, not_responding} ->
error_msg("** Node ~p not responding **~n"
"** Removing (timedout) connection **~n",
@@ -489,13 +503,24 @@ con_loop(Kernel, Node, Socket, TcpAddress,
case MFGetstat(Socket) of
{ok, Read, Write, _} ->
From ! {self(), get_status, {ok, Read, Write}},
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode,
- Type, Tick,
- MFTick, MFGetstat);
+ con_loop(ConData, Tick);
_ ->
?shutdown2(Node, get_status_failed)
- end
+ end;
+ {From, Ref, {setopts, Opts}} ->
+ Ret = case MFSetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFSetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick);
+ {From, Ref, {getopts, Opts}} ->
+ Ret = case MFGetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFGetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick)
end.
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index f8ef4a475d..7bc9e2ede3 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -103,6 +103,10 @@ names(EpmdAddr) ->
register_node(Name, PortNo) ->
register_node(Name, PortNo, inet).
+register_node(Name, PortNo, inet_tcp) ->
+ register_node(Name, PortNo, inet);
+register_node(Name, PortNo, inet6_tcp) ->
+ register_node(Name, PortNo, inet6);
register_node(Name, PortNo, Family) ->
gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
@@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 ->
0;
select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
0;
-select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
- 0;
select_best_version(_L1, H1, _L2, H2) ->
erlang:min(H1, H2).
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index a91a6ed517..75dd800c6b 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -75,6 +75,7 @@
-export_type([address_family/0, hostent/0, hostname/0, ip4_address/0,
ip6_address/0, ip_address/0, port_number/0,
local_address/0, socket_address/0, returned_non_ip_address/0,
+ socket_setopt/0, socket_getopt/0,
posix/0, socket/0, stat_option/0]).
%% imports
-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index 3aa61973af..9b6c2745d5 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -24,6 +24,7 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+-export([setopts/2, getopts/2]).
%% ------------------------------------------------------------
%% Select this protocol based on node name
@@ -72,3 +73,9 @@ close(Socket) ->
is_node_name(Node) when is_atom(Node) ->
inet_tcp_dist:is_node_name(Node).
+
+setopts(S, Opts) ->
+ inet_tcp_dist:setopts(S, Opts).
+
+getopts(S, Opts) ->
+ inet_tcp_dist:getopts(S, Opts).
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index f91d7ef7c3..3084bd599a 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -24,13 +24,16 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Optional
+-export([setopts/2, getopts/2]).
+
%% Generalized dist API
-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
gen_setup/6, gen_select/2]).
%% internal exports
--export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]).
+-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1,tick/2]).
-import(error_logger,[error_msg/2]).
@@ -74,7 +77,7 @@ gen_listen(Driver, Name) ->
TcpAddress = get_tcp_address(Driver, Socket),
{_,Port} = TcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{ok, {Socket, TcpAddress, Creation}};
Error ->
@@ -215,8 +218,10 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
inet:getll(S)
end,
f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end,
- mf_tick = fun(S) -> tick(Driver, S) end,
- mf_getstat = fun ?MODULE:getstat/1
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
+ mf_getstat = fun ?MODULE:getstat/1,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_other_started(HSData);
{false,IP} ->
@@ -320,6 +325,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
{packet, 4},
nodelay()])
end,
+
f_getll = fun inet:getll/1,
f_address =
fun(_,_) ->
@@ -329,9 +335,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
protocol = tcp,
family = AddressFamily}
end,
- mf_tick = fun(S) -> tick(Driver, S) end,
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
mf_getstat = fun ?MODULE:getstat/1,
- request_type = Type
+ request_type = Type,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_we_started(HSData);
_ ->
@@ -492,3 +500,12 @@ split_stat([], R, W, P) ->
{ok, R, W, P}.
+setopts(S, Opts) ->
+ case [Opt || {K,_}=Opt <- Opts,
+ K =:= active orelse K =:= deliver orelse K =:= packet] of
+ [] -> inet:setopts(S,Opts);
+ Opts1 -> {error, {badopts,Opts1}}
+ end.
+
+getopts(S, Opts) ->
+ inet:getopts(S, Opts).
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index ac19f4935b..0c679e7349 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -59,6 +59,8 @@
connect_node/1,
monitor_nodes/1,
monitor_nodes/2,
+ setopts/2,
+ getopts/2,
start/1,
stop/0]).
@@ -111,7 +113,7 @@
}).
-record(listen, {
- listen, %% listen pid
+ listen, %% listen socket
accept, %% accepting pid
address, %% #net_address
module %% proto module
@@ -384,7 +386,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) ->
connections =
ets:new(sys_dist,[named_table,
protected,
- {keypos, 2}]),
+ {keypos, #connection.node}]),
listen = Listeners,
allowed = [],
verbose = 0
@@ -554,6 +556,38 @@ handle_call({new_ticktime,_T,_TP},
#state{tick = #tick_change{time = T}} = State) ->
async_reply({reply, {ongoing_change_to, T}, State}, From);
+handle_call({setopts, new, Opts}, From, State) ->
+ Ret = setopts_new(Opts, State),
+ async_reply({reply, Ret, State}, From);
+
+handle_call({setopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {setopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
+handle_call({getopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {getopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
handle_call(_Msg, _From, State) ->
{noreply, State}.
@@ -1608,3 +1642,93 @@ async_gen_server_reply(From, Msg) ->
{'EXIT', _} ->
ok
end.
+
+call_owner(Owner, Msg) ->
+ Mref = monitor(process, Owner),
+ Owner ! {self(), Mref, Msg},
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, _} ->
+ error
+ end.
+
+
+-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when
+ Node :: node() | new,
+ Options :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+setopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({setopts, Node, Opts}).
+
+setopts_new(Opts, State) ->
+ %% First try setopts on listening socket(s)
+ %% Bail out on failure.
+ %% If successful, we are pretty sure Opts are ok
+ %% and we continue with config params and pending connections.
+ case setopts_on_listen(Opts, State#state.listen) of
+ ok ->
+ setopts_new_1(Opts);
+ Fail -> Fail
+ end.
+
+setopts_on_listen(_, []) -> ok;
+setopts_on_listen(Opts, [#listen {listen = LSocket, module = Mod} | T]) ->
+ try Mod:setopts(LSocket, Opts) of
+ ok ->
+ setopts_on_listen(Opts, T);
+ Fail -> Fail
+ catch
+ error:undef -> {error, enotsup}
+ end.
+
+setopts_new_1(Opts) ->
+ ConnectOpts = case application:get_env(kernel, inet_dist_connect_options) of
+ {ok, CO} -> CO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_connect_options,
+ merge_opts(Opts,ConnectOpts)),
+ ListenOpts = case application:get_env(kernel, inet_dist_listen_options) of
+ {ok, LO} -> LO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_listen_options,
+ merge_opts(Opts, ListenOpts)),
+ case lists:keyfind(nodelay, 1, Opts) of
+ {nodelay, ND} when is_boolean(ND) ->
+ application:set_env(kernel, dist_nodelay, ND);
+ _ -> ignore
+ end,
+
+ %% Update any pending connections
+ PendingConns = ets:select(sys_dist, [{'_',
+ [{'=/=',{element,#connection.state,'$_'},up}],
+ ['$_']}]),
+ lists:foreach(fun(#connection{state = pending, owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (#connection{state = up_pending, pending_owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (_) -> ignore
+ end, PendingConns),
+ ok.
+
+merge_opts([], B) ->
+ B;
+merge_opts([H|T], B0) ->
+ {Key, _} = H,
+ B1 = lists:filter(fun({K,_}) -> K =/= Key end, B0),
+ merge_opts(T, [H | B1]).
+
+-spec getopts(Node, Options) ->
+ {'ok', OptionValues} | {'error', Reason} | ignored when
+ Node :: node(),
+ Options :: [inet:socket_getopt()],
+ OptionValues :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+getopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({getopts, Node, Opts}).
+
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f0ad26b1f2..05bbf1069e 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -226,11 +226,13 @@ extensions() ->
Command :: atom() | io_lib:chars().
cmd(Cmd) ->
validate(Cmd),
- {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd),
+ {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
- stream, in, eof, hide | SpawnOpts]),
+ stream, in, hide | SpawnOpts]),
+ MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, []),
+ Bytes = get_data(Port, MonRef, Eot, []),
+ demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
is_list(String) -> String;
@@ -243,7 +245,7 @@ mk_cmd({win32,Wtype}, Cmd) ->
{false,_} -> lists:concat(["cmd /c", Cmd]);
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
- {Command, [], []};
+ {Command, [], [], <<>>};
mk_cmd(OsType,Cmd) when is_atom(Cmd) ->
mk_cmd(OsType, atom_to_list(Cmd));
mk_cmd(_,Cmd) ->
@@ -252,7 +254,8 @@ mk_cmd(_,Cmd) ->
{"/bin/sh -s unix:cmd", [out],
%% We insert a new line after the command, in case the command
%% contains a comment character.
- ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}.
+ ["(", unicode:characters_to_binary(Cmd), "\n); echo \"\^D\"\n"],
+ <<$\^D>>}.
validate(Atom) when is_atom(Atom) ->
ok;
@@ -267,21 +270,44 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, [Sofar,Bytes]);
- {Port, eof} ->
- Port ! {self(), close},
- receive
- {Port, closed} ->
- true
- end,
- receive
- {'EXIT', Port, _} ->
- ok
- after 1 -> % force context switch
- ok
- end,
+ case eot(Bytes, Eot) of
+ more ->
+ get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ Last ->
+ Port ! {self(), close},
+ flush_until_closed(Port),
+ flush_exit(Port),
+ iolist_to_binary([Sofar, Last])
+ end;
+ {'DOWN', MonRef, _, _ , _} ->
+ flush_exit(Port),
iolist_to_binary(Sofar)
end.
+
+eot(_Bs, <<>>) ->
+ more;
+eot(Bs, Eot) ->
+ case binary:match(Bs, Eot) of
+ nomatch -> more;
+ {Pos, _} ->
+ binary:part(Bs,{0, Pos})
+ end.
+
+flush_until_closed(Port) ->
+ receive
+ {Port, {data, _Bytes}} ->
+ flush_until_closed(Port);
+ {Port, closed} ->
+ true
+ end.
+
+flush_exit(Port) ->
+ receive
+ {'EXIT', Port, _} ->
+ ok
+ after 1 -> % force context switch
+ ok
+ end.
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index eb58e92224..e43be77428 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -25,6 +25,7 @@
init_per_group/2,end_per_group/2]).
-export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1,
+ setopts/1,
table_waste/1, net_setuptime/1,
inet_dist_options_options/1,
@@ -42,6 +43,8 @@
-export([get_socket_priorities/0,
tick_cli_test/1, tick_cli_test1/1,
tick_serv_test/2, tick_serv_test1/1,
+ run_remote_test/1,
+ setopts_do/2,
keep_conn/1, time_ping/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -66,6 +69,7 @@ suite() ->
all() ->
[tick, tick_change, illegal_nodenames, hidden_node,
+ setopts,
table_waste, net_setuptime, inet_dist_options_options,
{group, monitor_nodes}].
@@ -282,6 +286,165 @@ tick_cli_test1(Node) ->
end
end.
+setopts(Config) when is_list(Config) ->
+ register(setopts_regname, self()),
+ [N1,N2,N3,N4] = get_nodenames(4, setopts),
+
+ {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "1", "ping"]),
+ 0 = wait_for_port_exit(Port1),
+
+ {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "2", "ping"]),
+ 0 = wait_for_port_exit(Port2),
+
+ {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]),
+ {ok, LTcpPort} = inet:port(LSock),
+
+ {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "1", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N3F, Port3),
+ 0 = wait_for_port_exit(Port3),
+
+ {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "2", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N4F, Port4),
+ 0 = wait_for_port_exit(Port4),
+
+ ok.
+
+wait_and_connect(LSock, NodeName, NodePort) ->
+ {ok, Sock} = gen_tcp:accept(LSock),
+ {ok, "Connect please"} = gen_tcp:recv(Sock, 0),
+ flush_from_port(NodePort),
+ pong = net_adm:ping(NodeName),
+ gen_tcp:send(Sock, "Connect done"),
+ gen_tcp:close(Sock).
+
+
+flush_from_port(Port) ->
+ flush_from_port(Port, 10).
+
+flush_from_port(Port, Timeout) ->
+ receive
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ flush_from_port(Port, Timeout)
+ after Timeout ->
+ timeout
+ end.
+
+wait_for_port_exit(Port) ->
+ case (receive M -> M end) of
+ {Port,{exit_status,Status}} ->
+ Status;
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ wait_for_port_exit(Port)
+ end.
+
+run_remote_test([FuncStr, TestNodeStr | Args]) ->
+ Status = try
+ io:format("Node ~p started~n", [node()]),
+ TestNode = list_to_atom(TestNodeStr),
+ io:format("Node ~p spawning function ~p~n", [node(), FuncStr]),
+ {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]),
+ io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]),
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ 0;
+ Other ->
+ io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]),
+ 1
+ end
+ catch
+ C:E ->
+ io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
+ [node(), C, E, erlang:get_stacktrace()]),
+ 2
+ end,
+ io:format("Node ~p doing halt(~p).\n",[node(), Status]),
+ erlang:halt(Status).
+
+% Do the actual test on the remote node
+setopts_do(TestNode, [OptNr, ConnectData]) ->
+ [] = nodes(),
+ {Opt, Val} = opt_from_nr(OptNr),
+ ok = net_kernel:setopts(new, [{Opt, Val}]),
+
+ [] = nodes(),
+ {error, noconnection} = net_kernel:getopts(TestNode, [Opt]),
+
+ case ConnectData of
+ "ping" -> % We connect
+ net_adm:ping(TestNode);
+ TcpPort -> % Other connect
+ {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort),
+ [{active,false},{packet,2}]),
+ ok = gen_tcp:send(Sock, "Connect please"),
+ {ok, "Connect done"} = gen_tcp:recv(Sock, 0),
+ gen_tcp:close(Sock)
+ end,
+ [TestNode] = nodes(),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+ {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]),
+
+ NewVal = change_val(Val),
+ ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]),
+ {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok = net_kernel:setopts(TestNode, [{Opt, Val}]),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok.
+
+opt_from_nr("1") -> {nodelay, true};
+opt_from_nr("2") -> {nodelay, false}.
+
+change_val(true) -> false;
+change_val(false) -> true.
+
+start_node_unconnected(Name, Mod, Func, Args) ->
+ FullName = full_node_name(Name),
+ CmdLine = mk_node_cmdline(Name,Mod,Func,Args),
+ io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
+ case open_port({spawn, CmdLine}, [exit_status]) of
+ Port when is_port(Port) ->
+ {FullName, Port};
+ Error ->
+ exit({failed_to_start_node, FullName, Error})
+ end.
+
+full_node_name(PreName) ->
+ HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
+ atom_to_list(node())),
+ list_to_atom(atom_to_list(PreName) ++ HostSuffix).
+
+mk_node_cmdline(Name,Mod,Func,Args) ->
+ Static = "-noinput",
+ Pa = filename:dirname(code:which(?MODULE)),
+ Prog = case catch init:get_argument(progname) of
+ {ok,[[P]]} -> P;
+ _ -> exit(no_progname_argument_found)
+ end,
+ NameSw = case net_kernel:longnames() of
+ false -> "-sname ";
+ true -> "-name ";
+ _ -> exit(not_distributed_node)
+ end,
+ {ok, Pwd} = file:get_cwd(),
+ NameStr = atom_to_list(Name),
+ Prog ++ " "
+ ++ Static ++ " "
+ ++ NameSw ++ " " ++ NameStr
+ ++ " -pa " ++ Pa
+ ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
+ ++ " -setcookie " ++ atom_to_list(erlang:get_cookie())
+ ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func)
+ ++ " " ++ string:join(Args, " ").
+
%% OTP-4255.
tick_change(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index f836b2aa94..620ab235a0 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) ->
Stream = 0,
Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
Loopback = {127,0,0,1},
+ StatOpts =
+ [recv_avg,recv_cnt,recv_max,recv_oct,
+ send_avg,send_cnt,send_max,send_oct],
{ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ {ok,SbStat1} = inet:getstat(Sb, StatOpts),
{ok,Pb} = inet:port(Sb),
ok = gen_sctp:listen(Sb, true),
@@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}} =
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
ok = gen_sctp:close(Sa),
+ {ok,SbStat2} = inet:getstat(Sb, StatOpts),
+ [] = filter_stat_eq(SbStat1, SbStat2),
ok = gen_sctp:close(Sb),
receive
@@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) ->
end,
ok.
+filter_stat_eq([], []) ->
+ [];
+filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
+ if
+ Val1 == Val2 ->
+ [Stat|filter_stat_eq(SbStat1, SbStat2)];
+ true ->
+ filter_stat_eq(SbStat1, SbStat2)
+ end.
+
+
+
%% Minimal data transfer in active mode.
xfer_active(Config) when is_list(Config) ->
Timeout = 2000,
@@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) ->
assoc_id=S2AssocId} = S2AssocChange =
log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- case recv_event(log_ok(gen_sctp:recv(S1))) of
- {Loopback,P2,
- #sctp_assoc_change{
- state=comm_up,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId);
- {Loopback,P2,
- #sctp_paddr_change{
- state=addr_confirmed,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId),
+ S1AssocId =
+ case recv_event(log_ok(gen_sctp:recv(S1))) of
{Loopback,P2,
#sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} =
- recv_event(log_ok(gen_sctp:recv(S1)))
- end,
+ assoc_id=AssocId}} ->
+ AssocId;
+ {Loopback,P2,
+ #sctp_paddr_change{
+ state=addr_confirmed,
+ error=0,
+ assoc_id=AssocId}} ->
+ {Loopback,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1))),
+ AssocId
+ end,
+ ?LOGVAR(S1AssocId),
#sctp_sndrcvinfo{
ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
@@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 0,
Timeout = 333,
+ StartTime = timestamp(),
S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S2Ai),
S1Ai =
@@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S1Ai),
%%
@@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
receive
{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
@@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
receive
{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
%%
inet:i(sctp),
- socket_close_verbose(S1),
- socket_close_verbose(S2),
+ socket_close_verbose(S1, StartTime),
+ socket_close_verbose(S2, StartTime),
receive
{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
match_unless_solaris(S3Ai, S3Ai_X)
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
receive
{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S3Ai}}} -> ok
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
- socket_close_verbose(S3),
+ socket_close_verbose(S3, StartTime),
[] = flush(),
ok.
@@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 1,
Timeout = 3333,
+ StartTime = timestamp(),
S1 = socket_open([{ip,Addr}], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
S1Ai =
receive
@@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
socket_call(S1, {setopts,[{recbuf,Limit}]}),
@@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
- socket_close_verbose(S1),
+ socket_close_verbose(S1, StartTime),
receive
{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
receive
{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
- socket_close_verbose(S2),
+ socket_close_verbose(S2, StartTime),
[] = flush(),
ok.
@@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
end,
s_start(Starter, Timeout).
-socket_close_verbose(S) ->
- History = socket_history(socket_close(S)),
+socket_close_verbose(S, StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("socket_close ~p:~n ~p.~n", [S,History]),
History.
@@ -1535,19 +1557,19 @@ socket_call(S, Request) ->
%% socket_get(S, Key) ->
%% s_req(S, {get,Key}).
-socket_bailout([S|Ss]) ->
- History = socket_history(socket_close(S)),
+socket_bailout([S|Ss], StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("bailout ~p:~n ~p.~n", [S,History]),
- socket_bailout(Ss);
-socket_bailout([]) ->
+ socket_bailout(Ss, StartTime);
+socket_bailout([], _) ->
io:format("flush: ~p.~n", [flush()]),
ct:fail(socket_bailout).
-socket_history({State,Flush}) ->
+socket_history({State,Flush}, StartTime) ->
{lists:keysort(
2,
lists:flatten(
- [[{Key,Val} || Val <- Vals]
+ [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
|| {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
@@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{Parent,Ref,exit} ->
ok = gen_sctp:close(Socket),
Key = exit,
- Val = {now(),Socket},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, Socket, State),
Parent ! {self(),Ref,{NewState,flush()}};
{Parent,Ref,{Msg}} ->
Result = Handler(Msg),
Key = req,
- Val = {now(),{Msg,Result}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Msg,Result}, State),
Parent ! {self(),Ref,Result},
s_loop(Socket, Timeout, Parent, Handler, NewState);
%% {Parent,Ref,{get,Key}} ->
@@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
when not is_tuple(Data) ->
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,
- #sctp_assoc_change{
- state=comm_up,
- inbound_streams=Is}}}|_]
+ [{Addr,Port,
+ #sctp_assoc_change{
+ state=comm_up,
+ inbound_streams=Is}}|_]
when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
- Val = {now(),{Addr,Port,SRI,Data}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
Key = {assoc_change,AssocId},
- Val = {now(),{Addr,Port,SAC}},
case {gb_get(Key, State),St} of
{[],_} -> ok;
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SAC}, State),
Parent ! {self(),{Addr,Port,SAC}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= addr_available;
St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},
- Val = {now(),{Addr,Port,SPC}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SPC}, State),
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
{sctp,Socket,Addr,Port,
@@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+ [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
[] -> ok
end,
Key = {shutdown_event,AssocId},
- Val = {now(),{Addr,Port}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port}, State),
Parent ! {self(), {Addr,Port,SSE}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1707,11 +1723,12 @@ again(Socket) ->
end.
gb_push(Key, Val, GBT) ->
+ TS = timestamp(),
case gb_trees:lookup(Key, GBT) of
none ->
- gb_trees:insert(Key, [Val], GBT);
+ gb_trees:insert(Key, [{TS,Val}], GBT);
{value,V} ->
- gb_trees:update(Key, [Val|V], GBT)
+ gb_trees:update(Key, [{TS,Val}|V], GBT)
end.
gb_get(Key, GBT) ->
@@ -1719,7 +1736,7 @@ gb_get(Key, GBT) ->
none ->
[];
{value,V} ->
- V
+ [Val || {_TS,Val} <- V]
end.
match_unless_solaris(A, B) ->
@@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) ->
{unix,sunos} -> B;
_ -> A = B
end.
+
+timestamp() ->
+ erlang:monotonic_time().
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 2a1e5016ec..19ab3713a1 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -24,7 +24,8 @@
init_per_testcase/2,end_per_testcase/2]).
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
- large_output_command/1, perf_counter_api/1]).
+ large_output_command/1, background_command/0, background_command/1,
+ message_leak/1, perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +36,8 @@ suite() ->
all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
- large_output_command, perf_counter_api].
+ large_output_command, background_command, message_leak,
+ perf_counter_api].
groups() ->
[].
@@ -52,6 +54,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(background_command, Config) ->
+ case os:type() of
+ {win32, _} ->
+ {skip,"Should not work on windows"};
+ _ ->
+ Config
+ end;
init_per_testcase(_TC,Config) ->
Config.
@@ -261,13 +270,38 @@ deep_list_command(Config) when is_list(Config) ->
%% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"])
ok.
-%% Test to take sure that the correct data is
+%% Test to make sure that the correct data is
%% received when doing large commands.
large_output_command(Config) when is_list(Config) ->
%% Maximum allowed on windows is 8192, so we test well below that
AAA = lists:duplicate(7000, $a),
comp(AAA,os:cmd("echo " ++ AAA)).
+%% Test that it is possible on unix to start a background task using os:cmd.
+background_command() ->
+ [{timetrap, {seconds, 5}}].
+background_command(_Config) ->
+ %% This testcase fails when the os:cmd takes
+ %% longer then the 5 second timeout
+ os:cmd("sleep 10&").
+
+%% Test that message does not leak to the calling process
+message_leak(_Config) ->
+ process_flag(trap_exit, true),
+
+ os:cmd("echo hello"),
+ [] = receive_all(),
+
+ case os:type() of
+ {unix, _} ->
+ os:cmd("while true; do echo hello; done&"),
+ [] = receive_all();
+ _ ->
+ ok % Cannot background on non-unix
+ end,
+
+ process_flag(trap_exit, false).
+
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e7d422d03c..cdd200a234 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.0
+KERNEL_VSN = 5.0.2
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index cb2b23d534..d26daa5eda 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -147,7 +147,7 @@ AC_SUBST(THR_LIBS)
odbc_lib_link_success=no
AC_SUBST(TARGET_FLAGS)
case $host_os in
- darwin1[[0-4]].*|darwin[[0-9]].*)
+ darwin1[[0-5]].*|darwin[[0-9]].*)
TARGET_FLAGS="-DUNIX"
if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then
ODBC_LIB= -L"/usr/lib"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 3b6f988a2d..9d68ee0eee 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,26 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The TLS/SSL protocol version selection for the SSL server
+ has been corrected to follow RFC 5246 Appendix E.1
+ especially in case where the list of supported versions
+ has gaps. Now the server selects the highest protocol
+ version it supports that is not higher than what the
+ client supports.</p>
+ <p>
+ Own Id: OTP-13753 Aux Id: seq13150 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 11728128c4..fadc67ef80 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,20 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"7\\..*">>, [{restart_application, ssl}]},
+ {<<"^8[.]0$">>,
+ [{load_module, ssl_handshake, soft_purge, soft_purge, []}
+ ]},
+ {<<"^7[.][^.].*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"7\\..*">>, [{restart_application, ssl}]},
+ {<<"^8[.]0$">>,
+ [{load_module, ssl_handshake, soft_purge, soft_purge, []}
+ ]},
+ {<<"^7[.][^.].*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index bca341c8bc..081efda768 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -658,7 +658,7 @@ select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
end;
select_hashsign(#certificate_request{}, Cert, _, Version) ->
- select_hashsign(undefined, Cert, undefined, undefined, Version).
+ select_hashsign(undefined, Cert, undefined, [], Version).
%%--------------------------------------------------------------------
-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
@@ -1305,8 +1305,40 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
end.
select_version(RecordCB, ClientVersion, Versions) ->
- ServerVersion = RecordCB:highest_protocol_version(Versions),
- RecordCB:lowest_protocol_version(ClientVersion, ServerVersion).
+ do_select_version(RecordCB, ClientVersion, Versions).
+
+do_select_version(_, ClientVersion, []) ->
+ ClientVersion;
+do_select_version(RecordCB, ClientVersion, [Version | Versions]) ->
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client - keep looking
+ do_select_version(RecordCB, ClientVersion, Versions);
+ false ->
+ %% Version ok for client - look for a higher
+ do_select_version(RecordCB, ClientVersion, Versions, Version)
+ end.
+%%
+do_select_version(_, _, [], GoodVersion) ->
+ GoodVersion;
+do_select_version(
+ RecordCB, ClientVersion, [Version | Versions], GoodVersion) ->
+ BetterVersion =
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client
+ GoodVersion;
+ false ->
+ %% Version ok for client
+ case RecordCB:is_higher(Version, GoodVersion) of
+ true ->
+ %% Use higher version
+ Version;
+ false ->
+ GoodVersion
+ end
+ end,
+ do_select_version(RecordCB, ClientVersion, Versions, BetterVersion).
renegotiation_info(_, client, _, false) ->
#renegotiation_info{renegotiated_connection = undefined};
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a920f54ed2..08947f24dd 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -117,7 +117,7 @@ handle_call({listen, Driver, Name}, _From, State) ->
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{reply, {ok, {Socket, TcpAddress, Creation}},
State#state{listen={Socket, World}}};
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 00eb9fee4f..6ae9efe5e9 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -41,6 +41,15 @@ all() ->
create_server_hello_with_advertised_protocols_test,
create_server_hello_with_no_advertised_protocols_test].
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 3b51fa8c6b..6afac59109 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.0
+SSL_VSN = 8.0.1
diff --git a/lib/syntax_tools/src/merl_tests.erl b/lib/syntax_tools/src/merl_tests.erl
index c1aae3100e..27db594050 100644
--- a/lib/syntax_tools/src/merl_tests.erl
+++ b/lib/syntax_tools/src/merl_tests.erl
@@ -48,6 +48,21 @@ parse_error_test_() ->
f(merl:quote("{")))
].
+transform_parse_error_test_() ->
+ [?_assertEqual("merl:quote(\"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(\"{\")")], []))),
+ ?_assertEqual("merl:quote(2, \"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(2, \"{\")")], []))),
+ ?_assertEqual("merl:qquote(\"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(\"{\", [{var, V}])")], []))),
+ ?_assertEqual("merl:qquote(2, \"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(2, \"{\", [{var, V}])")], [])))
+ ].
+
term_test_() ->
[?_assertEqual(tuple, erl_syntax:type(merl:term({}))),
?_assertEqual("{foo, 42}", f(merl:term({foo, 42})))
diff --git a/lib/syntax_tools/src/merl_transform.erl b/lib/syntax_tools/src/merl_transform.erl
index fe58b6a122..497baddd0a 100644
--- a/lib/syntax_tools/src/merl_transform.erl
+++ b/lib/syntax_tools/src/merl_transform.erl
@@ -104,10 +104,15 @@ expand_qquote([Text, Env], T, Line) ->
case erl_syntax:is_literal(Text) of
true ->
As = [Line, erl_syntax:concrete(Text)],
- %% expand further if possible
- expand(merl:qquote(Line, "merl:subst(_@tree, _@env)",
- [{tree, eval_call(Line, quote, As, T)},
- {env, Env}]));
+ case eval_call(Line, quote, As, failed) of
+ failed ->
+ T;
+ T1 ->
+ %% expand further if possible
+ expand(merl:qquote(Line, "merl:subst(_@tree, _@env)",
+ [{tree, T1},
+ {env, Env}]))
+ end;
false ->
T
end;
diff --git a/lib/syntax_tools/test/merl_SUITE.erl b/lib/syntax_tools/test/merl_SUITE.erl
index 945972d405..52bbd9b3b8 100644
--- a/lib/syntax_tools/test/merl_SUITE.erl
+++ b/lib/syntax_tools/test/merl_SUITE.erl
@@ -29,12 +29,14 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([merl_smoke_test/1]).
+-export([merl_smoke_test/1,
+ transform_parse_error_test/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [merl_smoke_test].
+ [merl_smoke_test,
+ transform_parse_error_test].
groups() ->
[].
@@ -84,6 +86,21 @@ merl_smoke_test(Config) when is_list(Config) ->
end)),
ok.
+transform_parse_error_test(_Config) ->
+ ?assertEqual("merl:quote(\"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(\"{\")")], []))),
+ ?assertEqual("merl:quote(2, \"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(2, \"{\")")], []))),
+ ?assertEqual("merl:qquote(\"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(\"{\", [{var, V}])")], []))),
+ ?assertEqual("merl:qquote(2, \"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(2, \"{\", [{var, V}])")], []))),
+ ok.
+
%% utilities
f(Ts) when is_list(Ts) ->
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index 8db23dd151..1291a3e5ec 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1636,6 +1636,11 @@ trace_handler({trace_ts, Pid, gc_major_start, _Func, TS} = Trace, Table, _, Dump
dump_stack(Dump, get(Pid), Trace),
trace_gc_start(Table, Pid, TS),
TS;
+
+trace_handler({trace_ts, Pid, gc_start, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_start(Table, Pid, TS),
+ TS;
%%
%% gc_end
@@ -1648,6 +1653,12 @@ trace_handler({trace_ts, Pid, gc_major_end, _Func, TS} = Trace, Table, _, Dump)
dump_stack(Dump, get(Pid), Trace),
trace_gc_end(Table, Pid, TS),
TS;
+
+trace_handler({trace_ts, Pid, gc_end, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_end(Table, Pid, TS),
+ TS;
+
%%
%% link
trace_handler({trace_ts, Pid, link, _OtherPid, TS} = Trace,
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.erl b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
index acdb69fdeb..355a4cdfd1 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.erl
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
@@ -43,33 +43,36 @@ SortItems>>
<<EXPORT:wxListCtrl new/0, new/1, new/2 wxListCtrl:EXPORT>>
<<wxListCtrl_new_0
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(~s, <<>>).
wxListCtrl_new_0>>
<<wxListCtrl_new_2
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -101,26 +104,27 @@ wxListCtrl_new_2>>
<<EXPORT:Create create/2, create/3 Create:EXPORT>>
<<Create
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/api_gen/wx_extra/wxXmlResource.erl b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
index 7700e2333e..b29ffba7c6 100644
--- a/lib/wx/api_gen/wx_extra/wxXmlResource.erl
+++ b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
@@ -21,8 +21,6 @@
<<EXPORT:xrcctrl xrcctrl/3 xrcctrl:EXPORT>>
<<xrcctrl
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -32,6 +30,10 @@
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ~s
diff --git a/lib/wx/c_src/wxe_main.cpp b/lib/wx/c_src/wxe_main.cpp
index 6fcde42eb5..c7565e33bd 100644
--- a/lib/wx/c_src/wxe_main.cpp
+++ b/lib/wx/c_src/wxe_main.cpp
@@ -67,6 +67,7 @@ int load_native_gui()
int start_native_gui(wxe_data *sd)
{
int res;
+ ErlDrvThreadOpts *opts = NULL;
wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m");
wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c");
@@ -78,8 +79,11 @@ int start_native_gui(wxe_data *sd)
res = erl_drv_steal_main_thread((char *)"wxwidgets",
&wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#else
+ opts = erl_drv_thread_opts_create((char *)"wx thread");
+ opts->suggested_stack_size = 8192;
res = erl_drv_thread_create((char *)"wxwidgets",
- &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
+ &wxe_thread,wxe_main_loop,(void *) sd->pdl,opts);
+ erl_drv_thread_opts_destroy(opts);
#endif
if(res == 0) {
erl_drv_mutex_lock(wxe_status_m);
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index af7cca7ed2..30baeebb18 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -33,94 +33,51 @@
%% Here comes the definitions of all event records.
%% they contain the event type and possible some extra information.
--record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
--type wxInitDialogEventType() :: 'init_dialog'.
--type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
-
--record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
--type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
--type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
-
--record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
- position :: integer(),
- key :: integer(),
- modifiers :: integer(),
- modificationType :: integer(),
- text :: unicode:chardata(),
- length :: integer(),
- linesAdded :: integer(),
- line :: integer(),
- foldLevelNow :: integer(),
- foldLevelPrev :: integer(),
- margin :: integer(),
- message :: integer(),
- wParam :: integer(),
- lParam :: integer(),
- listType :: integer(),
- x :: integer(),
- y :: integer(),
- dragText :: unicode:chardata(),
- dragAllowMove :: boolean(),
- dragResult :: wx:wx_enum()}).
--type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
--type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
-
--record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
- path :: unicode:chardata()}).
--type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
--type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-
--record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
- nSel :: integer(),
- nOldSel :: integer()}).
--type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
--type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
+-record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
+ win :: wxWindow:wxWindow()}).
+-type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
+-type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
--record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
--type wxIdleEventType() :: 'idle'.
--type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
+-record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
+ cmdString :: unicode:chardata(),
+ commandInt :: integer(),
+ extraLong :: integer()}).
+-type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
+-type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
-record(wxColourPicker,{type :: wxColourPickerEventType(), %% Callback event: {@link wxColourPickerEvent}
colour :: wx:wx_colour()}).
-type wxColourPickerEventType() :: 'command_colourpicker_changed'.
-type wxColourPicker() :: #wxColourPicker{}. %% Callback event: {@link wxColourPickerEvent}
--record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
--type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
--type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
-
--record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
- edge :: wx:wx_enum(),
- dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
- dragStatus :: wx:wx_enum()}).
--type wxSashEventType() :: 'sash_dragged'.
--type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
-
-record(wxHelp, {type :: wxHelpEventType()}). %% Callback event: {@link wxHelpEvent}
-type wxHelpEventType() :: 'help' | 'detailed_help'.
-type wxHelp() :: #wxHelp{}. %% Callback event: {@link wxHelpEvent}
--record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
--type wxDisplayChangedEventType() :: 'display_changed'.
--type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
-
--record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
--type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
--type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+-record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
+ commandInt :: integer()}).
+-type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
+-type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
--record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
- font :: wxFont:wxFont()}).
--type wxFontPickerEventType() :: 'command_fontpicker_changed'.
--type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
+-record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
+ manager :: wxAuiManager:wxAuiManager(),
+ pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
+ button :: integer(),
+ veto_flag :: boolean(),
+ canveto_flag :: boolean(),
+ dc :: wxDC:wxDC()}).
+-type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
+-type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
--record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
- win :: wxWindow:wxWindow()}).
--type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
--type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
+-record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
+ date :: wx:wx_datetime()}).
+-type wxDateEventType() :: 'date_changed'.
+-type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
--record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
--type wxPaletteChangedEventType() :: 'palette_changed'.
--type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
+-record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
+ iconized :: boolean()}).
+-type wxIconizeEventType() :: 'iconize'.
+-type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
-record(wxScroll,{type :: wxScrollEventType(), %% Callback event: {@link wxScrollEvent}
commandInt :: integer(),
@@ -128,38 +85,41 @@
-type wxScrollEventType() :: 'scroll_top' | 'scroll_bottom' | 'scroll_lineup' | 'scroll_linedown' | 'scroll_pageup' | 'scroll_pagedown' | 'scroll_thumbtrack' | 'scroll_thumbrelease' | 'scroll_changed'.
-type wxScroll() :: #wxScroll{}. %% Callback event: {@link wxScrollEvent}
--record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
--type wxChildFocusEventType() :: 'child_focus'.
--type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
+-record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
+-type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
+-type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
--record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
- old_selection :: integer(),
- selection :: integer(),
- drag_source :: wxAuiNotebook:wxAuiNotebook()}).
--type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
--type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
+-record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
+-type wxPaletteChangedEventType() :: 'palette_changed'.
+-type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
--record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
- size :: {W::integer(), H::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxSizeEventType() :: 'size'.
--type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
+-record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
+ nSel :: integer(),
+ nOldSel :: integer()}).
+-type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
+-type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
--record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
- cmdString :: unicode:chardata(),
- commandInt :: integer(),
- extraLong :: integer()}).
--type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
--type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
+-record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
+ pos :: {X::integer(), Y::integer()}}).
+-type wxContextMenuEventType() :: 'context_menu'.
+-type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
--record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
--type wxMaximizeEventType() :: 'maximize'.
--type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
+-record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
+ font :: wxFont:wxFont()}).
+-type wxFontPickerEventType() :: 'command_fontpicker_changed'.
+-type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
--record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
- commandInt :: integer()}).
--type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
--type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
+-record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
+-type wxChildFocusEventType() :: 'child_focus'.
+-type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
+
+-record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
+-type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
+-type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
+
+-record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
+-type wxWindowDestroyEventType() :: 'destroy'.
+-type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
-record(wxMenu,{type :: wxMenuEventType(), %% Callback event: {@link wxMenuEvent}
menuId :: integer(),
@@ -167,30 +127,63 @@
-type wxMenuEventType() :: 'menu_open' | 'menu_close' | 'menu_highlight'.
-type wxMenu() :: #wxMenu{}. %% Callback event: {@link wxMenuEvent}
+-record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
+ active :: boolean()}).
+-type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
+-type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+
+-record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
+ row :: integer(),
+ col :: integer(),
+ x :: integer(),
+ y :: integer(),
+ selecting :: boolean(),
+ control :: boolean(),
+ meta :: boolean(),
+ shift :: boolean(),
+ alt :: boolean()}).
+-type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
+-type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+
+-record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
+-type wxPaintEventType() :: 'paint'.
+-type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+
-record(wxShow,{type :: wxShowEventType(), %% Callback event: {@link wxShowEvent}
show :: boolean()}).
-type wxShowEventType() :: 'show'.
-type wxShow() :: #wxShow{}. %% Callback event: {@link wxShowEvent}
--record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
--type wxWindowDestroyEventType() :: 'destroy'.
--type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
-
--record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
- pos :: {X::integer(), Y::integer()}}).
--type wxContextMenuEventType() :: 'context_menu'.
--type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
-
--record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
- active :: boolean()}).
--type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
--type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+-record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
+ position :: integer(),
+ key :: integer(),
+ modifiers :: integer(),
+ modificationType :: integer(),
+ text :: unicode:chardata(),
+ length :: integer(),
+ linesAdded :: integer(),
+ line :: integer(),
+ foldLevelNow :: integer(),
+ foldLevelPrev :: integer(),
+ margin :: integer(),
+ message :: integer(),
+ wParam :: integer(),
+ lParam :: integer(),
+ listType :: integer(),
+ x :: integer(),
+ y :: integer(),
+ dragText :: unicode:chardata(),
+ dragAllowMove :: boolean(),
+ dragResult :: wx:wx_enum()}).
+-type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
+-type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
--record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
- pos :: {X::integer(), Y::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxMoveEventType() :: 'move'.
--type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
+-record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
+ old_selection :: integer(),
+ selection :: integer(),
+ drag_source :: wxAuiNotebook:wxAuiNotebook()}).
+-type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
+-type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
-record(wxList,{type :: wxListEventType(), %% Callback event: {@link wxListEvent}
code :: integer(),
@@ -201,9 +194,9 @@
-type wxListEventType() :: 'command_list_begin_drag' | 'command_list_begin_rdrag' | 'command_list_begin_label_edit' | 'command_list_end_label_edit' | 'command_list_delete_item' | 'command_list_delete_all_items' | 'command_list_key_down' | 'command_list_insert_item' | 'command_list_col_click' | 'command_list_col_right_click' | 'command_list_col_begin_drag' | 'command_list_col_dragging' | 'command_list_col_end_drag' | 'command_list_item_selected' | 'command_list_item_deselected' | 'command_list_item_right_click' | 'command_list_item_middle_click' | 'command_list_item_activated' | 'command_list_item_focused' | 'command_list_cache_hint'.
-type wxList() :: #wxList{}. %% Callback event: {@link wxListEvent}
--record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
--type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
--type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+-record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
+-type wxUpdateUIEventType() :: 'update_ui'.
+-type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
-record(wxScrollWin,{type :: wxScrollWinEventType(), %% Callback event: {@link wxScrollWinEvent}
commandInt :: integer(),
@@ -211,92 +204,81 @@
-type wxScrollWinEventType() :: 'scrollwin_top' | 'scrollwin_bottom' | 'scrollwin_lineup' | 'scrollwin_linedown' | 'scrollwin_pageup' | 'scrollwin_pagedown' | 'scrollwin_thumbtrack' | 'scrollwin_thumbrelease'.
-type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent}
--record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
- iconized :: boolean()}).
--type wxIconizeEventType() :: 'iconize'.
--type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
+-record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
+ pos :: {X::integer(), Y::integer()},
+ zPosition :: integer(),
+ buttonChange :: integer(),
+ buttonState :: integer(),
+ joyStick :: integer()}).
+-type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
+-type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
--record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
--type wxUpdateUIEventType() :: 'update_ui'.
--type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
+-record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
+-type wxWindowCreateEventType() :: 'create'.
+-type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
--record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
+-record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
+-type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
+-type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
+
+-record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
x :: integer(),
y :: integer(),
- leftDown :: boolean(),
- middleDown :: boolean(),
- rightDown :: boolean(),
+ keyCode :: integer(),
controlDown :: boolean(),
shiftDown :: boolean(),
altDown :: boolean(),
metaDown :: boolean(),
- wheelRotation :: integer(),
- wheelDelta :: integer(),
- linesPerAction :: integer()}).
--type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
--type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
-
--record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
- item :: integer(),
- itemOld :: integer(),
- pointDrag :: {X::integer(), Y::integer()}}).
--type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
--type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
-
--record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
--type wxSysColourChangedEventType() :: 'sys_colour_changed'.
--type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
+ scanCode :: boolean(),
+ uniChar :: integer(),
+ rawCode :: integer(),
+ rawFlags :: integer()}).
+-type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
+-type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
--record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
- flags :: integer(),
- focus :: wxWindow:wxWindow()}).
--type wxNavigationKeyEventType() :: 'navigation_key'.
--type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
+-record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
+-type wxIdleEventType() :: 'idle'.
+-type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
-record(wxQueryNewPalette, {type :: wxQueryNewPaletteEventType()}). %% Callback event: {@link wxQueryNewPaletteEvent}
-type wxQueryNewPaletteEventType() :: 'query_new_palette'.
-type wxQueryNewPalette() :: #wxQueryNewPalette{}. %% Callback event: {@link wxQueryNewPaletteEvent}
--record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
--type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
--type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
-
-record(wxHtmlLink,{type :: wxHtmlLinkEventType(), %% Callback event: {@link wxHtmlLinkEvent}
linkInfo :: wx:wx_wxHtmlLinkInfo()}).
-type wxHtmlLinkEventType() :: 'command_html_link_clicked'.
-type wxHtmlLink() :: #wxHtmlLink{}. %% Callback event: {@link wxHtmlLinkEvent}
--record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
- x :: integer(),
- y :: integer(),
- keyCode :: integer(),
- controlDown :: boolean(),
- shiftDown :: boolean(),
- altDown :: boolean(),
- metaDown :: boolean(),
- scanCode :: boolean(),
- uniChar :: integer(),
- rawCode :: integer(),
- rawFlags :: integer()}).
--type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
--type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
+-record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
+-type wxInitDialogEventType() :: 'init_dialog'.
+-type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
--record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
--type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
--type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
+-record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
+-type wxMaximizeEventType() :: 'maximize'.
+-type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
--record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
- row :: integer(),
- col :: integer(),
- x :: integer(),
- y :: integer(),
- selecting :: boolean(),
- control :: boolean(),
- meta :: boolean(),
- shift :: boolean(),
- alt :: boolean()}).
--type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
--type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+-record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
+-type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
+-type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+
+-record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
+ item :: integer(),
+ itemOld :: integer(),
+ pointDrag :: {X::integer(), Y::integer()}}).
+-type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
+-type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
+
+-record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
+ dc :: wxDC:wxDC()}).
+-type wxEraseEventType() :: 'erase_background'.
+-type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+
+-record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
+ edge :: wx:wx_enum(),
+ dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
+ dragStatus :: wx:wx_enum()}).
+-type wxSashEventType() :: 'sash_dragged'.
+-type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
-record(wxCalendar,{type :: wxCalendarEventType(), %% Callback event: {@link wxCalendarEvent}
wday :: wx:wx_enum(),
@@ -304,42 +286,52 @@
-type wxCalendarEventType() :: 'calendar_sel_changed' | 'calendar_day_changed' | 'calendar_month_changed' | 'calendar_year_changed' | 'calendar_doubleclicked' | 'calendar_weekday_clicked'.
-type wxCalendar() :: #wxCalendar{}. %% Callback event: {@link wxCalendarEvent}
--record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
--type wxWindowCreateEventType() :: 'create'.
--type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
+-record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
+ x :: integer(),
+ y :: integer(),
+ leftDown :: boolean(),
+ middleDown :: boolean(),
+ rightDown :: boolean(),
+ controlDown :: boolean(),
+ shiftDown :: boolean(),
+ altDown :: boolean(),
+ metaDown :: boolean(),
+ wheelRotation :: integer(),
+ wheelDelta :: integer(),
+ linesPerAction :: integer()}).
+-type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
+-type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
--record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
- date :: wx:wx_datetime()}).
--type wxDateEventType() :: 'date_changed'.
--type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
+-record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
+ size :: {W::integer(), H::integer()},
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxSizeEventType() :: 'size'.
+-type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
--record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
- manager :: wxAuiManager:wxAuiManager(),
- pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
- button :: integer(),
- veto_flag :: boolean(),
- canveto_flag :: boolean(),
- dc :: wxDC:wxDC()}).
--type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
--type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
+-record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
+-type wxSysColourChangedEventType() :: 'sys_colour_changed'.
+-type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
--record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
+-record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
+-type wxDisplayChangedEventType() :: 'display_changed'.
+-type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
+
+-record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
pos :: {X::integer(), Y::integer()},
- zPosition :: integer(),
- buttonChange :: integer(),
- buttonState :: integer(),
- joyStick :: integer()}).
--type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
--type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxMoveEventType() :: 'move'.
+-type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
--record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
--type wxPaintEventType() :: 'paint'.
--type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+-record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
+ flags :: integer(),
+ focus :: wxWindow:wxWindow()}).
+-type wxNavigationKeyEventType() :: 'navigation_key'.
+-type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
--record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
- dc :: wxDC:wxDC()}).
--type wxEraseEventType() :: 'erase_background'.
--type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+-record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
+ path :: unicode:chardata()}).
+-type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
+-type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-record(wxSetCursor,{type :: wxSetCursorEventType(), %% Callback event: {@link wxSetCursorEvent}
x :: integer(),
@@ -348,6 +340,14 @@
-type wxSetCursorEventType() :: 'set_cursor'.
-type wxSetCursor() :: #wxSetCursor{}. %% Callback event: {@link wxSetCursorEvent}
+-record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
+-type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
+-type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
+
+-record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
+-type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
+-type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+
-type event() :: wxActivate() | wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClipboardText() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxInitDialog() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMouseCaptureLost() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
-type wxEventType() :: wxActivateEventType() | wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxClipboardTextEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxInitDialogEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseCaptureLostEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl
index d1a063d900..851686062a 100644
--- a/lib/wx/src/gen/wxListCtrl.erl
+++ b/lib/wx/src/gen/wxListCtrl.erl
@@ -94,31 +94,34 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxListCtrl() :: wx:wx_object().
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(?wxListCtrl_new_0, <<>>).
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -185,26 +188,27 @@ clearAll(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/src/gen/wxXmlResource.erl b/lib/wx/src/gen/wxXmlResource.erl
index ae02c74751..aa65b8b04e 100644
--- a/lib/wx/src/gen/wxXmlResource.erl
+++ b/lib/wx/src/gen/wxXmlResource.erl
@@ -334,8 +334,6 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
<<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>).
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -345,6 +343,10 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ?wxXmlResource_xrcctrl
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 40ee308358..40170b6eb1 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -55,7 +55,7 @@
%% When stop is returned in one of the functions above with Reason =
%% normal | shutdown | Term, terminate(State) is called. It lets the
%% user module clean up, it is always called when server terminates or
-%% when wxObject() in the driver is deleted. If the Parent process
+%% when wx_object() in the driver is deleted. If the Parent process
%% terminates the Module:terminate/2 function is called. <br/>
%% terminate(Reason, State)
%%
@@ -171,58 +171,59 @@
%% -----------------------------------------------------------------
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
+
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
+
gen_response({ok, Pid}) ->
receive {ack, Pid, Ref = #wx_ref{}} -> Ref end;
gen_response(Reply) ->
Reply.
-%% @spec (Ref::wxObject()|atom()|pid()) -> ok
%% @doc Stops a generic wx_object server with reason 'normal'.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the process does not exist, an
%% exception is raised.
+-spec stop(Obj) -> ok when
+ Obj::wx:wx_object()|atom()|pid().
stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
try
gen:stop(Pid)
@@ -236,11 +237,14 @@ stop(Name) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Reason::term(), Timeout::timeout()) -> ok
%% @doc Stops a generic wx_object server with the given Reason.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the call times out, or if the process
%% does not exist, an exception is raised.
+-spec stop(Obj, Reason, Timeout) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Reason::term(),
+ Timeout::timeout().
stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
try
gen:stop(Pid, Reason, Timeout)
@@ -254,12 +258,14 @@ stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> term()
%% @doc Make a call to a wx_object server.
%% The call waits until it gets a result.
%% Invokes handle_call(Request, From, State) in the server
+-spec call(Obj, Request) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) ->
- try
+ try
{ok,Res} = gen:call(Pid, '$gen_call', Request, infinity),
Res
catch _:Reason ->
@@ -272,10 +278,13 @@ call(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
catch _:Reason ->
erlang:error({Reason, {?MODULE, call, [Name, Request]}})
end.
-
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term(), Timeout::integer()) -> term()
+
%% @doc Make a call to a wx_object server with a timeout.
%% Invokes handle_call(Request, From, State) in server
+-spec call(Obj, Request, Timeout) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term(),
+ Timeout::integer().
call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) ->
try
{ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout),
@@ -291,10 +300,11 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> ok
%% @doc Make a cast to a wx_object server.
%% Invokes handle_cast(Request, State) in the server
-
+-spec cast(Obj, Request) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) ->
Pid ! {'$gen_cast',Request},
ok;
@@ -302,21 +312,23 @@ cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
Name ! {'$gen_cast',Request},
ok.
-%% @spec (Ref::wxObject()) -> pid()
%% @doc Get the pid of the object handle.
+-spec get_pid(Obj) -> pid() when
+ Obj::wx:wx_object()|atom()|pid().
get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
Pid.
-%% @spec (Ref::wxObject(), pid()) -> wxObject()
%% @doc Sets the controlling process of the object handle.
+-spec set_pid(Obj, pid()) -> wx:wx_object() when
+ Obj::wx:wx_object()|atom()|pid().
set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
R#wx_ref{state=Pid}.
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
-%% @spec (From::tuple(), Reply::term()) -> pid()
%% @doc Get the pid of the object handle.
+-spec reply({pid(), Tag::term()}, Reply::term()) -> pid().
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.