%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1999-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(beam_literals_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). -export([putting/1, matching_smalls/1, matching_smalls_jt/1, matching_bigs/1, matching_more_bigs/1, matching_bigs_and_smalls/1, badmatch/1, case_clause/1, receiving/1, literal_type_tests/1, put_list/1, fconv/1, literal_case_expression/1, increment/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [putting, matching_smalls, matching_smalls_jt, matching_bigs, matching_more_bigs, matching_bigs_and_smalls, badmatch, case_clause, receiving, literal_type_tests, put_list, fconv, literal_case_expression, increment]. groups() -> []. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. %% Test creating lists and tuples containing big number literals. putting(Config) when is_list(Config) -> -773973888575883407313908 = chksum(putting1(8987697898797)). putting1(X) -> {8797987987987987872256443, [1324483773773], {3.1415, 2.71, [2.5, 35.125|9.31]}, [X|349873987387373], [329878349873|-387394729872], -773973937933873929749873}. %% Test matching of a few big number literals (in Beam select_val/3 will NOT be used). matching_bigs(Config) when is_list(Config) -> a = matching1(3972907842873739), b = matching1(-389789298378939783333333333333333333784), other = matching1(3141699999999999999999999999999999999), other = matching1(42). %% Test matching small numbers (both positive and negative). matching_smalls(Config) when is_list(Config) -> a = m_small(-42), b = m_small(0), c = m_small(105), d = m_small(-13), e = m_small(337848), other = m_small(324), other = m_small(-7), ok. m_small(-42) -> a; m_small(0) -> b; m_small(105) -> c; m_small(-13) -> d; m_small(337848) -> e; m_small(_) -> other. %% Test matching small numbers (both positive and negative). %% Make sure that a jump table is used. matching_smalls_jt(Config) when is_list(Config) -> a = m_small_jt(-2), b = m_small_jt(-1), c = m_small_jt(0), d = m_small_jt(2), e = m_small_jt(3), other = m_small(324), other = m_small(-7), ok. m_small_jt(-2) -> a; m_small_jt(-1) -> b; m_small_jt(0) -> c; m_small_jt(2) -> d; m_small_jt(3) -> e; m_small_jt(_) -> other. %% Big numbers, no select_val. matching1(3972907842873739) -> a; matching1(-389789298378939783333333333333333333784) -> b; matching1(_) -> other. %% Test matching of a big number literals (in Beam, a select_val/3 instruction will be used) matching_more_bigs(Config) when is_list(Config) -> a = matching2(-999766349740978337), b = matching2(9734097866575478), c = matching2(-966394677364879734), d = matching2(13987294872948990), e = matching2(777723896192459245), other = matching2(7), other = matching2(39789827988888888888888888888347474444444444444444444). %% Big numbers with select_val. matching2(-999766349740978337) -> a; matching2(9734097866575478) -> b; matching2(-966394677364879734) -> c; matching2(13987294872948990) -> d; matching2(777723896192459245) -> e; matching2(_) -> other. %% Test matching of a mix of big numbers and literals. matching_bigs_and_smalls(Config) when is_list(Config) -> a = matching3(38472928723987239873873), b = matching3(0), c = matching3(-3873973932710954671207461057614287561348756348743634876436784367873), d = matching3(3978429867297393873), e = matching3(42), f = matching3(-4533), other = matching3(77), other = matching3(39274120984379249874219748). %% Mixed small and big. matching3(38472928723987239873873) -> a; matching3(0) -> b; matching3(-3873973932710954671207461057614287561348756348743634876436784367873) -> c; matching3(3978429867297393873) -> d; matching3(42) -> e; matching3(-4533) -> f; matching3(_) -> other. %% Test literal badmatches with big number and floats. badmatch(Config) when is_list(Config) -> %% We are satisfied if we can load this module and run it. Big = id(32984798729847892498297824872982972978239874), Float = id(3.1415927), catch a = Big, catch b = Float, {'EXIT',{{badmatch,3879373498378993387},_}} = (catch c = 3879373498378993387), {'EXIT',{{badmatch,7.0},_}} = (catch d = 7.0), case Big of Big -> ok end, case Float of Float -> ok end, ok. case_clause(Config) when is_list(Config) -> {'EXIT',{{case_clause,337.0},_}} = (catch case_clause_float()), {'EXIT',{{try_clause,42.0},_}} = (catch try_case_clause_float()), {'EXIT',{{case_clause,37932749837839747383847398743789348734987},_}} = (catch case_clause_big()), {'EXIT',{{try_clause,977387349872349870423364354398566348},_}} = (catch try_case_clause_big()), ok. case_clause_float() -> case 337.0 of blurf -> ok end. try_case_clause_float() -> try 42.0 of blurf -> ok catch _:_ -> error end. case_clause_big() -> case 37932749837839747383847398743789348734987 of blurf -> ok end. try_case_clause_big() -> try 977387349872349870423364354398566348 of blurf -> ok catch _:_ -> error end. %% Test receive with a big number literal (more than 27 bits, less than 32 bits). receiving(Config) when is_list(Config) -> Self = self(), spawn(fun() -> Self ! here_is_a_message end), ok = receive here_is_a_message -> ok after 16#f1234567 -> timeout end. %% Test type tests on literal values. literal_type_tests(Config) when is_list(Config) -> %% Generate an Erlang module with all different type of type tests. Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]), Mod = literal_test, Anno = erl_anno:new(0), Func = {function, Anno, test, 0, [{clause,Anno,[],[],Tests}]}, Form = [{attribute,Anno,module,Mod}, {attribute,Anno,compile,export_all}, Func, {eof,Anno}], %% Print generated code for inspection. lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form), %% Test compile:form/1. This implies full optimization (default). {ok,Mod,Code1} = compile:forms(Form), {module,Mod} = code:load_binary(Mod, Mod, Code1), Mod:test(), true = code:delete(Mod), code:purge(Mod), %% Test compile:form/2. Turn off all optimizations. {ok,Mod,Code2} = compile:forms(Form, [binary,report,time, no_copt,no_postopt]), {module,Mod} = code:load_binary(Mod, Mod, Code2), Mod:test(), true = code:delete(Mod), code:purge(Mod), ok. make_test([{is_function=T,L}|Ts]) -> [guard_test(T, L),guard_test(T, 0, L),body_test(T, L),body_test(T, 0, L)|make_test(Ts)]; make_test([{T,L}|Ts]) -> [guard_test(T, L),body_test(T, L)|make_test(Ts)]; make_test([]) -> []. guard_test(_, L) when is_function(L) -> %% Skip guard tests with exports - they are not literals {atom,erl_anno:new(0),true}; guard_test(T, L) -> S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p}]), if ~w(~w) -> true; true -> false end end. ", [T, L, T, L]), {Val,Expr} = eval_string(S), Anno = erl_anno:new(0), {match,Anno,{atom,Anno,Val},Expr}. guard_test(_, _, L) when is_function(L) -> %% Skip guard tests with exports - they are not literals {atom,erl_anno:new(0),true}; guard_test(T, A, L) -> S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ", [T,L,A,T,L,A]), {Val,Expr} = eval_string(S), Anno = erl_anno:new(0), {match,Anno,{atom,Anno,Val},Expr}. body_test(T, L) -> S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p}]), ~w(~w) end. ", [T,L,T,L]), {Val,Expr} = eval_string(S), Anno = erl_anno:new(0), {match,Anno,{atom,Anno,Val},Expr}. body_test(T, A, L) -> S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), ~w(~w,~w) end. ", [T,L,A,T,L,A]), {Val,Expr} = eval_string(S), Anno = erl_anno:new(0), {match,Anno,{atom,Anno,Val},Expr}. eval_string(S) -> {ok,Toks,_Line} = erl_scan:string(lists:flatten(S)), {ok,E} = erl_parse:parse_exprs(Toks), {value,Val,_Bs} = erl_eval:exprs(E, []), {Val,hd(E)}. literals() -> [42, 3.14, -3, 32982724987789283473473838474, [], "abc", <<"abc">>, {}, xxxx, fun erlang:erase/0]. type_tests() -> [is_boolean, is_integer, is_float, is_number, is_atom, is_list, is_tuple, is_pid, is_reference, is_port, is_binary, is_function]. put_list(Config) when is_list(Config) -> %% put_list x0 Literal Reg [Config|8739757395764] = put_list_rqr(Config), {[Config|7779757395764],Config} = put_list_rqx(Config), [Config|98765432100000] = put_list_rqy(Config), %% put_list x Literal Reg [Config|16#FFFFF77777137483769] = put_list_xqr(ignore, Config), {[Config|16#AAAAAFFFFF77777],{a,b},Config} = put_list_xqx({a,b}, Config), [Config|12777765432979879] = put_list_xqy(ignore, Config), %% put_list y Literal Reg [Config|17424134793676869867] = put_list_yqr(Config), {[Config|77424134793676869867],Config} = put_list_yqx(Config), {Config,[Config|16#BCDEFF4241676869867]} = put_list_yqy(Config), %% put_list Literal x0 Reg [42.0|Config] = put_list_qrr(Config), [Config,42.0|Config] = put_list_qrx(Config), [100.0|Config] = put_list_qry(Config), %% put_list Literal x1 Reg [127.0|Config] = put_list_qxr({ignore,me}, Config), [Config,130.0|Config] = put_list_qxx(ignore, Config), [99.0|Config] = put_list_qxy(Config), %% put_list Literal y0 Reg [200.0|Config] = put_list_qyr(Config), [Config,210.0|Config] = put_list_qyx(Config), [[300.0|Config]|Config] = put_list_qyy(Config), ok. %% put_list x0 Literal x0 put_list_rqr(Config) -> [Config|8739757395764]. %% put_list x0 Literal x1 put_list_rqx(Config) -> {[Config|7779757395764],Config}. %% put_list x0 Literal y0 put_list_rqy(Config) -> Res = [Config|98765432100000], id(42), Res. %% put_list x1 Literal x0 put_list_xqr(_, Config) -> [Config|16#FFFFF77777137483769]. %% put_list x1 Literal x2 put_list_xqx(A, Config) -> {[Config|16#AAAAAFFFFF77777],A,Config}. %% put_list x1 Literal y0 put_list_xqy(_, Config) -> Res = [Config|12777765432979879], id(42), Res. %% put_list y0 Literal x0 put_list_yqr(Config) -> id(Config), [Config|17424134793676869867]. %% put_list y0 Literal x1 put_list_yqx(Config) -> id(Config), {[Config|77424134793676869867],Config}. %% put_list y1 Literal y0 put_list_yqy(Config) -> id(Config), Res = [Config|16#BCDEFF4241676869867], id(Config), {Config,Res}. %% put_list Literal x0 x0 put_list_qrr(Config) -> [42.0|Config]. %% put_list Literal x0 x1 put_list_qrx(Config) -> [Config,42.0|Config]. %% put_list Literal x0 y0 put_list_qry(Config) -> Res = [100.0|Config], id(0), Res. %% put_list Literal x1 x0 put_list_qxr(_, Config) -> [127.0|Config]. %% put_list Literal x1 x2 put_list_qxx(_, Config) -> [Config,130.0|Config]. %% put_list Literal x1 y0 put_list_qxy(Config) -> Res = [99.0|Config], id(0), Res. %% put_list Literal y0 x0 put_list_qyr(Config) -> id(Config), [200.0|Config]. %% put_list Literal y0 x1 put_list_qyx(Config) -> id(Config), [Config,210.0|Config]. %% put_list Literal y1 y0 put_list_qyy(Config) -> id(Config), Res = [300.0|Config], id(Config), [Res|Config]. fconv(Config) when is_list(Config) -> 5.0 = fconv_1(-34444444450.0), 13.0 = fconv_2(7.0), ok. fconv_1(F) when is_float(F) -> 34444444455 + F. fconv_2(F) when is_float(F) -> 6.0 + F. literal_case_expression(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), Src = filename:join(DataDir, "literal_case_expression"), {ok,literal_case_expression=Mod,Code} = compile:file(Src, [from_asm,binary]), {module,Mod} = code:load_binary(Mod, Src, Code), ok = Mod:x(), ok = Mod:y(), ok = Mod:zi1(), ok = Mod:zi2(), ok = Mod:za1(), ok = Mod:za2(), true = code:delete(Mod), code:purge(Mod), ok. %% Test the i_increment instruction. increment(Config) when is_list(Config) -> %% In the 32-bit emulator, Neg32 can be represented as a small, %% but -Neg32 cannot. Therefore the i_increment instruction must %% not be used in the subtraction that follows (since i_increment %% cannot handle a bignum literal). Neg32 = -(1 bsl 27), Big32 = id(1 bsl 32), Result32 = (1 bsl 32) + (1 bsl 27), Result32 = Big32 + (1 bsl 27), Result32 = Big32 - Neg32, %% Same thing, but for the 64-bit emulator. Neg64 = -(1 bsl 59), Big64 = id(1 bsl 64), Result64 = (1 bsl 64) + (1 bsl 59), Result64 = Big64 + (1 bsl 59), Result64 = Big64 - Neg64, %% Test error handling for the i_increment instruction. Bad = id(bad), {'EXIT',{badarith,_}} = (catch Bad + 42), %% Small operands, but a big result. Res32 = 1 bsl 27, Small32 = id(Res32-1), Res32 = Small32 + 1, Res64 = 1 bsl 59, Small64 = id(Res64-1), Res64 = Small64 + 1, ok. %% Help functions. chksum(Term) -> chksum(Term, 0). chksum([List|T], Sum) when is_list(List) -> chksum(T, chksum(List, Sum)); chksum([H|T], Sum) -> chksum(T, chksum(H, Sum)); chksum([], Sum) -> Sum; chksum(Tuple, Sum) when is_tuple(Tuple) -> chksum(tuple_to_list(Tuple), Sum); chksum(Int, Sum) when is_integer(Int) -> Sum * 5 + Int; chksum(Other, Sum) -> erlang:phash2([Other|Sum], 39729747). id(I) -> I.