%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2005-2018. 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. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(bs_match_SUITE). -compile(nowarn_shadow_vars). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, verify_highest_opcode/1, size_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1, bin_tail/1,save_restore/1, partitioned_bs_match/1,function_clause/1, unit/1,shared_sub_bins/1,bin_and_float/1, dec_subidentifiers/1,skip_optional_tag/1,decode_integer/1, wfbm/1,degenerated_match/1,bs_sum/1,coverage/1, multiple_uses/1,zero_label/1,followed_by_catch/1, matching_meets_construction/1,simon/1,matching_and_andalso/1, otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1, match_string/1,zero_width/1,bad_size/1,haystack/1, cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1, no_partition/1,calling_a_binary/1,binary_in_map/1, match_string_opt/1,select_on_integer/1, map_and_binary/1,unsafe_branch_caching/1, bad_literals/1,good_literals/1,constant_propagation/1, parse_xml/1,get_payload/1,escape/1,num_slots_different/1, beam_bsm/1,guard/1,is_ascii/1,non_opt_eq/1, expression_before_match/1,erl_689/1,restore_on_call/1, restore_after_catch/1,matches_on_parameter/1,big_positions/1, matching_meets_apply/1,bs_start_match2_defs/1]). -export([coverage_id/1,coverage_external_ignore/2]). -include_lib("common_test/include/ct.hrl"). -include_lib("syntax_tools/include/merl.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. all() -> [{group,p}]. groups() -> [{p,[], [verify_highest_opcode, size_shadow,int_float,otp_5269,null_fields,wiger, bin_tail,save_restore, partitioned_bs_match,function_clause,unit, shared_sub_bins,bin_and_float,dec_subidentifiers, skip_optional_tag,decode_integer,wfbm,degenerated_match,bs_sum, coverage,multiple_uses,zero_label,followed_by_catch, matching_meets_construction,simon, matching_and_andalso,otp_7188,otp_7233,otp_7240, otp_7498,match_string,zero_width,bad_size,haystack, cover_beam_bool,matched_out_size,follow_fail_branch, no_partition,calling_a_binary,binary_in_map, match_string_opt,select_on_integer, map_and_binary,unsafe_branch_caching, bad_literals,good_literals,constant_propagation,parse_xml, get_payload,escape,num_slots_different, beam_bsm,guard,is_ascii,non_opt_eq, expression_before_match,erl_689,restore_on_call, matches_on_parameter,big_positions, matching_meets_apply,bs_start_match2_defs]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> ok. verify_highest_opcode(_Config) -> case ?MODULE of bs_match_r21_SUITE -> {ok,Beam} = file:read_file(code:which(?MODULE)), case test_lib:highest_opcode(Beam) of Highest when Highest =< 163 -> ok; TooHigh -> ct:fail({too_high_opcode_for_21,TooHigh}) end; _ -> ok end. size_shadow(Config) when is_list(Config) -> %% Originally OTP-5270. 7 = size_shadow_1(), 7 = size_shadow_2(8), 7 = size_shadow_3(), no = size_shadow_4(8), Any = {any,term,goes}, {2577,Any,-175,whatever} = (size_shadow_5(Any, 12))(<<2577:12>>, -175, whatever), {7777,Any,42,whatever} = (size_shadow_6(Any, 13))(42, <<7777:13>>, whatever), {<<45>>,<<>>} = size_shadow_7({int,1}, <<1:16,45>>), {'EXIT',{function_clause,_}} = (catch size_shadow_7({int,42}, <<1:16,45>>)), ok. size_shadow_1() -> L = 8, F = fun(<>) -> B end, F(<<16:8, 7:16>>). size_shadow_2(L) -> F = fun(<>) -> B end, F(<<16:8, 7:16>>). size_shadow_3() -> L = 8, F = fun(<>) -> B end, F(<<16:8, 7:16,16:16>>). size_shadow_4(L) -> F = fun(<>) -> B; (_) -> no end, F(<<16:8, 7:16,15:16>>). size_shadow_5(X, Y) -> fun (<< A:Y >>, Y, B) -> fum(A, X, Y, B) end. size_shadow_6(X, Y) -> fun (Y, << A:Y >>, B) -> fum(A, X, Y, B) end. fum(A, B, C, D) -> {A,B,C,D}. size_shadow_7({int,N}, <>) -> {B,T}. int_float(Config) when is_list(Config) -> %% OTP-5323 <<103133.0:64/float>> = <<103133:64/float>>, <<103133:64/float>> = <<103133:64/float>>, %% Coverage of error cases in sys_pre_expand:coerce_to_float/2. case id(default) of <<(1 bsl 1024):64/float>> -> ct:fail(should_not_match); default -> ok end. %% Stolen from erl_eval_SUITE and modified. %% OTP-5269. Bugs in the bit syntax. otp_5269(Config) when is_list(Config) -> check(fun() -> L = 8, F = fun(<>) -> B end, F(<<16:8, 7:16>>) end, 7), check(fun() -> L = 8, <> = <<16:8, 7:16>>, B end, 7), check(fun() -> U = 8, (fun(<>) -> U end)(<<32:8>>) end, 32), check(fun() -> U = 8, [U || <> <- [<<32:8>>]] end, [32]), check(fun() -> [X || <> <- [<<16:8,19:16>>], <> <- [<>]] end, [19]), check(fun() -> A = 4, B = 28, bit_size(<<13:(A+(X=B))>>), X end, 28), check(fun() -> <> = <<2,"AB","CD">>, {Size,B,Rest} end, {2,<<"AB">>,<<"CD">>}), check(fun() -> X = 32, [X || <> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end, %% "binsize variable" ^ [1,2]), check(fun() -> (fun (<>) -> case A of B -> wrong; _ -> ok end end)(<<1,2,3,4>>) end, ok), ok. null_fields(Config) when is_list(Config) -> check(fun() -> W = id(0), F = fun(<<_:W>>) -> tail; (<<>>) -> empty end, F(<<>>) end, tail), check(fun() -> F = fun(<<_/binary>>) -> tail; (<<>>) -> empty end, F(<<>>) end, tail), ok. wiger(Config) when is_list(Config) -> ok1 = wcheck(<<3>>), ok2 = wcheck(<<1,2,3>>), ok3 = wcheck(<<4>>), {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>), {error,<<>>} = wcheck(<<>>), ok. wcheck(<>) when A==3-> ok1; wcheck(<<_,_:2/binary>>) -> ok2; wcheck(<<_>>) -> ok3; wcheck(Other) -> {error,Other}. bin_tail(Config) when is_list(Config) -> S = <<"abcde">>, $a = bin_tail_c(S, 0), $c = bin_tail_c(S, 2), $e = bin_tail_c(S, 4), {'EXIT',_} = (catch bin_tail_c(S, 5)), {'EXIT',_} = (catch bin_tail_c_var(S, 5)), $a = bin_tail_d(S, 0), $b = bin_tail_d(S, 8), $d = bin_tail_d(S, 3*8), {'EXIT',_} = (catch bin_tail_d_dead(S, 1)), {'EXIT',_} = (catch bin_tail_d_dead(S, 9)), {'EXIT',_} = (catch bin_tail_d_dead(S, 5*8)), {'EXIT',_} = (catch bin_tail_d_var(S, 1)), ok = bin_tail_e(<<2:2,0:1,1:5>>), ok = bin_tail_e(<<2:2,1:1,1:5,42:64>>), error = bin_tail_e(<<3:2,1:1,1:5,42:64>>), error = bin_tail_e(<<>>), MD5 = erlang:md5(<<42>>), <<"abc">> = bin_tail_f(<>, MD5, 3), error = bin_tail_f(<>, MD5, 999), {'EXIT',{_,_}} = (catch bin_tail_f(<<0:16/unit:8>>, MD5, 0)), ok. bin_tail_c(Bin, Offset) -> Res = bin_tail_c_dead(Bin, Offset), <<_:Offset/binary,_,Tail/binary>> = Bin, {Res,Tail} = bin_tail_c_var(Bin, Offset), Res. bin_tail_c_dead(Bin, Offset) -> <<_:Offset/binary,C,_/binary>> = Bin, C. bin_tail_c_var(Bin, Offset) -> <<_:Offset/binary,C,Tail/binary>> = Bin, {C,Tail}. bin_tail_d(Bin, BitOffset) -> Res = bin_tail_d_dead(Bin, BitOffset), <<_:BitOffset,_:8,Tail/binary>> = Bin, {Res,Tail} = bin_tail_d_var(Bin, BitOffset), Res. bin_tail_d_dead(Bin, BitOffset) -> <<_:BitOffset,C,_/binary>> = Bin, C. bin_tail_d_var(Bin, BitOffset) -> <<_:BitOffset,C,Tail/binary>> = Bin, {C,Tail}. bin_tail_e(Bin) -> case bin_tail_e_dead(Bin) of ok -> <<_,Tail/binary>> = Bin, Tail = bin_tail_e_var(Bin), ok; error -> bin_tail_e_var(Bin) end. bin_tail_e_dead(Bin) -> case Bin of %% The binary is aligned at the end; neither the bs_skip_bits2 nor %% bs_test_tail2 instructions are needed. <<2:2,_:1,1:5,_/binary>> -> ok; _ -> error end. bin_tail_e_var(Bin) -> case Bin of %% The binary is aligned at the end; neither the bs_skip_bits2 nor %% bs_test_tail2 instructions are needed. <<2:2,_:1,1:5,Tail/binary>> -> Tail; _ -> error end. bin_tail_f(Bin, MD5, Size) -> case Bin of <> -> Tail; <> -> error end. save_restore(Config) when is_list(Config) -> 0 = save_restore_1(<<0:2,42:6>>), {1,3456} = save_restore_1(<<1:2,3456:14>>), {2,7981234} = save_restore_1(<<2:2,7981234:30>>), {3,763967493838} = save_restore_1(<<0:2,763967493838:62>>), A = <<" x">>, B = <<".x">>, C = <<"-x">>, {" ",<<"x">>} = lll(A), {" ",<<"x">>} = mmm(A), {" ",<<"x">>} = nnn(A), {" ",<<"x">>} = ooo(A), {".",<<"x">>} = lll(B), {".",<<"x">>} = mmm(B), {".",<<"x">>} = nnn(B), {".",<<"x">>} = ooo(B), {"-",<<"x">>} = lll(C), {"-",<<"x">>} = mmm(C), {"-",<<"x">>} = nnn(C), {"-",<<"x">>} = ooo(C), a = multiple_matches(<<777:16>>, <<777:16>>), b = multiple_matches(<<777:16>>, <<999:16>>), c = multiple_matches(<<777:16>>, <<57:8>>), d = multiple_matches(<<17:8>>, <<1111:16>>), Bin = <<-1:64>>, case bad_float_unpack_match(Bin) of -1 -> ok; _Other -> ct:fail(bad_return_value_probably_NaN) end. save_restore_1(Bin) -> case Bin of <<0:2,_:6>> -> 0; <<1:2,A:14>> -> {1,A}; <<2:2,A:30>> -> {2,A}; <> -> {3,A} end. lll(<>) -> {[Char],Tail}. mmm(<<$.,$.,$., Tail/binary>>) -> Tail; mmm(<<$\s,$-,$\s, Tail/binary>>) -> Tail; mmm(<>) -> {[Char],Tail}. %% Buggy Tail! nnn(<<"...", Tail/binary>>) -> Tail; nnn(<<" - ", Tail/binary>>) -> Tail; nnn(<>) -> {[Char],Tail}. %% Buggy Tail! ooo(<<" - ", Tail/binary>>) -> Tail; ooo(<>) -> {[Char],Tail}. multiple_matches(<>, <>) -> a; multiple_matches(<<_:16>>, <<_:16>>) -> b; multiple_matches(<<_:16>>, <<_:8>>) -> c; multiple_matches(<<_:8>>, <<_:16>>) -> d. bad_float_unpack_match(<>) -> F; bad_float_unpack_match(<>) -> I. partitioned_bs_match(Config) when is_list(Config) -> <<1,2,3>> = partitioned_bs_match(blurf, <<42,1,2,3>>), error = partitioned_bs_match(10, <<7,8,15,13>>), error = partitioned_bs_match(100, {a,tuple,is,'not',a,binary}), ok = partitioned_bs_match(0, <<>>), fc(partitioned_bs_match, [-1,blurf], catch partitioned_bs_match(-1, blurf)), fc(partitioned_bs_match, [-1,<<1,2,3>>], catch partitioned_bs_match(-1, <<1,2,3>>)), {17,<<1,2,3>>} = partitioned_bs_match_2(1, <<17,1,2,3>>), {7,<<1,2,3>>} = partitioned_bs_match_2(7, <<17,1,2,3>>), fc(partitioned_bs_match_2, [4,<<0:17>>], catch partitioned_bs_match_2(4, <<0:17>>)), anything = partitioned_bs_match_3(anything, <<42>>), ok = partitioned_bs_match_3(1, 2), ok. partitioned_bs_match(_, <<42:8,T/binary>>) -> T; partitioned_bs_match(N, _) when N > 0 -> error; partitioned_bs_match(_, <<>>) -> ok. partitioned_bs_match_2(1, <>) -> {B,T}; partitioned_bs_match_2(Len, <<_:8,T/binary>>) -> {Len,T}. partitioned_bs_match_3(Var, <<_>>) -> Var; partitioned_bs_match_3(1, 2) -> ok. function_clause(Config) when is_list(Config) -> ok = function_clause_1(<<0,7,0,7,42>>), fc(function_clause_1, [<<0,1,2,3>>], catch function_clause_1(<<0,1,2,3>>)), fc(function_clause_1, [<<0,1,2,3>>], catch function_clause_1(<<0,7,0,1,2,3>>)), ok = function_clause_2(<<0,7,0,7,42>>), ok = function_clause_2(<<255>>), ok = function_clause_2(<<13:4>>), fc(function_clause_2, [<<0,1,2,3>>], catch function_clause_2(<<0,1,2,3>>)), fc(function_clause_2, [<<0,1,2,3>>], catch function_clause_2(<<0,7,0,1,2,3>>)), ok. function_clause_1(<<0:8,7:8,T/binary>>) -> function_clause_1(T); function_clause_1(<<_:8>>) -> ok. function_clause_2(<<0:8,7:8,T/binary>>) -> function_clause_2(T); function_clause_2(<<_:8>>) -> ok; function_clause_2(<<_:4>>) -> ok. unit(Config) when is_list(Config) -> 42 = peek1(<<42>>), 43 = peek1(<<43,1,2>>), 43 = peek1(<<43,1,2,(-1):1>>), 43 = peek1(<<43,1,2,(-1):2>>), 43 = peek1(<<43,1,2,(-1):7>>), 99 = peek8(<<99>>), 100 = peek8(<<100,101>>), fc(peek8, [<<100,101,0:1>>], catch peek8(<<100,101,0:1>>)), 37484 = peek16(<<37484:16>>), 37489 = peek16(<<37489:16,5566:16>>), fc(peek16, [<<8>>], catch peek16(<<8>>)), fc(peek16, [<<42:15>>], catch peek16(<<42:15>>)), fc(peek16, [<<1,2,3,4,5>>], catch peek16(<<1,2,3,4,5>>)), 127 = peek7(<<127:7>>), 100 = peek7(<<100:7,19:7>>), fc(peek7, [<<1,2>>], catch peek7(<<1,2>>)), 1 = unit_opt(1, -1), 8 = unit_opt(8, -1), <<1:32,"abc">> = unit_opt_2(<<1:32,"abc">>), <<"def">> = unit_opt_2(<<2:32,"def">>), {'EXIT',_} = (catch unit_opt_2(<<1:32,33:7>>)), {'EXIT',_} = (catch unit_opt_2(<<2:32,55:7>>)), ok. peek1(<>) -> B. peek7(<>) -> B. peek8(<>) -> B. peek16(<>) -> B. unit_opt(U, X) -> %% Cover type analysis in beam_ssa_type. Bin = case U of 1 -> <>; 8 -> <> end, %% The type of Bin will be set to {binary,gcd(1, 8)}. case Bin of <<_/binary-unit:8>> -> 8; <<_/binary-unit:1>> -> 1 end. unit_opt_2(<> = Bin0) -> Bin = if St =:= 1 -> Bin0; St =:= 2 -> <> end, id(Bin). shared_sub_bins(Config) when is_list(Config) -> {15,[<<>>,<<5>>,<<4,5>>,<<3,4,5>>,<<2,3,4,5>>]} = sum(<<1,2,3,4,5>>, [], 0), ok. sum(<>, Acc, Sum) -> sum(T, [T|Acc], Sum+B); sum(<<>>, Last, Sum) -> {Sum,Last}. bin_and_float(Config) when is_list(Config) -> 14.0 = bin_and_float(<<1.0/float,2.0/float,3.0/float>>, 0.0), ok. bin_and_float(<>, Sum) when is_float(X), is_float(Y), is_float(Z) -> bin_and_float(T, Sum+X*X+Y*Y+Z*Z); bin_and_float(<<>>, Sum) -> Sum. dec_subidentifiers(Config) when is_list(Config) -> {[],<<1,2,3>>} = do_dec_subidentifiers(<<1:1,42:7,1:1,99:7,1,2,3>>, 0, [], 2), {[5389],<<1,2,3>>} = do_dec_subidentifiers(<<1:1,42:7,0:1,13:7,1,2,3>>, 0, [], 2), {[3,2,1],not_a_binary} = dec_subidentifiers(not_a_binary, any, [1,2,3], 0), ok. do_dec_subidentifiers(Buffer, Av, Al, Len) -> Res = dec_subidentifiers(Buffer, Av, Al, Len), Res = dec_subidentifiers2(Buffer, Av, Al, Len), Res = dec_subidentifiers4(Buffer, Av, Al, Len), Res = dec_subidentifiers3(Buffer, Av, Al, Len). dec_subidentifiers(Buffer, _Av, Al, 0) -> {lists:reverse(Al),Buffer}; dec_subidentifiers(<<1:1,H:7,T/binary>>, Av, Al, Len) -> dec_subidentifiers(T, (Av bsl 7) bor H, Al, Len-1); dec_subidentifiers(<>, Av, Al, Len) -> dec_subidentifiers(T, 0, [((Av bsl 7) bor H)|Al], Len-1). dec_subidentifiers2(<>, _Av, Al, 0) -> {lists:reverse(Al),Buffer}; dec_subidentifiers2(<<1:1,H:7,T/binary>>, Av, Al, Len) -> dec_subidentifiers2(T, (Av bsl 7) bor H, Al, Len-1); dec_subidentifiers2(<>, Av, Al, Len) -> dec_subidentifiers2(T, 0, [((Av bsl 7) bor H)|Al], Len-1). dec_subidentifiers3(Buffer, _Av, Al, 0) when is_binary(Buffer) -> {lists:reverse(Al),Buffer}; dec_subidentifiers3(<<1:1,H:7,T/binary>>, Av, Al, Len) -> dec_subidentifiers3(T, (Av bsl 7) bor H, Al, Len-1); dec_subidentifiers3(<>, Av, Al, Len) -> dec_subidentifiers3(T, 0, [((Av bsl 7) bor H)|Al], Len-1). dec_subidentifiers4(<<1:1,H:7,T/binary>>, Av, Al, Len) when Len =/= 0 -> dec_subidentifiers4(T, (Av bsl 7) bor H, Al, Len-1); dec_subidentifiers4(<>, Av, Al, Len) when Len =/= 0 -> dec_subidentifiers4(T, 0, [((Av bsl 7) bor H)|Al], Len-1); dec_subidentifiers4(Buffer, _Av, Al, 0) -> {lists:reverse(Al),Buffer}. skip_optional_tag(Config) when is_list(Config) -> {ok,<<>>} = skip_optional_tag(<<42>>, <<42>>), {ok,<<>>} = skip_optional_tag(<<42,1>>, <<42,1>>), {ok,<<1,2,3>>} = skip_optional_tag(<<42>>, <<42,1,2,3>>), missing = skip_optional_tag(<<2:3>>, blurf), ok. skip_optional_tag(<<>>, Binary) -> {ok,Binary}; skip_optional_tag(<>, <>) -> skip_optional_tag(RestTag, Rest); skip_optional_tag(_, _) -> missing. decode_integer(_Config) -> {10795,<<43>>,whatever} = decode_integer(1, <<42,43>>, whatever), {-28909,<<19>>,whatever} = decode_integer(1, <<143,19>>, whatever), ok. decode_integer(Len, <>, RemovedBytes) when B1 == 0 -> Bin = <<_Skip:Len/unit:8, Buffer2/binary>> = <>, Size = byte_size(Bin), <> = Bin, {Int,Buffer2,RemovedBytes}; decode_integer(Len, <>, RemovedBytes) -> Bin = <<_Skip:Len/unit:8,Buffer2/binary>> = <>, Size = byte_size(Bin), <> = <>, Int = N - (1 bsl (8 * size(Bin) -1)), {Int,Buffer2,RemovedBytes}. -define(DATELEN, 16). wfbm(Config) when is_list(Config) -> %% check_for_dot_or_space and get_tail is from wfbm4 by Steve Vinoski, %% with modifications. {nomatch,0} = check_for_dot_or_space(<<" ">>), {nomatch,0} = check_for_dot_or_space(<<" abc">>), {ok,<<"abcde">>} = check_for_dot_or_space(<<"abcde 34555">>), {nomatch,0} = check_for_dot_or_space(<<".gurka">>), {nomatch,1} = check_for_dot_or_space(<<"g.urka">>), nomatch = get_tail(<<>>), {ok,<<"2007/10/23/blurf">>} = get_tail(<<"200x/2007/10/23/blurf ">>), {skip,?DATELEN+5} = get_tail(<<"200x/2007/10/23/blurf.">>), nomatch = get_tail(<<"200y.2007.10.23.blurf ">>), {'EXIT',_} = (catch get_tail({no,binary,at,all})), {'EXIT',_} = (catch get_tail(no_binary)), ok. check_for_dot_or_space(Bin) -> check_for_dot_or_space(Bin, 0). check_for_dot_or_space(<<$\s, _/binary>>, 0) -> {nomatch,0}; check_for_dot_or_space(Bin, Len) -> case Bin of <> -> {ok,Front}; <<_:Len/binary, $., _/binary>> -> {nomatch,Len}; _ -> check_for_dot_or_space(Bin, Len+1) end. get_tail(<<>>) -> nomatch; get_tail(Bin) -> <> = Bin, case Front of <<_:3/binary,"x/",Y:4/binary,$/,M:2/binary,$/,D:2/binary,$/>> -> case check_for_dot_or_space(Tail) of {ok,Match} -> {ok,<>}; {nomatch,Skip} -> {skip,?DATELEN + Skip} end; _ -> nomatch end. degenerated_match(Config) when is_list(Config) -> error = degenerated_match_1(<<>>), 1 = degenerated_match_1(<<1:1>>), 2 = degenerated_match_1(<<42,43>>), error = degenerated_match_2(<<>>), no_split = degenerated_match_2(<<1,2>>), {<<1,2,3,4>>,<<5>>} = degenerated_match_2(<<1,2,3,4,5>>), ok. degenerated_match_1(<<>>) -> error; degenerated_match_1(Bin) -> byte_size(Bin). degenerated_match_2(<<>>) -> error; degenerated_match_2(Bin) -> case byte_size(Bin) > 4 of true -> split_binary(Bin, 4); false -> no_split end. bs_sum(Config) when is_list(Config) -> 0 = bs_sum_1([]), 0 = bs_sum_1(<<>>), 42 = bs_sum_1([42]), 1 = bs_sum_1(<<1>>), 10 = bs_sum_1([1,2,3,4]), 15 = bs_sum_1(<<1,2,3,4,5>>), 21 = bs_sum_1([1,2,3|<<4,5,6>>]), 15 = bs_sum_1([1,2,3|{4,5}]), 6 = bs_sum_1([1,2,3|zero]), 6 = bs_sum_1([1,2,3|0]), 7 = bs_sum_1([1,2,3|one]), fc(catch bs_sum_1({too,big,tuple})), fc(catch bs_sum_1([1,2,3|{too,big,tuple}])), [] = sneaky_alias(<<>>), [559,387655] = sneaky_alias(id(<<559:32,387655:32>>)), fc(sneaky_alias, [<<1>>], catch sneaky_alias(id(<<1>>))), fc(sneaky_alias, [[1,2,3,4]], catch sneaky_alias(lists:seq(1, 4))), ok. bs_sum_1(<>) -> H+bs_sum_1(T); bs_sum_1([H|T]) -> H+bs_sum_1(T); bs_sum_1({A,B}=_Tuple=_AliasForNoGoodReason) -> A+B; bs_sum_1(0) -> 0; bs_sum_1(zero=_Zero) -> 0; bs_sum_1(one) -> 1; bs_sum_1([]) -> 0; bs_sum_1(<<>>) -> 0. sneaky_alias(<<>>=L) -> binary_to_list(L); sneaky_alias(<>) -> [From|sneaky_alias(L)]. coverage(Config) when is_list(Config) -> 0 = coverage_fold(fun(B, A) -> A+B end, 0, <<>>), 6 = coverage_fold(fun(B, A) -> A+B end, 0, <<1,2,3>>), fc(catch coverage_fold(fun(B, A) -> A+B end, 0, [a,b,c])), {<<42.0:64/float>>,float} = coverage_build(<<>>, <<42>>, float), {<<>>,not_a_tuple} = coverage_build(<<>>, <<>>, not_a_tuple), {<<16#76,"abc",16#A9,"abc">>,{x,42,43}} = coverage_build(<<>>, <<16#7,16#A>>, {x,y,z}), [<<2>>,<<1>>] = coverage_bc(<<1,2>>, []), {x,<<"abc">>,z} = coverage_setelement(<<2,"abc">>, {x,y,z}), [42] = coverage_apply(<<42>>, [coverage_id]), 42 = coverage_external(<<42>>), do_coverage_bin_to_term_list([]), do_coverage_bin_to_term_list([lists:seq(0, 10),{a,b,c},<<23:42>>]), fc(coverage_bin_to_term_list, [<<0,0,0,7>>], catch do_coverage_bin_to_term_list_1(<<7:32>>)), <<>> = coverage_per_key(<<4:32>>), <<$a,$b,$c>> = coverage_per_key(<<7:32,"abc">>), binary = coverage_bitstring(<<>>), binary = coverage_bitstring(<<7>>), bitstring = coverage_bitstring(<<7:4>>), other = coverage_bitstring([a]), %% Cover code in beam_trim. {done,<<17,53>>,[253,155,200]} = coverage_trim(<<253,155,200,17,53>>, e0, e1, e2, e3, []), <<"(right|linux)">> = coverage_trim_1(<<"">>, <<"right">>, <<"linux">>), <<"/(right|linux)">> = coverage_trim_1(<<"/">>, <<"right">>, <<"linux">>), <<"(left|linux)/(right|linux)">> = coverage_trim_1(<<"left">>, <<"right">>, <<"linux">>), {10,<<"-">>,""} = coverage_trim_2(<<"-">>, 10, []), {8,<<"-">>,"aa"} = coverage_trim_2(<<"aa-">>, 10, []), ok. coverage_fold(Fun, Acc, <>) -> IdFun = fun id/1, coverage_fold(Fun, Fun(IdFun(H), IdFun(Acc)), T); coverage_fold(Fun, Acc, <<>>) when is_function(Fun, 2) -> Acc. coverage_build(Acc0, <>, float) -> Float = id(<>), Acc = <>, coverage_build(Acc, T, float); coverage_build(Acc0, <>, Tuple0) -> Str = id(<>), Acc = id(<>), Tuple = setelement(2, setelement(3, Tuple0, 43), 42), if byte_size(Acc) > 0 -> coverage_build(Acc, T, Tuple) end; coverage_build(Acc, <<>>, Tuple) -> {Acc,Tuple}. coverage_bc(<>, Acc) -> B = << <> || C <- [H] >>, coverage_bc(T, [B|Acc]); coverage_bc(<<>>, Acc) -> Acc. coverage_setelement(<>, Tuple) when element(1, Tuple) =:= x -> setelement(H, Tuple, T1). coverage_apply(<>, [F|Fs]) -> [?MODULE:F(H)|coverage_apply(T, Fs)]; coverage_apply(<<>>, []) -> []. coverage_external(<>) -> ?MODULE:coverage_external_ignore(T, T), H. coverage_external_ignore(_, _) -> ok. coverage_id(I) -> id(I). do_coverage_bin_to_term_list(L) -> Bin = << <<(begin BinTerm = term_to_binary(Term), <<(byte_size(BinTerm)):32,BinTerm/binary>> end)/binary>> || Term <- L >>, L = do_coverage_bin_to_term_list_1(Bin), L = do_coverage_bin_to_term_list_1(<>), L = do_coverage_bin_to_term_list_1(<<7:32,"garbage",Bin/binary>>). do_coverage_bin_to_term_list_1(Bin) -> Res = coverage_bin_to_term_list(Bin), Res = coverage_bin_to_term_list(Bin, []), Res = coverage_bin_to_term_list_catch(Bin), Res = coverage_bin_to_term_list_catch(Bin, []). coverage_bin_to_term_list(<>) -> try binary_to_term(BinTerm) of Term -> [Term|coverage_bin_to_term_list(T)] catch error:badarg -> coverage_bin_to_term_list(T) end; coverage_bin_to_term_list(<<>>) -> []. coverage_bin_to_term_list(<>, Acc) -> try binary_to_term(BinTerm) of Term -> coverage_bin_to_term_list(T, [Term|Acc]) catch error:badarg -> coverage_bin_to_term_list(T, Acc) end; coverage_bin_to_term_list(<<>>, Acc) -> lists:reverse(Acc). coverage_bin_to_term_list_catch(<>) -> case catch binary_to_term(BinTerm) of {'EXIT',_} -> coverage_bin_to_term_list_catch(T); Term -> [Term|coverage_bin_to_term_list_catch(T)] end; coverage_bin_to_term_list_catch(<<>>) -> []. coverage_bin_to_term_list_catch(<>, Acc) -> case catch binary_to_term(BinTerm) of {'EXIT',_} -> coverage_bin_to_term_list_catch(T, Acc); Term -> coverage_bin_to_term_list_catch(T, [Term|Acc]) end; coverage_bin_to_term_list_catch(<<>>, Acc) -> lists:reverse(Acc). coverage_per_key(<> = B) -> true = (byte_size(B) =:= BinSize), Bin. coverage_bitstring(Bin) when is_binary(Bin) -> binary; coverage_bitstring(<<_/bitstring>>) -> bitstring; coverage_bitstring(_) -> other. coverage_trim(<> = Bin, E0, E1, E2, E3, Acc) -> case id(C > 128) of true -> coverage_trim(T, E0, E1, E2, E3, [C|Acc]); false -> {done,Bin,lists:reverse(Acc)} end. coverage_trim_1(<<>>, Right, OsType) -> do_coverage_trim_1(Right, OsType); coverage_trim_1(<<"/">>, Right, OsType) -> <<"/",(do_coverage_trim_1(Right, OsType))/binary>>; coverage_trim_1(Left, Right, OsType) -> <<(do_coverage_trim_1(Left, OsType))/binary, "/", (do_coverage_trim_1(Right, OsType))/binary>>. do_coverage_trim_1(A, OsType) -> <<"(",A/binary,"|",OsType/binary,")">>. coverage_trim_2(<> = Bin, I, L) -> case printable_char(C) of true -> coverage_trim_2(R, I - 1, [C | L]); false -> {I,Bin,lists:reverse(L)} end. printable_char($a) -> true; printable_char(_) -> false. multiple_uses(Config) when is_list(Config) -> {344,62879,345,<<245,159,1,89>>} = multiple_uses_1(<<1,88,245,159,1,89>>), true = multiple_uses_2(<<0,0,197,18>>), <<42,43>> = multiple_uses_3(<<0,0,42,43>>, fun id/1), ok = first_after(<<>>, 42), <<1>> = first_after(<<1,2,3>>, 0), <<2>> = first_after(<<1,2,3>>, 1), ok. multiple_uses_1(<>) -> %% NOT OPTIMIZED: sub binary is matched or used in more than one place {Y,Z} = multiple_uses_match(Tail), {X,Y,Z,Tail}. multiple_uses_2(<<_:16,Tail/binary>>) -> %% NOT OPTIMIZED: sub binary is matched or used in more than one place multiple_uses_cmp(Tail, Tail). multiple_uses_3(<<_:16,Tail/binary>>, Fun) -> %% NOT OPTIMIZED: sub binary is used or returned Fun(Tail). multiple_uses_match(<>) -> {Y,Z}. multiple_uses_cmp(<>, <>) -> true; multiple_uses_cmp(<<_:16>>, <<_:16>>) -> false. first_after(Data, Offset) -> case byte_size(Data) > Offset of false -> {_First, _Rest} = {ok, ok}, ok; true -> <<_:Offset/binary, Rest/binary>> = Data, %% 'Rest' saved in y(0) before the call. {First, _} = match_first(Data, Rest), %% When beam_bsm sees the code, the following line %% which uses y(0) has been optimized away. {First, Rest} = {First, Rest}, First end. match_first(_, <>) -> {First, Rest}. zero_label(Config) when is_list(Config) -> <<"nosemouth">> = read_pols(<<"FACE","nose","mouth">>), <<"CE">> = read_pols(<<"noFACE">>), ok. read_pols(Data) -> <> = Data, %% Intentional warning. (PolygonType == <<"FACE">>) or (PolygonType == <<"PTCH">>), Rest. followed_by_catch(Config) when is_list(Config) -> ok = handle(<<0,1,2,3,4,5>>). -record(rec,{field}). handle(<<>>) -> ok; handle(Msg) -> <<_DataLen:16, Rest/binary>> = Msg, case catch fooX:func() of [X] -> X#rec.field; _ -> ok end, handle(Rest). matching_meets_construction(Config) when is_list(Config) -> Bin = id(<<"abc">>), Len = id(2), Tail0 = id(<<1,2,3,4,5>>), <<_:Len/binary,Tail/binary>> = Tail0, Res = <>, <<3,4,5,"abc">> = Res, {'EXIT',{badarg,_}} = (catch matching_meets_construction_1(<<"Abc">>)), {'EXIT',{badarg,_}} = (catch matching_meets_construction_2(<<"Abc">>)), <<"Bbc">> = matching_meets_construction_3(<<"Abc">>), <<1,2>> = encode_octet_string(<<1,2,3>>, 2), ok. matching_meets_construction_1(<<"A",H/binary>>) -> <<"B",H>>. matching_meets_construction_2(<<"A",H/binary>>) -> <<"B",H/float>>. matching_meets_construction_3(<<"A",H/binary>>) -> <<"B",H/binary>>. encode_octet_string(<>, Len) -> <>. simon(Config) when is_list(Config) -> one = simon(blurf, <<>>), two = simon(0, <<42>>), fc(simon, [17,<<1>>], catch simon(17, <<1>>)), fc(simon, [0,<<1,2,3>>], catch simon(0, <<1,2,3>>)), one = simon2(blurf, <<9>>), two = simon2(0, <<9,1>>), fc(simon2, [0,<<9,10,11>>], catch simon2(0, <<9,10,11>>)), ok. simon(_, <<>>) -> one; simon(0, <<_>>) -> two. simon2(_, <<9>>) -> one; simon2(0, <<_:16>>) -> two. %% OTP-7113: Crash in v3_codegen. matching_and_andalso(Config) when is_list(Config) -> ok = matching_and_andalso_1(<<1,2,3>>, 3), {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, -8)), {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, blurf)), {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, 19)), {"abc",<<"xyz">>} = matching_and_andalso_23("abc", <<"-xyz">>), {"abc",<<"">>} = matching_and_andalso_23("abc", <<($a-1)>>), {"abc",<<"">>} = matching_and_andalso_23("abc", <<($z+1)>>), {"abc",<<"">>} = matching_and_andalso_23("abc", <<($A-1)>>), {"abc",<<"">>} = matching_and_andalso_23("abc", <<($Z+1)>>), error = matching_and_andalso_23([], <<>>), error = matching_and_andalso_23([], <<$A>>), error = matching_and_andalso_23([], <<$Z>>), error = matching_and_andalso_23([], <<$a>>), error = matching_and_andalso_23([], <<$z>>), ok. matching_and_andalso_1(<>, K) when is_integer(K) andalso size(Bitmap) >= K andalso 0 < K -> ok. matching_and_andalso_23(Datetime, Bin) -> Res = matching_and_andalso_2(Datetime, Bin), Res = matching_and_andalso_3(Datetime, Bin), Res. matching_and_andalso_2(Datetime, <>) when not ((H >= $a) andalso (H =< $z)) andalso not ((H >= $A) andalso (H =< $Z)) -> {Datetime,T}; matching_and_andalso_2(_, _) -> error. %% Contrived example to ensure we cover the handling of 'call' instructions %% in v3_codegen:bsm_rename_ctx/4. matching_and_andalso_3(Datetime, <>) when not ((abs(H) >= $a) andalso (abs(H) =< $z)) andalso not ((abs(H) >= $A) andalso (abs(H) =< $Z)) -> {Datetime,T}; matching_and_andalso_3(_, _) -> error. %% Thanks to Tomas Stejskal. otp_7188(Config) when is_list(Config) -> MP3 = <<84,65,71,68,117,154,105,232,107,121,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,68,97,110,105,101,108,32,76, 97,110,100,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66, 101,115,116,32,79,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,50,48,48,48,50,48,48,48,32,45,32,66,101,115, 116,32,79,102,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,12>>, {ok,{"ID3v1", [{title,<<68,117,154,105,232,107,121>>}, {artist,<<"Daniel Landa">>}, {album,<<"Best Of">>}]}} = parse_v1_or_v11_tag(MP3). parse_v1_or_v11_tag(<<"TAG", Title:30/binary, Artist:30/binary, Album:30/binary, _Year:4/binary, _Comment:28/binary, 0:8, Track:8, _Genre:8>>) -> {ok, {"ID3v1.1", [{track, Track}, {title, trim(Title)}, {artist, trim(Artist)}, {album, trim(Album)}]}}; parse_v1_or_v11_tag(<<"TAG", Title:30/binary, Artist:30/binary, Album:30/binary, _Year:4/binary, _Comment:30/binary, _Genre:8>>) -> {ok, {"ID3v1", [{title, trim(Title)}, {artist, trim(Artist)}, {album, trim(Album)}]}}; parse_v1_or_v11_tag(_) -> error. trim(Bin) -> list_to_binary(trim_blanks(binary_to_list(Bin))). trim_blanks(L) -> lists:reverse(skip_blanks_and_zero(lists:reverse(L))). skip_blanks_and_zero([$\s|T]) -> skip_blanks_and_zero(T); skip_blanks_and_zero([0|T]) -> skip_blanks_and_zero(T); skip_blanks_and_zero(L) -> L. %% OTP-7233. Record and binary matching optimizations clashed. %% Thanks to Vladimir Klebansky. -record(rec_otp_7233, {key, val}). otp_7233(Config) when is_list(Config) -> otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[{"xxxxxxxx",42}]}), [<<"XXabcde">>,{"xxxxxxxx",42}] = get(io_format), erase(io_format), otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[]}), undefined = get(io_format), ok. otp_7233_1(Rec) -> <> = Rec#rec_otp_7233.key, case K of <<"XX">> -> Value = Rec#rec_otp_7233.val, case lists:keyfind("xxxxxxxx", 1, Value) of false -> ok; T -> put(io_format, [Rec#rec_otp_7233.key,T]) end; _ -> ok end. otp_7240(Config) when is_list(Config) -> a = otp_7240_a(0, <<>>), b = otp_7240_a(1, 2), a = otp_7240_b(anything, <<>>), b = otp_7240_b(1, {x,y}), a = otp_7240_c(anything, <<>>), b = otp_7240_c(1, <<2>>), a = otp_7240_d(anything, <<>>), b = otp_7240_d(again, <<2>>), a = otp_7240_e(anything, <<>>), b = otp_7240_e(1, 41), a = otp_7240_f(anything, <<>>), b = otp_7240_f(1, {}), ok. otp_7240_a(_, <<>>) -> a; otp_7240_a(1, 2) -> b. otp_7240_b(_, <<>>) -> a; otp_7240_b(1, {_,_}) -> b. otp_7240_c(_, <<>>) -> a; otp_7240_c(1, <<2>>) -> b. otp_7240_d(_, <<>>) -> a; otp_7240_d(_, <<2>>) -> b. otp_7240_e(_, <<>>) -> a; otp_7240_e(1, B) when B < 42 -> b. otp_7240_f(_, <<>>) -> a; otp_7240_f(1, B) when is_tuple(B) -> b. otp_7498(Config) when is_list(Config) -> <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 0), <<2,3>> = otp_7498_foo(<<1,2,3>>, 1), <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 2), <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 0), <<2,3>> = otp_7498_bar(<<1,2,3>>, 1), <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 2), <<>> = otp_7498_bar(<<>>, 2), <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 3), ok. otp_7498_foo(Bin, 0) -> otp_7498_foo(Bin, 42); otp_7498_foo(<<_A, Rest/bitstring>>, 1) -> otp_7498_foo(Rest, 43); otp_7498_foo(Bin, _I) -> Bin. otp_7498_bar(Bin, 0) -> otp_7498_bar(Bin, 42); otp_7498_bar(<<_A, Rest/bitstring>>, 1) -> otp_7498_bar(Rest, 43); otp_7498_bar(<<>>, 2) -> otp_7498_bar(<<>>, 44); otp_7498_bar(Bin, _I) -> Bin. match_string(Config) when is_list(Config) -> %% To make sure that native endian really is handled correctly %% (i.e. that the compiler does not attempt to use bs_match_string/4 %% instructions for native segments), running this test is not enough. %% Either examine the generated for do_match_string_native/1 or %% check the coverage for the v3_kernel module. case erlang:system_info(endian) of little -> do_match_string_native(<<$a,0,$b,0>>); big -> do_match_string_native(<<0,$a,0,$b>>) end, do_match_string_big(<<0,$a,0,$b>>), do_match_string_little(<<$a,0,$b,0>>), do_match_string_big_signed(<<255,255>>), do_match_string_little_signed(<<255,255>>), plain = no_match_string_opt(<<"abc">>), strange = no_match_string_opt(<<$a:9,$b:9,$c:9>>), ok. do_match_string_native(<<$a:16/native,$b:16/native>>) -> ok. do_match_string_big(<<$a:16/big,$b:16/big>>) -> ok. do_match_string_little(<<$a:16/little,$b:16/little>>) -> ok. do_match_string_big_signed(<<(-1):16/signed>>) -> ok. do_match_string_little_signed(<<(-1):16/little-signed>>) -> ok. no_match_string_opt(<<"abc">>) -> plain; no_match_string_opt(<<$a:9,$b:9,$c:9>>) -> strange. %% OTP-7591: A zero-width segment in matching would crash the compiler. zero_width(Config) when is_list(Config) -> <> = <<2, 0, $h, $i, 0:0>>, 2 = Len, Str = <<"hi">>, %% Match sure that values that cannot fit in a segment will not match. case id(<<0:8>>) of <<256:8>> -> ct:fail(should_not_match); _ -> ok end, ok. %% OTP_7650: A invalid size for binary segments could crash the compiler. bad_size(Config) when is_list(Config) -> Tuple = {a,b,c}, {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Tuple>> = id(<<>>)), Binary = <<1,2,3>>, {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Binary>> = id(<<>>)), ok. haystack(Config) when is_list(Config) -> <<0:10/unit:8>> = haystack_1(<<0:10/unit:8>>), [<<0:10/unit:8>>, <<0:20/unit:8>>] = haystack_2(<<1:8192>>), ok. %% Used to crash the compiler. haystack_1(Haystack) -> Subs = [10], [begin <> = Haystack, B end || Y <- Subs], Haystack. %% There would be an incorrect badmatch exception. haystack_2(Haystack) -> Subs = [{687,10},{369,20}], [begin <<_:X/binary,B:Y/binary,_/binary>> = Haystack, B end || {X,Y} <- Subs ]. fc({'EXIT',{function_clause,_}}) -> ok; fc({'EXIT',{{case_clause,_},_}}) when ?MODULE =:= bs_match_inline_SUITE -> ok. fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Args,_}|_]}}) -> ok; fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Arity,_}|_]}}) when length(Args) =:= Arity -> true = test_server:is_native(?MODULE); fc(_, Args, {'EXIT',{{case_clause,ActualArgs},_}}) when ?MODULE =:= bs_match_inline_SUITE -> Args = tuple_to_list(ActualArgs). %% Cover the clause handling bs_context to binary in %% beam_block:initialized_regs/2. cover_beam_bool(Config) when is_list(Config) -> ok = do_cover_beam_bool(<<>>, 3), <<19>> = do_cover_beam_bool(<<19>>, 2), <<42>> = do_cover_beam_bool(<<42>>, 1), <<17>> = do_cover_beam_bool(<<13,17>>, 0), ok. do_cover_beam_bool(Bin, X) when X > 0 -> if X =:= 1; X =:= 2 -> Bin; true -> ok end; do_cover_beam_bool(<<_,Bin/binary>>, X) -> do_cover_beam_bool(Bin, X+1). matched_out_size(Config) when is_list(Config) -> {253,16#DEADBEEF} = mos_int(<<8,253,16#DEADBEEF:32>>), {6,16#BEEFDEAD} = mos_int(<<3,6:3,16#BEEFDEAD:32>>), {53,16#CAFEDEADBEEFCAFE} = mos_int(<<16,53:16,16#CAFEDEADBEEFCAFE:64>>), {23,16#CAFEDEADBEEFCAFE} = mos_int(<<5,23:5,16#CAFEDEADBEEFCAFE:64>>), {<<1,2,3>>,4} = mos_bin(<<3,1,2,3,4,3>>), {<<1,2,3,7>>,19,42} = mos_bin(<<4,1,2,3,7,19,4,42>>), <<1,2,3,7>> = mos_bin(<<4,1,2,3,7,"abcdefghij">>), ok. mos_int(<>) -> {I,X}; mos_int(<>) -> {I,X}. mos_bin(<>) -> L = byte_size(Bin), {Bin,X}; mos_bin(<>) -> L = byte_size(Bin), {Bin,X,Y}; mos_bin(<>) -> L = byte_size(Bin), Bin. follow_fail_branch(_) -> 42 = ffb_1(<<0,1>>, <<0>>), 8 = ffb_1(<<0,1>>, [a]), 42 = ffb_2(<<0,1>>, <<0>>, 17), 8 = ffb_2(<<0,1>>, [a], 0), ok. ffb_1(<<_,T/bitstring>>, List) -> case List of <<_>> -> 42; [_|_] -> %% The fail branch of the bs_start_match2 instruction %% pointing to here would be ignored, making the compiler %% incorrectly assume that the delayed sub-binary %% optimization was safe. bit_size(T) end. ffb_2(<<_,T/bitstring>>, List, A) -> case List of <<_>> when A =:= 17 -> 42; [_|_] -> bit_size(T) end. no_partition(_) -> one = no_partition_1(<<"string">>, a1), {two,<<"string">>} = no_partition_1(<<"string">>, a2), {two,<<>>} = no_partition_1(<<>>, a2), {two,a} = no_partition_1(a, a2), three = no_partition_1(undefined, a3), {four,a,[]} = no_partition_1([a], a4), {five,a,b} = no_partition_1({a,b}, a5), one = no_partition_2(<<"string">>, a1), two = no_partition_2(<<"string">>, a2), two = no_partition_2(<<>>, a2), two = no_partition_2(a, a2), three = no_partition_2(undefined, a3), four = no_partition_2(42, a4), five = no_partition_2([], a5), six = no_partition_2(42.0, a6), ok. no_partition_1(<<"string">>, a1) -> one; no_partition_1(V, a2) -> {two,V}; no_partition_1(undefined, a3) -> three; no_partition_1([H|T], a4) -> {four,H,T}; no_partition_1({A,B}, a5) -> {five,A,B}. no_partition_2(<<"string">>, a1) -> one; no_partition_2(_, a2) -> two; no_partition_2(undefined, a3) -> three; no_partition_2(42, a4) -> four; no_partition_2([], a5) -> five; no_partition_2(42.0, a6) -> six. calling_a_binary(Config) when is_list(Config) -> [] = call_binary(<<>>, []), {'EXIT',{badarg,_}} = (catch call_binary(<<1>>, [])), {'EXIT',{badarg,_}} = (catch call_binary(<<1,2,3>>, [])), ok. call_binary(<<>>, Acc) -> Acc; call_binary(<>, Acc) -> T(<>). binary_in_map(Config) when is_list(Config) -> ok = match_binary_in_map(#{key => <<42:8>>}), {'EXIT',{{badmatch,#{key := 1}},_}} = (catch match_binary_in_map(#{key => 1})), {'EXIT',{{badmatch,#{key := <<1023:16>>}},_}} = (catch match_binary_in_map(#{key => <<1023:16>>})), {'EXIT',{{badmatch,#{key := <<1:8>>}},_}} = (catch match_binary_in_map(#{key => <<1:8>>})), {'EXIT',{{badmatch,not_a_map},_}} = (catch match_binary_in_map(not_a_map)), ok. match_binary_in_map(Map) -> case 8 of N -> #{key := <<42:N>>} = Map, ok end. match_string_opt(Config) when is_list(Config) -> {x,<<1,2,3>>,{<<1>>,{v,<<1,2,3>>}}} = do_match_string_opt({<<1>>,{v,<<1,2,3>>}}), ok. do_match_string_opt({<<1>>,{v,V}}=T) -> {x,V,T}. select_on_integer(Config) when is_list(Config) -> 42 = do_select_on_integer(<<42>>), <<"abc">> = do_select_on_integer(<<128,"abc">>), {'EXIT',_} = (catch do_select_on_integer(<<0:1>>)), {'EXIT',_} = (catch do_select_on_integer(<<1:1>>)), {'EXIT',_} = (catch do_select_on_integer(<<0:1,0:15>>)), ok. %% The ASN.1 compiler frequently generates code like this. do_select_on_integer(<<0:1,I:7>>) -> I; do_select_on_integer(<<1:1,_:7,Bin/binary>>) -> Bin. %% If 'bin_opt_info' was given the warning would lack filename %% and line number. map_and_binary(_Config) -> {<<"10">>,<<"37">>,<<"am">>} = do_map_and_binary(<<"10:37am">>), Map1 = #{time => "noon"}, {ok,Map1} = do_map_and_binary(Map1), Map2 = #{hour => 8, min => 42}, {8,42,Map2} = do_map_and_binary(Map2), ok. do_map_and_binary(<>) -> {Hour, Min, Rest}; do_map_and_binary(#{time := _} = T) -> {ok, T}; do_map_and_binary(#{hour := Hour, min := Min} = T) -> {Hour, Min, T}. %% Unsafe caching of branch outcomes in beam_bsm would cause the %% delayed creation of sub-binaries optimization to be applied even %% when it was unsafe. unsafe_branch_caching(_Config) -> <<>> = do_unsafe_branch_caching(<<42,1>>), <<>> = do_unsafe_branch_caching(<<42,2>>), <<>> = do_unsafe_branch_caching(<<42,3>>), <<17,18>> = do_unsafe_branch_caching(<<42,3,17,18>>), <<>> = do_unsafe_branch_caching(<<1,3,42,2>>), ok. do_unsafe_branch_caching(<>) -> <> = Bin, case C1 of X when X =:= 1 orelse X =:= 2 -> Bin2 = <<>>; _ -> Bin2 = B1 end, case Code of 1 -> do_unsafe_branch_caching(Bin2); _ -> Bin2 end. bad_literals(_Config) -> Mod = list_to_atom(?MODULE_STRING ++ "_" ++ atom_to_list(?FUNCTION_NAME)), S = [signed_lit_match(V, Sz) || V <- lists:seq(-8, 8), Sz <- [0,1,2,3]] ++ [unsigned_lit_match(V, Sz) || V <- lists:seq(-2, 8), Sz <- [0,1,2]] ++ [unicode_match(V) || V <- [-100,-1,0,1,2|lists:seq(16#10FFFC, 16#110004)]], Code = ?Q(["-module('@Mod@').\n" "-export([f/0]).\n" "f() ->\n" "_@S,\n" "ok.\n"]), merl:print(Code), Opts = test_lib:opt_opts(?MODULE), {ok,_} = merl:compile_and_load(Code, Opts), Mod:f(), {'EXIT',<<42>>} = (catch bad_literals_1()), Sz = id(8), {'EXIT',{{badmatch,_},_}} = (catch <<-1:Sz>> = <<-1>>), ok. bad_literals_1() -> BadSz = bad, case case <<42>> of <<42:BadSz>> -> ok; Val -> exit(Val) end of ok -> ok; error -> error end. signed_lit_match(V, Sz) -> case <> of <> -> ?Q("<<_@V@:_@Sz@/signed>> = <<_@V@:_@Sz@>>"); _ -> ?Q(["case <<_@V@:_@Sz@>> of\n", " <<_@V@:_@Sz@/signed>> ->\n", " ct:fail(should_not_match);\n", " _ ->\n", " ok\n", "end\n"]) end. unsigned_lit_match(V, Sz) -> case <> of <> -> ?Q("<<_@V@:_@Sz@>> = <<_@V@:_@Sz@>>"); _ -> ?Q(["case <<_@V@:_@Sz@>> of\n", " <<_@V@:_@Sz@/unsigned>> ->\n", " ct:fail(should_not_match);\n", " _ ->\n", " ok\n", "end\n"]) end. unicode_match(V) -> try <> of <> -> ?Q(["<<_@V@/utf8>> = <<_@V@/utf8>>,\n", "<<_@V@/utf16>> = <<_@V@/utf16>>,\n", "<<_@V@/utf32>> = <<_@V@/utf32>>\n"]) catch error:badarg -> ?Q(["case <<_@V@:32>> of\n", " <<_@V@/utf32>> ->\n", " ct:fail(should_not_match);\n", " _ ->\n", " ok\n", "end\n"]) end. %% Test a few legal but rare cases. good_literals(_Config) -> Sz = id(64), %% Variable size. <<42:Sz>> = id(<<42:Sz>>), <<42.0:Sz/float>> = id(<<42:Sz/float>>), %% unit > 1 <<16#cafebeef:4/unit:8>> = id(<<16#cafebeef:32>>), ok. constant_propagation(_Config) -> <<5>> = constant_propagation_a(a, <<5>>), {'EXIT',{{case_clause,b},_}} = (catch constant_propagation_a(b, <<5>>)), 258 = constant_propagation_b(<<1,2>>), F = constant_propagation_c(), 259 = F(<<1,3>>), ok. constant_propagation_a(X, Y) -> case X of a -> Y2 = 8 end, <<5:Y2>> = Y. constant_propagation_b(B) -> Sz = 16, <> = B, X. constant_propagation_c() -> Size = 16, fun(Bin) -> <> = Bin, X end. parse_xml(_Config) -> <<"> = do_parse_xml(<<">), <<" ">> = do_parse_xml(<<">), ok. do_parse_xml(<<"> = Bytes) -> %% Delayed sub-binary creation is not safe. A buggy (development) %% version of check_liveness_everywhere() in beam_utils would turn %% on the optimization. Rest1 = case is_next_char_whitespace(Rest) of false -> Bytes; true -> id(Rest) end, id(Rest1). is_next_char_whitespace(<>) -> C =:= $\s. -record(ext_header, {this_hdr = 17, ext_hdr_opts}). get_payload(_Config) -> <<3445:48>> = do_get_payload(#ext_header{ext_hdr_opts = <<3445:48>>}), {'EXIT',_} = (catch do_get_payload(#ext_header{})), ok. do_get_payload(ExtHdr) -> _ = ExtHdr#ext_header.this_hdr, ExtHdrOptions = ExtHdr#ext_header.ext_hdr_opts, <<_:13,_:35>> = ExtHdr#ext_header.ext_hdr_opts, ExtHdrOptions. escape(_Config) -> 0 = escape(<<>>, 0), 1 = escape(<<128>>, 0), 2 = escape(<<128,255>>, 0), 42 = escape(<<42>>, 0), 50 = escape(<<42,8>>, 0), ok. escape(<>, Pos) when Byte >= 127 -> escape(Rest, Pos + 1); escape(<>, Pos) -> escape(Rest, Pos + Byte); escape(<<_Rest/bits>>, Pos) -> Pos. %% ERL-490 num_slots_different(_Config) -> Ts = [{<<"de">>, <<"default">>, <<"Remove">>, <<"a">>}, {<<"de">>, <<"default">>, <<"Remove from list">>, <<"a">>}, {<<"de">>, <<"default">>, <<"Remove from the list">>, <<"a">>}, {<<"de">>, <<"default">>, <<"Results">>, <<"Ergebnisse">>}, {<<"de">>, <<"default">>, <<"Reservatio">>, <<"a">>}, {<<"de">>, <<"navigation">>, <<"Results">>, <<"Ergebnisse">>}, {<<"de">>, <<"navigation">>, <<"Resources">>, <<"Ressourcen">>}], _ = [{ok,Res} = lgettext(A, B, C) || {A,B,C,Res} <- Ts], {'EXIT',_} = (catch lgettext(<<"d">>, <<"default">>, <<"Remove">>)), {'EXIT',_} = (catch lgettext("", <<"default">>, <<"Remove">>)), {'EXIT',_} = (catch lgettext(<<"de">>, <<"def">>, <<"Remove">>)), {'EXIT',_} = (catch lgettext(<<"de">>, <<"default">>, <<"Res">>)), ok. lgettext(<<"de">>, <<"default">>, <<"Remove">>) -> {ok, <<"a">>}; lgettext(<<"de">>, <<"default">>, <<"Remove from list">>) -> {ok, <<"a">>}; lgettext(<<"de">>, <<"default">>, <<"Remove from the list">>) -> {ok, <<"a">>}; lgettext(<<"de">>, <<"default">>, <<"Results">>) -> {ok, <<"Ergebnisse">>}; lgettext(<<"de">>, <<"default">>, <<"Reservatio">>) -> {ok, <<"a">>}; lgettext(<<"de">>, <<"navigation">>, <<"Results">>) -> {ok, <<"Ergebnisse">>}; lgettext(<<"de">>, <<"navigation">>, <<"Resources">>) -> {ok, <<"Ressourcen">>}. %% Test more code in beam_bsm. beam_bsm(_Config) -> true = check_bitstring_list(<<1:1,0:1,1:1,1:1>>, [1,0,1,1]), false = check_bitstring_list(<<1:1,0:1,1:1,1:1>>, [0]), true = bsm_validate_scheme(<<>>), true = bsm_validate_scheme(<<5,10>>), false = bsm_validate_scheme(<<5,10,11,12>>), true = bsm_validate_scheme([]), true = bsm_validate_scheme([5,10]), false = bsm_validate_scheme([5,6,7]), <<1,2,3>> = bsm_must_save_and_not_save(<<1,2,3>>, []), D = fun(N) -> 2*N end, [2,4|<<3>>] = bsm_must_save_and_not_save(<<1,2,3>>, [D,D]), ok. check_bitstring_list(<>, [H|T2]) -> check_bitstring_list(T1, T2); check_bitstring_list(<<>>, []) -> true; check_bitstring_list(_, _) -> false. bsm_validate_scheme([]) -> true; bsm_validate_scheme([H|T]) -> case bsm_is_scheme(H) of true -> bsm_validate_scheme(T); false -> false end; bsm_validate_scheme(<<>>) -> true; bsm_validate_scheme(<>) -> case bsm_is_scheme(H) of true -> bsm_validate_scheme(Rest); false -> false end. bsm_is_scheme(Int) -> Int rem 5 =:= 0. %% NOT OPTIMIZED: different control paths use different positions in the binary bsm_must_save_and_not_save(Bin, []) -> Bin; bsm_must_save_and_not_save(<>, [F|Fs]) -> [F(H)|bsm_must_save_and_not_save(T, Fs)]; bsm_must_save_and_not_save(<<>>, []) -> []. guard(_Config) -> _Tuple = id({a,b}), ok = guard_1(<<1,2,3>>, {1,2,3}), ok = guard_2(<<42>>, #{}), ok. %% Cover handling of #k_put{} in v3_codegen:bsm_rename_ctx/4. guard_1(<>, Tuple) when Tuple =:= {A,B,C} -> ok. %% Cover handling of #k_call{} in v3_codegen:bsm_rename_ctx/4. guard_2(<<_>>, Healing) when Healing#{[] => Healing} =:= #{[] => #{}} -> ok. is_ascii(_Config) -> true = do_is_ascii(<<>>), true = do_is_ascii(<<"string">>), false = do_is_ascii(<<1024/utf8>>), {'EXIT',{function_clause,_}} = (catch do_is_ascii(<<$A,0:3>>)), {'EXIT',{function_clause,_}} = (catch do_is_ascii(<<16#80,0:3>>)), ok. do_is_ascii(<<>>) -> true; do_is_ascii(<>) when C >= 16#80 -> %% This clause must fail to match if the size of the argument in %% bits is not divisible by 8. Beware of unsafe optimizations. false; do_is_ascii(<<_, T/binary>>) -> do_is_ascii(T). non_opt_eq(_Config) -> true = non_opt_eq([], <<>>), true = non_opt_eq([$a], <<$a>>), false = non_opt_eq([$a], <<$b>>), ok. %% An example from the Efficiency Guide. It used to be not optimized, %% but now it can be optimized. non_opt_eq([H|T1], <>) -> non_opt_eq(T1, T2); non_opt_eq([_|_], <<_,_/binary>>) -> false; non_opt_eq([], <<>>) -> true. %% ERL-689 erl_689(_Config) -> {{0, 0, 0}, <<>>} = do_erl_689_1(<<0>>, ?MODULE), {{2018, 8, 7}, <<>>} = do_erl_689_1(<<4,2018:16/little,8,7>>, ?MODULE), {{0, 0, 0}, <<>>} = do_erl_689_2(?MODULE, <<0>>), {{2018, 8, 7}, <<>>} = do_erl_689_2(?MODULE, <<4,2018:16/little,8,7>>), ok. do_erl_689_1(Arg1, Arg2) -> Res = do_erl_689_1a(Arg1, Arg2), Res = do_erl_689_1b(Arg1, Arg2). do_erl_689_2(Arg1, Arg2) -> Res = do_erl_689_2a(Arg1, Arg2), Res = do_erl_689_2b(Arg1, Arg2). do_erl_689_1a(<>, _) -> case {Data, Length} of {_, 0} -> %% bs_context_to_binary would incorrectly set Data to the original %% binary (before matching in the function head). {{0, 0, 0}, Data}; {<>, 4} -> {{Y, M, D}, Rest} end. do_erl_689_1b(<>, _) -> case {Data, Length} of {_, 0} -> %% bs_context_to_binary would incorrectly set Data to the original %% binary (before matching in the function head). id(0), {{0, 0, 0}, Data}; {<>, 4} -> id(1), {{Y, M, D}, Rest} end. do_erl_689_2a(_, <>) -> case {Length, Data} of {0, _} -> %% bs_context_to_binary would incorrectly set Data to the original %% binary (before matching in the function head). {{0, 0, 0}, Data}; {4, <>} -> {{Y, M, D}, Rest} end. do_erl_689_2b(_, <>) -> case {Length, Data} of {0, _} -> %% bs_context_to_binary would incorrectly set Data to the original %% binary (before matching in the function head). id(0), {{0, 0, 0}, Data}; {4, <>} -> id(1), {{Y, M, D}, Rest} end. %% ERL-753 bs_start_match2_defs(_Config) -> {<<"http://127.0.0.1:1234/vsaas/hello">>} = api_url(<<"hello">>), {"https://127.0.0.1:4321/vsaas/hello"} = api_url({https, "hello"}). api_url(URL) -> case URL of <<_/binary>> -> {<<"http://127.0.0.1:1234/vsaas/",URL/binary>>}; {https, [_|_] = URL1} -> {"https://127.0.0.1:4321/vsaas/"++URL1} end. check(F, R) -> R = F(). %% Make sure that an expression that comes between function start and a match %% expression passes validation. expression_before_match(Config) when is_list(Config) -> <<_,R/binary>> = id(<<0,1,2,3>>), {1, <<2,3>>} = expression_before_match_1(R), ok. expression_before_match_1(R) -> A = id(1), case R of <<1,Bar/binary>> -> {A, Bar}; <<>> -> {A, baz} end. %% Make sure that context positions are updated on calls. restore_on_call(Config) when is_list(Config) -> ok = restore_on_call_plain(<<0, 1, 2>>), <<"x">> = restore_on_call_match(<<0, "x">>), ok. restore_on_call_plain(<<0, Rest/binary>>) -> <<2>> = restore_on_call_plain_1(Rest), %% {badmatch, <<>>} on missing restore. <<2>> = restore_on_call_plain_1(Rest), ok. restore_on_call_plain_1(<<1, Rest/binary>>) -> Rest; restore_on_call_plain_1(Other) -> Other. %% Calls a function that moves the match context passed to it, and then matches %% on its result to confuse the reposition algorithm's success/fail logic. restore_on_call_match(<<0, Bin/binary>>) -> case skip_until_zero(Bin) of {skipped, Rest} -> Rest; not_found -> %% The match context did not get repositioned before the %% bs_get_tail instruction here. Bin end. skip_until_zero(<<0,Rest/binary>>) -> {skipped, Rest}; skip_until_zero(<<_C,Rest/binary>>) -> skip_until_zero(Rest); skip_until_zero(_) -> not_found. %% 'catch' must invalidate positions. restore_after_catch(Config) when is_list(Config) -> <<0, 1>> = restore_after_catch_1(<<0, 1>>), ok. restore_after_catch_1(<>) -> try throw_after_byte(A) of _ -> impossible catch throw:_Any -> %% Will equal <<1>> if the bug is present. A end. throw_after_byte(<<_,_/binary>>) -> throw(away). matches_on_parameter(Config) when is_list(Config) -> %% This improves coverage for matching on "naked" parameters. {<<"urka">>, <<"a">>} = matches_on_parameter_1(<<"gurka">>), ok = (catch matches_on_parameter_2(<<"10001110101">>, 0)). matches_on_parameter_1(Bin) -> <<"g", A/binary>> = Bin, <<_,_,"rk", B/binary>> = Bin, {A, B}. matches_on_parameter_2(Bin, Offset) -> <<_:Offset, Bit:1, Rest/bits>> = Bin, case bit_size(Rest) of 0 -> throw(ok); _ -> [Bit | matches_on_parameter_2(Bin, Offset + 1)] end. big_positions(Config) when is_list(Config) -> %% This provides coverage for when match context positions no longer fit %% into an immediate on 32-bit platforms. A = <<0:((1 bsl 27) - 8), $A, 1:1, "gurka", $A>>, B = <<0:((1 bsl 27) - 8), $B, "hello", $B>>, {a,$A} = bp_start_match(A), {b,$B} = bp_start_match(B), {a,$A} = bp_getpos(A), {b,$B} = bp_getpos(B), ok. %% After the first iteration the context's position will no longer fit into an %% immediate. To improve performance the bs_start_match3 instruction will %% return a new context with an updated base position so that we won't have to %% resort to using bigints. bp_start_match(<<_:(1 bsl 27),T/bits>>) -> bp_start_match(T); bp_start_match(<<1:1,"gurka",A>>) -> {a,A}; bp_start_match(<<"hello",B>>) -> {b,B}. %% This is a corner case where the above didn't work perfectly; if the position %% was _just_ small enough to fit into an immediate when bs_start_match3 was %% hit, but too large at bs_get_position, then it must be saved as a bigint. bp_getpos(<<_:((1 bsl 27) - 8),T/bits>>) -> bp_getpos(T); bp_getpos(<>) -> {a,A}; bp_getpos(<>) -> {b,B}. matching_meets_apply(_Config) -> <<"abc">> = do_matching_meets_apply(<<"/abc">>, []), 42 = do_matching_meets_apply(<<"">>, {erlang,-42}), 100 = do_matching_meets_apply(no_binary, {erlang,-100}), ok. do_matching_meets_apply(<<$/, Rest/binary>>, _Handler) -> id(Rest); do_matching_meets_apply(<<_/binary>>=Name, never_matches_a) -> %% Used to crash the compiler because variables in a remote %% were not handled properly by beam_ssa_bsm. Name:foo(gurka); do_matching_meets_apply(<<_/binary>>=Name, never_matches_b) -> %% Another case of the above. foo:Name(gurka); do_matching_meets_apply(_Bin, {Handler, State}) -> %% Another case of the above. Handler:abs(State). id(I) -> I.