1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2006-2020. 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(misc_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	 init_per_testcase/2,end_per_testcase/2,
25	 tobias/1,empty_string/1,md5/1,silly_coverage/1,
26	 confused_literals/1,integer_encoding/0,integer_encoding/1,
27	 override_bif/1]).
28
29-include_lib("common_test/include/ct.hrl").
30
31%% For the override_bif testcase.
32%% NB, no other testcases in this testsuite can use these without erlang:prefix!
33-compile({no_auto_import,[abs/1]}).
34-compile({no_auto_import,[binary_part/3]}).
35-compile({no_auto_import,[binary_part/2]}).
36-import(test_lib,[binary_part/2]).
37
38%% This should do no harm (except for fun byte_size/1 which does not, by design, work with import
39-compile({no_auto_import,[byte_size/1]}).
40-import(erlang,[byte_size/1]).
41
42%% Cover the code for callback handling.
43-callback must_define_this_one() -> 'ok'.
44-callback do_something_strange(atom()) -> 'ok'.
45-optional_callbacks([do_something_strange/1]).
46-optional_callbacks([ignore_me]).		%Invalid; ignored.
47
48%% Include an opaque declaration to cover the stripping of
49%% opaque types from attributes in v3_kernel.
50-opaque misc_SUITE_test_cases() :: [atom()].
51
52init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
53    Config.
54
55end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
56    ok.
57
58suite() ->
59    [{ct_hooks,[ts_install_cth]},
60     {timetrap,{minutes,10}}].
61
62-spec all() -> misc_SUITE_test_cases().
63all() ->
64    slow_group() ++ [{group,p}].
65
66groups() ->
67    [{p,[parallel],
68      [tobias,empty_string,silly_coverage,
69       confused_literals,override_bif]},
70     {slow,[parallel],[integer_encoding,md5]}].
71
72init_per_suite(Config) ->
73    test_lib:recompile(?MODULE),
74    Config.
75
76end_per_suite(_Config) ->
77    ok.
78
79init_per_group(_GroupName, Config) ->
80    Config.
81
82end_per_group(_GroupName, Config) ->
83    Config.
84
85slow_group() ->
86    case ?MODULE of
87	misc_SUITE ->
88            %% Canononical module name. Run slow cases.
89            [{group,slow}];
90        _ ->
91            %% Cloned module. Don't run.
92            []
93    end.
94
95%%
96%% Functions that override new and old bif's
97%%
98abs(_N) ->
99    dummy_abs.
100
101binary_part(_,_,_) ->
102    dummy_bp.
103
104%% Test that local functions and imports override auto-imported BIFs.
105override_bif(Config) when is_list(Config) ->
106    dummy_abs = abs(1),
107    dummy_bp = binary_part(<<"hello">>,1,1),
108    dummy = binary_part(<<"hello">>,{1,1}),
109    1 = erlang:abs(1),
110    <<"e">> = erlang:binary_part(<<"hello">>,1,1),
111    <<"e">> = erlang:binary_part(<<"hello">>,{1,1}),
112    F = fun(X) when byte_size(X) =:= 4 ->
113		four;
114	   (X) ->
115		byte_size(X)
116	end,
117    four = F(<<1,2,3,4>>),
118    5 = F(<<1,2,3,4,5>>),
119    ok.
120
121%% A bug reported by Tobias Lindahl for a development version of R11B.
122
123tobias(Config) when is_list(Config) ->
124    1 = tobias_1([1,2,3]),
125    ok.
126
127tobias_1([H|_T]) ->
128    %% In an R11B compiler, the move optimizer in beam_block would
129    %% confuse H and _T.
130    tobias_2(0, 0),
131    H.
132
133tobias_2(_, _) ->
134    2.
135
136
137%% A bug reported by Richard Carlsson. Used to crash beam_asm
138%% because of a put_string instruction with an empty string.
139%% The real problem was in sys_core_fold (empty strings should
140%% be replaced by []).
141
142-record(r, {s = ""}).
143
144empty_string(Config) when is_list(Config) ->
145    #r{s="x"} = empty_string_1(#r{}),
146    ok.
147
148empty_string_1(T) ->
149    case T of
150	#r{s = ""} -> T #r{s = "x"}
151    end.
152
153md5(Config) when is_list(Config) ->
154    Dir = filename:dirname(code:which(?MODULE)),
155    Beams = filelib:wildcard(filename:join(Dir, "*.beam")),
156    io:format("Found ~w beam files", [length(Beams)]),
157    lists:foreach(fun md5_1/1, Beams).
158
159md5_1(Beam) ->
160    {ok,{Mod,[Vsn]}} = beam_lib:version(Beam),
161    {ok,Code} = file:read_file(Beam),
162    {Mod,<<Vsn:128>>} = {Mod,code:module_md5(Code)}.
163
164%% Cover some code that handles internal errors.
165
166silly_coverage(Config) when is_list(Config) ->
167    %% sys_core_fold, sys_core_alias, sys_core_bsm, v3_kernel
168    BadCoreErlang = {c_module,[],
169		     name,[],[],
170		     [{{c_var,[],{foo,2}},seriously_bad_body}]},
171    expect_error(fun() -> sys_core_fold:module(BadCoreErlang, []) end),
172    expect_error(fun() -> sys_core_alias:module(BadCoreErlang, []) end),
173    expect_error(fun() -> sys_core_bsm:module(BadCoreErlang, []) end),
174    expect_error(fun() -> v3_kernel:module(BadCoreErlang, []) end),
175
176    %% beam_kernel_to_ssa
177    BadKernel = {k_mdef,[],?MODULE,
178		 [{foo,0}],
179		 [],
180		 [{k_fdef,
181		   {k,[],[],[]},
182		   f,0,[],
183		   seriously_bad_body}]},
184    expect_error(fun() -> beam_kernel_to_ssa:module(BadKernel, []) end),
185
186    %% beam_ssa_lint
187    %% beam_ssa_recv
188    %% beam_ssa_share
189    %% beam_ssa_pre_codegen
190    %% beam_ssa_codegen
191    BadSSA = {b_module,#{},a,b,c,
192              [{b_function,#{func_info=>{mod,foo,0}},args,bad_blocks,0}]},
193    expect_error(fun() -> beam_ssa_lint:module(BadSSA, []) end),
194    expect_error(fun() -> beam_ssa_recv:module(BadSSA, []) end),
195    expect_error(fun() -> beam_ssa_share:module(BadSSA, []) end),
196    expect_error(fun() -> beam_ssa_pre_codegen:module(BadSSA, []) end),
197    expect_error(fun() -> beam_ssa_codegen:module(BadSSA, []) end),
198
199    %% beam_ssa_opt
200    BadSSABlocks = #{0 => {b_blk,#{},[bad_code],{b_ret,#{},arg}}},
201    BadSSAOpt = {b_module,#{},a,[],c,
202                 [{b_function,#{func_info=>{mod,foo,0}},[],
203                   BadSSABlocks,0}]},
204    expect_error(fun() -> beam_ssa_opt:module(BadSSAOpt, []) end),
205
206    %% beam_ssa_lint, beam_ssa_pp
207    {error,[{_,Errors}]} = beam_ssa_lint:module(bad_ssa_lint_input(), []),
208    _ = [io:put_chars(Mod:format_error(Reason)) ||
209            {Mod,Reason} <- Errors],
210
211    %% Cover printing of annotations in beam_ssa_pp
212    PPAnno = #{func_info=>{mod,foo,0},other_anno=>value,map_anno=>#{k=>v}},
213    PPBlocks = #{0=>{b_blk,#{},[],{b_ret,#{},{b_literal,42}}}},
214    PP = {b_function,PPAnno,[],PPBlocks,0},
215    io:put_chars(beam_ssa_pp:format_function(PP)),
216
217    %% beam_a
218    BeamAInput = {?MODULE,[{foo,0}],[],
219		  [{function,foo,0,2,
220		    [{label,1},
221		     {func_info,{atom,?MODULE},{atom,foo},0},
222		     {label,2}|non_proper_list]}],99},
223    expect_error(fun() -> beam_a:module(BeamAInput, []) end),
224
225    %% beam_block
226    BlockInput = {?MODULE,[{foo,0}],[],
227		  [{function,foo,0,2,
228		    [{label,1},
229		     {func_info,{atom,?MODULE},{atom,foo},0},
230		     {label,2}|non_proper_list]}],99},
231    expect_error(fun() -> beam_block:module(BlockInput, []) end),
232
233    %% beam_except
234    ExceptInput = {?MODULE,[{foo,0}],[],
235		   [{function,foo,0,2,
236		     [{label,1},
237		      {line,loc},
238		      {func_info,{atom,?MODULE},{atom,foo},0},
239		      {label,2}|non_proper_list]}],99},
240    expect_error(fun() -> beam_except:module(ExceptInput, []) end),
241
242    %% beam_jump
243    JumpInput = BlockInput,
244    expect_error(fun() -> beam_jump:module(JumpInput, []) end),
245
246    %% beam_clean
247    CleanInput = {?MODULE,[{foo,0}],[],
248		  [{function,foo,0,2,
249		    [{label,1},
250		     {func_info,{atom,?MODULE},{atom,foo},0},
251		     {label,2},
252		     {jump,{f,42}}]}],99},
253    expect_error(fun() -> beam_clean:module(CleanInput, []) end),
254
255    %% beam_jump
256    TrimInput = BlockInput,
257    expect_error(fun() -> beam_trim:module(TrimInput, []) end),
258
259    %% beam_peep. This is tricky. Use a select instruction with
260    %% an odd number of elements in the list to crash
261    %% prune_redundant_values/2 but not beam_clean:clean_labels/1.
262    PeepInput = {?MODULE,[{foo,0}],[],
263		 [{function,foo,0,2,
264		   [{label,1},
265		    {func_info,{atom,?MODULE},{atom,foo},0},
266		    {label,2},{select,select_val,r,{f,2},[{f,2}]}]}],
267		 2},
268    expect_error(fun() -> beam_peep:module(PeepInput, []) end),
269
270    BeamZInput = {?MODULE,[{foo,0}],[],
271		  [{function,foo,0,2,
272		    [{label,1},
273		     {func_info,{atom,?MODULE},{atom,foo},0},
274		     {label,2}|non_proper_list]}],99},
275    expect_error(fun() -> beam_z:module(BeamZInput, []) end),
276
277    %% beam_validator.
278    BeamValInput = {?MODULE,[{foo,0}],[],
279		    [{function,foo,0,2,
280		      [{label,1},
281		       {func_info,{atom,?MODULE},{atom,foo},0},
282		       {label,2}|non_proper_list]}],99},
283    expect_error(fun() -> beam_validator:module(BeamValInput, []) end),
284
285    ok.
286
287bad_ssa_lint_input() ->
288    {b_module,#{},t,
289     [{foobar,1},{module_info,0},{module_info,1}],
290     [],
291     [{b_function,
292       #{func_info => {t,foobar,1},location => {"t.erl",4}},
293       [{b_var,0}],
294       #{0 => {b_blk,#{},[],{b_ret,#{},{b_var,'@undefined_var'}}}},
295       3},
296      {b_function,
297       #{func_info => {t,module_info,0}},
298       [],
299       #{0 =>
300             {b_blk,#{},
301              [{b_set,#{},
302                {b_var,{'@ssa_ret',3}},
303                call,
304                [{b_remote,
305                  {b_literal,erlang},
306                  {b_literal,get_module_info},
307                  1},
308                 {b_var,'@unknown_variable'}]}],
309              {b_ret,#{},{b_var,{'@ssa_ret',3}}}}},
310       4}]}.
311
312expect_error(Fun) ->
313    try	Fun() of
314	Any ->
315	    io:format("~p", [Any]),
316	    ct:fail(call_was_supposed_to_fail)
317    catch
318	Class:Reason:Stk ->
319	    io:format("~p:~p\n~p\n", [Class,Reason,Stk]),
320	    case {Class,Reason} of
321		{error,undef} ->
322		    ct:fail(not_supposed_to_fail_with_undef);
323		{_,_} ->
324		    ok
325	    end
326    end.
327
328confused_literals(Config) when is_list(Config) ->
329    {0,infinity} = confused_literals_1(int),
330    {0.0,infinity} = confused_literals_1(float),
331    ok.
332
333confused_literals_1(int) -> {0,infinity};
334confused_literals_1(float) -> {0.0,infinity}.
335
336integer_encoding() ->
337    [{timetrap,{minutes,4}}].
338
339integer_encoding(Config) when is_list(Config) ->
340    PrivDir = proplists:get_value(priv_dir, Config),
341    SrcFile = filename:join(PrivDir, "misc_SUITE_integer_encoding.erl"),
342    DataFile = filename:join(PrivDir, "integer_encoding.data"),
343    Mod = misc_SUITE_integer_encoding,
344
345    %% Create files.
346    {ok,Src} = file:open(SrcFile, [write]),
347    {ok,Data} = file:open(DataFile, [write]),
348    io:format(Src, "-module(~s).\n", [Mod]),
349    io:put_chars(Src, "-export([t/1]).\n"),
350    io:put_chars(Src, "t(Last) ->[\n"),
351    io:put_chars(Data, "[\n"),
352
353    do_integer_encoding(137, 0, Src, Data),
354    _ = [begin
355	     B = 1 bsl I,
356	     do_integer_encoding(-B-1, Src, Data),
357	     do_integer_encoding(-B, Src, Data),
358	     do_integer_encoding(-B+1, Src, Data),
359	     do_integer_encoding(B-1, Src, Data),
360	     do_integer_encoding(B, Src, Data),
361	     do_integer_encoding(B+1, Src, Data)
362	 end || I <- lists:seq(1, 130)],
363    io:put_chars(Src, "Last].\n\n"),
364    ok = file:close(Src),
365    io:put_chars(Data, "0].\n\n"),
366    ok = file:close(Data),
367
368    %% Compile and load Erlang module.
369    SrcRoot = filename:rootname(SrcFile),
370    {ok,Mod,Binary} = compile:file(SrcRoot, [binary,report]),
371    {module,Mod} = code:load_binary(Mod, SrcRoot, Binary),
372
373    %% Compare lists.
374    List = Mod:t(0),
375    {ok,[List]} = file:consult(DataFile),
376
377    %% Cleanup.
378    file:delete(SrcFile),
379    file:delete(DataFile),
380    ok.
381
382do_integer_encoding(0, _, _, _) -> ok;
383do_integer_encoding(N, I0, Src, Data) ->
384    I1 = (I0 bsl 5) bor (rand:uniform(32) - 1),
385    do_integer_encoding(I1, Src, Data),
386    I2 = -(I1 bxor (rand:uniform(32) - 1)),
387    do_integer_encoding(I2, Src, Data),
388    do_integer_encoding(N-1, I1, Src, Data).
389
390do_integer_encoding(I, Src, Data) ->
391    Str = integer_to_list(I),
392    io:put_chars(Src, [Str,",\n"]),
393    io:put_chars(Data, [Str,",\n"]).
394