1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2015-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-module(beam_utils_SUITE).
21
22-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24	 apply_fun/1,apply_mf/1,bs_init/1,bs_save/1,
25	 is_not_killed/1,is_not_used_at/1,
26	 select/1,y_catch/1,otp_8949_b/1,liveopt/1,coverage/1,
27         y_registers/1,user_predef/1,scan_f/1,cafu/1,
28         receive_label/1,read_size_file_version/1,not_used/1,
29         is_used_fr/1,unsafe_is_function/1]).
30-export([id/1]).
31
32suite() -> [{ct_hooks,[ts_install_cth]}].
33
34all() ->
35    [{group,p}].
36
37groups() ->
38    [{p,[parallel],
39      [apply_fun,
40       apply_mf,
41       bs_init,
42       bs_save,
43       is_not_killed,
44       is_not_used_at,
45       select,
46       y_catch,
47       otp_8949_b,
48       liveopt,
49       coverage,
50       y_registers,
51       user_predef,
52       scan_f,
53       cafu,
54       read_size_file_version,
55       not_used,
56       is_used_fr,
57       unsafe_is_function
58      ]}].
59
60init_per_suite(Config) ->
61    test_lib:recompile(?MODULE),
62    Config.
63
64end_per_suite(_Config) ->
65    ok.
66
67init_per_group(_GroupName, Config) ->
68    Config.
69
70end_per_group(_GroupName, Config) ->
71    Config.
72
73apply_fun(_Config) ->
74    3 = do_apply_fun(false, false),
75    3 = do_apply_fun(false, true),
76    3 = do_apply_fun(true, false),
77    2 = do_apply_fun(true, true),
78    ok.
79
80do_apply_fun(X, Y) ->
81    F = fun(I) -> I+1 end,
82    Arg = case X andalso id(Y) of
83	      true -> 1;
84	      false -> 2
85	  end,
86    F(Arg).
87
88apply_mf(_Config) ->
89    ok = do_apply_mf_used({a,b}, ?MODULE, id),
90    error = do_apply_mf_used([a], ?MODULE, id),
91    {'EXIT',{{case_clause,{[],b}},_}} = (catch do_apply_mf_used({[],b}, ?MODULE, id)),
92
93    error = do_apply_mf_killed({error,[a]}, ?MODULE, id),
94    ok = do_apply_mf_killed([b], ?MODULE, id),
95    {'EXIT',{{case_clause,{a,[b]}},_}} = (catch do_apply_mf_killed({a,[b]}, ?MODULE, id)),
96    {'EXIT',{{case_clause,{error,[]}},_}} = (catch do_apply_mf_killed({error,[]}, ?MODULE, id)),
97
98    ok.
99
100do_apply_mf_used(Arg, Mod, Func) ->
101    Res = case id(Arg) of
102	      {Decoded,_} when Decoded =/= [] ->
103		  ok;
104	      List when is_list(List) ->
105		  error
106	  end,
107    Mod:Func(Res).
108
109do_apply_mf_killed(Arg, Mod, Func) ->
110    Res = case id(Arg) of
111	      {Tag,Decoded} when Decoded =/= [], Tag =:= error ->
112		  error;
113	      List when is_list(List) ->
114		  ok
115	  end,
116    Mod:Func(Res).
117
118bs_init(_Config) ->
119    <<7>> = do_bs_init_1([?MODULE], 7),
120    error = do_bs_init_1([?MODULE], 0.0),
121    error = do_bs_init_1([?MODULE], -43),
122    error = do_bs_init_1([?MODULE], 42),
123
124    <<>> = do_bs_init_2([]),
125    <<0:32,((1 bsl 32)-1):32>> = do_bs_init_2([0,(1 bsl 32)-1]),
126    {'EXIT',{badarg,_}} = (catch do_bs_init_2([0.5])),
127    {'EXIT',{badarg,_}} = (catch do_bs_init_2([-1])),
128    {'EXIT',{badarg,_}} = (catch do_bs_init_2([1 bsl 32])),
129
130    <<>> = do_bs_init_3({tag,0}, 0, 0),
131    <<0>> = do_bs_init_3({tag,0}, 2, 1),
132
133    <<"_build/shared">> = do_bs_init_4([], false),
134    <<"abc/shared">> = do_bs_init_4(<<"abc">>, false),
135    <<"foo/foo">> = do_bs_init_4(<<"foo">>, true),
136    error = do_bs_init_4([], not_boolean),
137
138    Id = 17575,
139    Domain = -8798798,
140    [<<10,1:16,Id:16/signed>>,<<8,2:16,Domain:32/signed>>] =
141        do_bs_init_5(#{tag=>value,id=>Id,domain=>Domain}),
142    {'EXIT',{{required,id},[_|_]}} =
143        (catch do_bs_init_5(#{tag=>value,id=>nil,domain=>Domain})),
144    {'EXIT',{{required,domain},[_|_]}} =
145        (catch do_bs_init_5(#{tag=>value,id=>Id,domain=>nil})),
146
147    ok.
148
149do_bs_init_1([?MODULE], Sz) ->
150    if
151	is_integer(Sz), Sz >= -42, Sz < 42 ->
152	    id(<<Sz:8>>);
153	true ->
154	    error
155    end.
156
157do_bs_init_2(SigNos) ->
158    << <<SigNo:32>> ||
159	SigNo <- SigNos,
160	(is_integer(SigNo) andalso SigNo >= 0 andalso SigNo < (1 bsl 32)) orelse
161	    erlang:error(badarg)
162    >>.
163
164do_bs_init_3({tag,Pos}, Offset, Len) ->
165    N0 = Offset - Pos,
166    N = if N0 > Len -> Len;
167           true -> N0
168        end,
169    <<0:N/unit:8>>.
170
171do_bs_init_4(Arg1, Arg2) ->
172    Build =
173        case id(Arg1) of
174            X when X =:= [] orelse X =:= false -> <<"_build">>;
175            X -> X
176        end,
177    case id(Arg2) of
178        true ->
179            id(<<case Build of
180                     Rewrite when is_binary(Rewrite) ->
181                         Rewrite;
182                     Rewrite ->
183                         id(Rewrite)
184                 end/binary,
185                 "/",
186                 case id(<<"foo">>) of
187                     Rewrite when is_binary(Rewrite) ->
188                         Rewrite;
189                     Rewrite ->
190                         id(Rewrite)
191                 end/binary>>);
192        false ->
193            id(<<case Build of
194                     Rewrite when is_binary(Rewrite) ->
195                         Rewrite;
196                     Rewrite ->
197                         id(Rewrite)
198                 end/binary,
199                 "/shared">>);
200        _Other ->
201            error
202    end.
203
204do_bs_init_5(#{tag := value, id := Id, domain := Domain}) ->
205    [case Id of
206         nil ->
207             error(id({required, id}));
208         _ ->
209             <<10, 1:16/signed, Id:16/signed>>
210     end,
211     case Domain of
212         nil ->
213             error(id({required, domain}));
214         _ ->
215             <<8, 2:16/signed, Domain:32/signed>>
216     end].
217
218bs_save(_Config) ->
219    {a,30,<<>>} = do_bs_save(<<1:1,30:5>>),
220    {b,127,<<>>} = do_bs_save(<<1:1,31:5,0:1,127:7>>),
221    {c,127,<<>>} = do_bs_save(<<1:1,31:5,1:1,127:7>>),
222    {c,127,<<>>} = do_bs_save(<<0:1,31:5,1:1,127:7>>),
223    {d,1024,<<>>} = do_bs_save(<<0:1,31:5>>),
224    ok.
225
226do_bs_save(<<_:1, Tag:5, T/binary>>) when Tag < 31 ->
227    {a,Tag,T};
228do_bs_save(<<1:1, 31:5, 0:1, Tag:7, T/binary>>)  ->
229    {b,Tag,T};
230do_bs_save(<<_:1, 31:5, 1:1, Tag:7, T/binary>>) ->
231    {c,Tag,T};
232do_bs_save(<<_:1, 31:5, T/binary>>) ->
233    {d,1024,T}.
234
235is_not_killed(_Config) ->
236    {Pid,Ref} = spawn_monitor(fun() -> exit(banan) end),
237    receive
238	{'DOWN', Ref, process, Pid, banan} ->
239	    ok
240    end,
241    receive after 0 -> ok end.
242
243is_not_used_at(_Config) ->
244    {a,b} = do_is_not_used_at(a, [{a,b}]),
245    {a,b} = do_is_not_used_at(a, [x,{a,b}]),
246    {a,b} = do_is_not_used_at(a, [{x,y},{a,b}]),
247    none = do_is_not_used_at(z, [{a,b}]),
248    none = do_is_not_used_at(a, [x]),
249    none = do_is_not_used_at(a, [{x,y}]),
250    ok.
251
252do_is_not_used_at(Key, [P|Ps]) ->
253    if
254	tuple_size(P) >= 1, element(1, P) =:= Key ->
255	    P;
256	true ->
257	    do_is_not_used_at(Key, Ps)
258    end;
259do_is_not_used_at(_Key, []) -> none.
260
261-record(select, {fixed=false}).
262
263select(_Config) ->
264    a = do_select(#select{}, 0, 0),
265    b = do_select(#select{}, 0, 1),
266    c = do_select(#select{fixed=true}, 0, 0),
267    c = do_select(#select{fixed=true}, 0, 1),
268    ok.
269
270do_select(Head, OldSize, BSize) ->
271    Overwrite0 =
272	if
273	    OldSize =:= BSize -> same;
274	    true -> true
275	end,
276    Overwrite =
277	if
278	    Head#select.fixed =/= false ->
279		false;
280	    true ->
281		Overwrite0
282	end,
283    if
284	Overwrite =:= same ->
285	    a;
286	Overwrite ->
287	    b;
288	true ->
289	    c
290    end.
291
292y_catch(_Config) ->
293    ok = try
294	     do_y_catch(<<"<?xmlX">>, {state}),
295	     failed
296	 catch
297	     throw:{<<"<?xmlX">>,{state}} ->
298		 ok
299	 end.
300
301do_y_catch(<<"<?xml",Rest0/binary>> = Bytes, State0) ->
302    {Rest1,State1} =
303	case do_y_catch_1(Rest0, State0) of
304	    false ->
305		{Bytes,State0};
306	    true ->
307		{_XmlAttributes, R, S} = do_y_catch_2(Rest0),
308		{R,S}
309	end,
310    case catch id({Rest1,State1}) of
311	Other ->
312	    throw(Other)
313    end.
314
315do_y_catch_1(<<_,_/binary>>, _) ->
316    false.
317
318do_y_catch_2(_) -> {a,b,c}.
319
320otp_8949_b(_Config) ->
321    self() ! something,
322    value = otp_8949_b([], false),
323    {'EXIT',_} = (catch otp_8949_b([], true)),
324    ok.
325
326%% Would cause an endless loop in beam_utils.
327otp_8949_b(A, B) ->
328    Var = id(value),
329    if
330	A == [], B == false ->
331	    ok
332    end,
333    receive
334        something ->
335	    id(Var)
336    end.
337
338-record(alarmInfo, {type,cause,origin}).
339
340liveopt(_Config) ->
341    F = liveopt_fun(42, pebkac, user),
342    void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}),
343
344
345    A = {#alarmInfo{cause = {abc, def}}, ghi},
346    A = liveopt_guard_bif(A),
347
348    B = {#alarmInfo{cause = {abc}}, def},
349    {#alarmInfo{cause = {{abc}}}, def} = liveopt_guard_bif(B),
350
351    ok.
352
353liveopt_fun(Peer, Cause, Origin) ->
354    fun(PeerNo, AlarmInfo)
355	  when PeerNo == Peer andalso
356	       AlarmInfo == #alarmInfo{type=sctp,
357				       cause=Cause,
358				       origin=Origin} ->
359	    void
360    end.
361
362liveopt_guard_bif({#alarmInfo{cause=F}=R, X}=A) ->
363    %% ERIERL-48
364    if
365        is_tuple(F), tuple_size(F) == 2 -> A;
366        true ->
367            R2 = R#alarmInfo{cause={F}},
368            {R2,X}
369    end.
370
371%% Thanks to QuickCheck.
372coverage(_Config) ->
373    42+7 = merchant([[],7,false]),
374
375    {'EXIT',{{try_clause,0},_}} = (catch resulting([0], stone)),
376    0.0 = resulting([true], stone),
377
378    {'EXIT',{if_clause,_}} = (catch clinic(false)),
379    {'EXIT',{{try_clause,"trials"},_}} = (catch clinic(true)),
380
381    {'EXIT',{function_clause,_}} = (catch town(overall, {{abc},alcohol})),
382
383    self() ! junk_message,
384    {"url",#{true:="url"}} = appointment(#{"resolution" => "url"}),
385
386    ok.
387
388%% Cover check_liveness/3.
389merchant([Merchant, Laws, Electric]) ->
390    id(42),
391    oklahoma([[] || 0 <- Merchant],
392	     if true; Electric -> Laws end) + 42.
393oklahoma([], Int) -> Int.
394
395town(overall, {{If}, Healing = alcohol})
396  when Healing#{[] => Healing}; include ->
397    [If || Healing <- awareness].
398
399%% Cover is_reg_used_at/3.
400resulting([Conservation], stone) ->
401    try 0 of
402	Conservation when Conservation -> Conservation;
403	_ when Conservation; 0 -> 0.0
404    after
405	Conservation
406    end.
407
408%% Cover is_reg_used_at_1/3.
409clinic(Damage) ->
410    if
411      Damage ->
412	  try "trials" of Damage when Damage -> Damage catch true -> [] end
413    end,
414    carefully.
415
416y_registers(_Config) ->
417    {'EXIT',{{badfun,0},_}} = (catch economic(0.0, jim)),
418    {'EXIT',{{badmatch,apartments},_}} = (catch louisiana()),
419    {a,b} = (boxes(true))({a,b}),
420    {'EXIT',{{case_clause,webmaster},_}} = (catch yellow(true)),
421    ok.
422
423economic(0.0 = Serves, Existence) ->
424    case Serves of
425	Serves -> 0
426    end,
427    Existence = jim,
428    0(),
429    Serves,
430    Existence.
431
432louisiana() ->
433    {catch necessarily,
434     try
435	 [] == reg,
436	 true = apartments
437     catch [] -> barbara
438     end}.
439
440boxes(Call) ->
441    case Call of
442	Call -> approval
443    end,
444    Call,
445    fun id/1.
446
447yellow(Hill) ->
448    case webmaster of
449	station -> eyes; Hill ->
450	    "under"
451    end,
452    Hill,
453    id(42).
454
455do(A, B) -> {A,B}.
456appointment(#{"resolution" := Url}) ->
457    do(receive _ -> Url end, #{true => Url}).
458
459%% From epp.erl.
460user_predef(_Config) ->
461    #{key:="value"} = user_predef({key,"value"}, #{}),
462    #{key:="value"} = user_predef({key,"value"}, #{key=>defined}),
463    error = user_predef({key,"value"}, #{key=>[defined]}),
464    ok.
465
466user_predef({M,Val}, Ms) ->
467    case Ms of
468	#{M:=Defs} when is_list(Defs) ->
469	    error;
470	_ ->
471	    Ms#{M=>Val}
472    end.
473
474%% From disk_log_1.erl.
475scan_f(_Config) ->
476    {1,<<>>,[]} = scan_f(<<1:32>>, 1, []),
477    {1,<<>>,[<<156>>]} = scan_f(<<1:32,156,1:32>>, 1, []),
478    ok.
479
480scan_f(<<Size:32,Tail/binary>>, FSz, Acc) when Size =< FSz ->
481    case Tail of
482        <<BinTerm:Size/binary,Tail2/binary>> ->
483            scan_f(Tail2, FSz, [BinTerm | Acc]);
484        _ ->
485            {Size,Tail,Acc}
486    end.
487
488%% From file_io_server.erl.
489cafu(_Config) ->
490    error = cafu(<<42:32>>, -1, 0, {utf32,big}),
491    error = cafu(<<42:32>>, 10, 0, {utf32,big}),
492    error = cafu(<<42:32>>, -1, 0, {utf32,little}),
493    ok.
494
495cafu(<<_/big-utf32,Rest/binary>>, N, Count, {utf32,big}) when N < 0 ->
496    cafu(Rest, -1, Count+1, {utf32,big});
497cafu(<<_/big-utf32,Rest/binary>>, N, Count, {utf32,big}) ->
498    cafu(Rest, N-1, Count+1, {utf32,big});
499cafu(<<_/little-utf32,Rest/binary>>, N, Count, {utf32,little}) when N < 0 ->
500    cafu(Rest, -1, Count+1, {utf32,little});
501cafu(_, _, _, _) ->
502    error.
503
504-record(rec_label, {bool}).
505
506receive_label(_Config) ->
507    Pid = spawn_link(fun() -> do_receive_label(#rec_label{bool=true}) end),
508    Msg = {a,b,c},
509    Pid ! {self(),Msg},
510    receive
511        {ok,Msg} ->
512            unlink(Pid),
513            exit(Pid, die),
514            ok
515    end.
516
517do_receive_label(Rec) ->
518    receive
519        {From,Message} when Rec#rec_label.bool ->
520            From ! {ok,Message},
521            do_receive_label(Rec)
522    end.
523
524read_size_file_version(_Config) ->
525    ok = do_read_size_file_version({ok,<<42>>}),
526    {ok,7777} = do_read_size_file_version({ok,<<7777:32>>}),
527    ok.
528
529do_read_size_file_version(E) ->
530    case E of
531	{ok,<<Version>>} when Version =:= 42 ->
532            ok;
533	{ok,<<MaxFiles:32>>} ->
534            {ok,MaxFiles}
535    end.
536
537-record(s, { a, b }).
538-record(k, { v }).
539
540not_used(_Config) ->
541    [] = not_used_p(any, #s{b=true}, #k{}, ignored),
542    #k{v=42} = not_used_p(any, #s{b=false}, #k{v=42}, ignored),
543    #k{v=42} = not_used_p(any, #s{b=bad}, #k{v=42}, ignored),
544    ok.
545
546not_used_p(_C, S, K, L) when is_record(K, k) ->
547    if ((S#s.b) and
548         (S#s.b)) ->
549            [];
550       true ->
551            id(L),
552            id(K#k.v),
553            id(K)
554    end.
555
556is_used_fr(_Config) ->
557    1 = is_used_fr(self(), self()),
558    1 = is_used_fr(self(), other),
559    receive 1 -> ok end,
560    receive 1 -> ok end,
561    receive 1 -> ok end,
562    receive 1 -> ok end,
563    ok.
564
565is_used_fr(X, Y) ->
566    %% beam_utils:is_used({fr,R}, Code) would crash.
567    _ = 0 / (X ! 1),
568    _ = case Y of
569            X -> ok;
570            _ -> error
571        end,
572    X ! 1.
573
574%% ERL-778.
575unsafe_is_function(_Config) ->
576    {undefined,any} = unsafe_is_function(undefined, any),
577    {ok,any} = unsafe_is_function(fun() -> ok end, any),
578    {'EXIT',{{case_clause,_},_}} = (catch unsafe_is_function(fun(_) -> ok end, any)),
579    ok.
580
581unsafe_is_function(F, M) ->
582    %% There would be an internal consistency failure:
583    %%   Instruction: {bif,is_function,{f,0},[{x,0},{integer,0}],{x,2}}
584    %%   Error:       {uninitialized_reg,{y,0}}:
585
586    NewValue = case is_function(F, 0) of
587                true -> F();
588                false when F =:= undefined -> undefined
589            end,
590    {NewValue,M}.
591
592
593%% The identity function.
594id(I) -> I.
595