1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%% Purpose : Compiles various modules with tough code
21
22-module(receive_SUITE).
23
24-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
25	 init_per_group/2,end_per_group/2,
26	 init_per_testcase/2,end_per_testcase/2,
27	 export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1,
28	 wait/1,recv_in_try/1,double_recv/1]).
29
30-include_lib("common_test/include/ct.hrl").
31
32init_per_testcase(_Case, Config) ->
33    Config.
34
35end_per_testcase(_Case, _Config) ->
36    ok.
37
38suite() ->
39    [{ct_hooks,[ts_install_cth]},
40     {timetrap,{minutes,2}}].
41
42all() ->
43    [{group,p}].
44
45groups() ->
46    [{p,test_lib:parallel(),
47      [recv,coverage,otp_7980,ref_opt,export,wait,
48       recv_in_try,double_recv]}].
49
50
51init_per_suite(Config) ->
52    test_lib:recompile(?MODULE),
53    Config.
54
55end_per_suite(_Config) ->
56    ok.
57
58init_per_group(_GroupName, Config) ->
59    Config.
60
61end_per_group(_GroupName, Config) ->
62    Config.
63
64-record(state, {ena = true}).
65
66recv(Config) when is_list(Config) ->
67    Pid = spawn_link(fun() -> loop(#state{}) end),
68    Self = self(),
69    Pid ! {Self,test},
70    receive
71	{ok,test} -> ok;
72	{error,Other} ->
73	    io:format("Got unpexected ~p", [Other]),
74	    ct:fail(unexpected)
75    after 10000 ->
76	    ct:fail(no_answer)
77    end,
78    receive
79	X ->
80	    io:format("Unexpected extra message: ~p", [X]),
81	    ct:fail(unexpected)
82    after 10 ->
83	    ok
84    end,
85    ok.
86
87loop(S) ->
88    receive
89	_ when S#state.ena == false ->
90	    loop(S);
91	{P,test} ->
92	    P ! {ok,test},
93	    loop(S);
94	_X ->
95	    loop(S)
96    end.
97
98coverage(Config) when is_list(Config) ->
99    do_link(self()),
100    do_unlink(self()),
101    do_monitor_node(node(), true),
102    do_monitor_node(node(), false),
103    do_group_leader(group_leader(), self()),
104    id(node(self())),
105
106    erlang:'!'(self(), {a,10}),
107    self() ! {b,20},
108    [{a,10},{b,20}] = receive_all(),
109    self() ! {c,42},
110    receive
111	{c,42} ->
112	    ok
113    after infinity ->
114	    exit(cant_happen)
115    end,
116
117    self() ! 17,
118    self() ! 19,
119    59 = tuple_to_values(infinity, x),
120    61 = tuple_to_values(999999, x),
121    0 = tuple_to_values(1, x),
122
123    {'EXIT',{{badmap,[]},_}} = (catch monitor_plus_badmap(self())),
124
125    ok.
126
127monitor_plus_badmap(Pid) ->
128    monitor(process, Pid) + []#{}.
129
130receive_all() ->
131    receive
132	Any ->
133	    [Any|receive_all()]
134    after 0 ->
135	    []
136    end.
137
138do_monitor_node(Node, Bool) ->
139    monitor_node(Node, Bool).
140
141do_link(Pid) ->
142    link(Pid).
143
144do_unlink(Pid) ->
145    unlink(Pid).
146
147do_group_leader(Leader, Pid) ->
148    group_leader(Leader, Pid).
149
150
151%% cover sys_core_fold:tuple_to_values/2
152tuple_to_values(infinity, X) ->
153    {A,B} = case X of
154		x ->
155		    receive
156			Any ->
157			    {42,Any}
158		    end
159	    end,
160    A+B;
161tuple_to_values(Timeout, X) ->
162    {A,B} = case X of
163		x ->
164		    receive
165			Any ->
166			    {42,Any}
167		    after Timeout ->
168			    {0,0}
169		    end
170	    end,
171    A+B.
172
173%% OTP-7980. Thanks to Vincent de Phily. The following code would
174%% be inccorrectly optimized by beam_jump.
175
176otp_7980(Config) when is_list(Config) ->
177    7 = otp_7980_add_clients(10),
178    ok.
179
180otp_7980_add_clients(Count) ->
181    Timeout = 42,
182    lists:foldl(fun(_, N) ->
183			case N of
184                           1 -> ok;
185			    _ -> receive after Timeout -> ok end
186			end,
187			N - 1
188		end, Count, [1,2,3]).
189
190ref_opt(Config) when is_list(Config) ->
191    case ?MODULE of
192	receive_SUITE -> ref_opt_1(Config);
193	_ -> {skip,"Enough to run this case once."}
194    end.
195
196ref_opt_1(Config) ->
197    DataDir = proplists:get_value(data_dir, Config),
198    PrivDir = proplists:get_value(priv_dir, Config),
199    Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])),
200    test_lib:p_run(fun(Src) ->
201			   do_ref_opt(Src, PrivDir)
202		   end, Sources),
203    cover_recv_instructions(),
204    ok.
205
206do_ref_opt(Source, PrivDir) ->
207    try
208	Ext = filename:extension(Source),
209	{ok,Mod} = compile:file(Source, [report_errors,report_warnings,
210					 {outdir,PrivDir}] ++
211					[from_asm || Ext =:= ".S" ]),
212	Base = filename:rootname(filename:basename(Source), Ext),
213	code:purge(list_to_atom(Base)),
214	BeamFile = filename:join(PrivDir, Base),
215	code:load_abs(BeamFile),
216	ok = Mod:Mod(),
217	{beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile),
218	case Base of
219	    "no_"++_ ->
220		[] = collect_recv_opt_instrs(Code);
221	    "yes_"++_ ->
222		[{recv_mark,{f,L}},{recv_set,{f,L}}] =
223		    collect_recv_opt_instrs(Code)
224	end,
225	ok
226    catch Class:Error:Stk ->
227	    io:format("~s: ~p ~p\n~p\n", [Source,Class,Error,Stk]),
228	    error
229    end.
230
231collect_recv_opt_instrs(Code) ->
232    L = [ [I || I <- Is,
233		begin
234		    case I of
235			{recv_mark,{f,_}} -> true;
236			{recv_set,{f,_}} -> true;
237			_ -> false
238		    end
239		end] || {function,_,_,_,Is} <- Code],
240    lists:append(L).
241
242cover_recv_instructions() ->
243    %% We want to cover the handling of recv_mark and recv_set in beam_utils.
244    %% Since those instructions are introduced in a late optimization pass,
245    %% beam_utils:live_opt() will not see them unless the compilation is
246    %% started from a .S file. The compile_SUITE:asm/1 test case will
247    %% compile all test suite files to .S and then run them through the
248    %% compiler again.
249    %%
250    %% Here will we will ensure that this modules contains recv_mark
251    %% and recv_set instructions.
252    Pid = spawn_link(fun() ->
253			     receive {Parent,Ref} ->
254				     Parent ! Ref
255			     end
256		     end),
257    Ref = make_ref(),
258    Pid ! {self(),Ref},
259    receive
260	Ref -> ok
261    end.
262
263export(Config) when is_list(Config) ->
264    Ref = make_ref(),
265    self() ! {result,Ref,42},
266    42 = export_1(Ref),
267    {error,timeout} = export_1(Ref),
268
269    self() ! {result,Ref},
270    {ok,Ref} = export_2(),
271
272    ok.
273
274export_1(Reference) ->
275    id(Reference),
276    receive
277	{result,Reference,Result} ->
278	    Result
279    after 1 ->
280	    Result = {error,timeout}
281    end,
282    %% Result ({x,1}) is used, but not the return value ({x,0})
283    %% of the receive. Used to be incorrectly optimized
284    %% by beam_block.
285    id({build,self()}),
286    Result.
287
288export_2() ->
289    receive {result,Result} -> ok end,
290    {ok,Result}.
291
292wait(Config) when is_list(Config) ->
293    self() ! <<42>>,
294    <<42>> = wait_1(r, 1, 2),
295    {1,2,3} = wait_1(1, 2, 3),
296    {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
297    ok.
298
299wait_1(r, _, _) ->
300    receive
301	B when byte_size(B) > 0 ->
302	    B
303    end;
304%% beam_utils would wrongly assume that wait/1 could fall through
305%% to the next clause.
306wait_1(A, B, C) ->
307    {A,B,C}.
308
309recv_in_try(_Config) ->
310    self() ! {ok,fh}, {ok,fh} = recv_in_try(infinity, native),
311    self() ! {ok,ignored}, {ok,42} = recv_in_try(infinity, plain),
312    self() ! {error,ignored}, nok = recv_in_try(infinity, plain),
313    timeout = recv_in_try(1, plain),
314    ok.
315
316recv_in_try(Timeout, Format) ->
317    try
318	receive
319	    {Status,History} ->
320                %% {test,is_tuple,{f,148},[{x,0}]}.
321                %% {test,test_arity,{f,148},[{x,0},2]}.
322                %% {get_tuple_element,{x,0},0,{y,1}}.  %y1 is fragile.
323                %%
324                %% %% Here the fragility of y1 would be be progated to
325                %% %% the 'catch' below. Incorrect, since get_tuple_element
326                %% %% can't fail.
327                %% {get_tuple_element,{x,0},1,{x,2}}.
328                %%
329                %% remove_message.                     %y1 fragility cleared.
330		FH = case Format of
331			native ->
332                             id(History);
333			plain ->
334                             id(42)
335		    end,
336		case Status of
337		    ok ->
338			{ok,FH};
339		    error ->
340			nok
341		end
342	after Timeout ->
343		timeout
344	end
345    catch
346        %% The fragility of y1 incorrectly propagated to here.
347        %% beam_validator would complain.
348	throw:{error,Reason} ->
349	    {nok,Reason}
350    end.
351
352%% ERL-703. The compiler would crash because beam_utils:anno_defs/1
353%% failed to take into account that code after loop_rec_end is
354%% unreachable.
355
356double_recv(_Config) ->
357    self() ! {more,{a,term}},
358    ok = do_double_recv({more,{a,term}}, any),
359    self() ! message,
360    ok = do_double_recv(whatever, message),
361
362    error = do_double_recv({more,42}, whatever),
363    error = do_double_recv(whatever, whatever),
364    ok.
365
366do_double_recv({more, Rest}, _Msg) ->
367    receive
368        {more, Rest} ->
369            ok
370    after 0 ->
371            error
372    end;
373do_double_recv(_, Msg) ->
374    receive
375        Msg ->
376            ok
377    after 0 ->
378            error
379    end.
380
381id(I) -> I.
382