%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-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(erl_eval_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2, init_per_group/2,end_per_group/2]). -export([guard_1/1, guard_2/1, match_pattern/1, match_bin/1, string_plusplus/1, pattern_expr/1, guard_3/1, guard_4/1, guard_5/1, lc/1, simple_cases/1, unary_plus/1, apply_atom/1, otp_5269/1, otp_6539/1, otp_6543/1, otp_6787/1, otp_6977/1, otp_7550/1, otp_8133/1, otp_10622/1, otp_13228/1, otp_14826/1, funs/1, try_catch/1, eval_expr_5/1, zero_width/1, eep37/1, eep43/1, otp_15035/1]). %% %% Define to run outside of test server %% %%-define(STANDALONE,1). -import(lists,[concat/1, sort/1]). -export([count_down/2, count_down_fun/0, do_apply/2, local_func/3, local_func_value/2]). -export([simple/0]). -ifdef(STANDALONE). -define(config(A,B),config(A,B)). -export([config/2]). -define(line, noop, ). config(priv_dir,_) -> ".". -else. -include_lib("common_test/include/ct.hrl"). -endif. init_per_testcase(_Case, Config) -> Config. end_per_testcase(_Case, _Config) -> ok. suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. all() -> [guard_1, guard_2, match_pattern, string_plusplus, pattern_expr, match_bin, guard_3, guard_4, guard_5, lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, otp_6787, otp_6977, otp_7550, otp_8133, otp_10622, otp_13228, otp_14826, funs, try_catch, eval_expr_5, zero_width, eep37, eep43, otp_15035]. groups() -> []. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. %% OTP-2405 guard_1(Config) when is_list(Config) -> {ok,Tokens ,_} = erl_scan:string("if a+4 == 4 -> yes; true -> no end. "), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), no = guard_1_compiled(), {value, no, []} = erl_eval:expr(Expr, []), ok. guard_1_compiled() -> if a+4 == 4 -> yes; true -> no end. %% Similar to guard_1, but type-correct. guard_2(Config) when is_list(Config) -> {ok,Tokens ,_} = erl_scan:string("if 6+4 == 4 -> yes; true -> no end. "), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), no = guard_2_compiled(), {value, no, []} = erl_eval:expr(Expr, []), ok. guard_2_compiled() -> if 6+4 == 4 -> yes; true -> no end. %% OTP-3069: syntactic sugar string ++ ... string_plusplus(Config) when is_list(Config) -> check(fun() -> case "abc" of "ab" ++ L -> L end end, "case \"abc\" of \"ab\" ++ L -> L end. ", "c"), check(fun() -> case "abcde" of "ab" ++ "cd" ++ L -> L end end, "case \"abcde\" of \"ab\" ++ \"cd\" ++ L -> L end. ", "e"), check(fun() -> case "abc" of [97, 98] ++ L -> L end end, "case \"abc\" of [97, 98] ++ L -> L end. ", "c"), ok. %% OTP-2983: match operator in pattern. match_pattern(Config) when is_list(Config) -> check(fun() -> case {a, b} of {a, _X}=Y -> {x,Y} end end, "case {a, b} of {a, X}=Y -> {x,Y} end. ", {x, {a, b}}), check(fun() -> case {a, b} of Y={a, _X} -> {x,Y} end end, "case {a, b} of Y={a, X} -> {x,Y} end. ", {x, {a, b}}), check(fun() -> case {a, b} of Y={a, _X}=Z -> {Z,Y} end end, "case {a, b} of Y={a, X}=Z -> {Z,Y} end. ", {{a, b}, {a, b}}), check(fun() -> A = 4, B = 28, <<13:(A+(X=B))>>, X end, "begin A = 4, B = 28, <<13:(A+(X=B))>>, X end.", 28), ok. %% Binary match problems. match_bin(Config) when is_list(Config) -> check(fun() -> <<"abc">> = <<"abc">> end, "<<\"abc\">> = <<\"abc\">>. ", <<"abc">>), check(fun() -> <> = <<2,"AB","CD">>, {Size,B,Rest} end, "begin <> = <<2,\"AB\",\"CD\">>, " "{Size,B,Rest} end. ", {2,<<"AB">>,<<"CD">>}), ok. %% OTP-3144: compile-time expressions in pattern. pattern_expr(Config) when is_list(Config) -> check(fun() -> case 4 of 2+2 -> ok end end, "case 4 of 2+2 -> ok end. ", ok), check(fun() -> case 2 of +2 -> ok end end, "case 2 of +2 -> ok end. ", ok), ok. %% OTP-4518. guard_3(Config) when is_list(Config) -> check(fun() -> if false -> false; true -> true end end, "if false -> false; true -> true end.", true), check(fun() -> if <<"hej">> == <<"hopp">> -> true; true -> false end end, "begin if <<\"hej\">> == <<\"hopp\">> -> true; true -> false end end.", false), check(fun() -> if <<"hej">> == <<"hej">> -> true; true -> false end end, "begin if <<\"hej\">> == <<\"hej\">> -> true; true -> false end end.", true), ok. %% OTP-4885. guard_4(Config) when is_list(Config) -> check(fun() -> if erlang:'+'(3,a) -> true ; true -> false end end, "if erlang:'+'(3,a) -> true ; true -> false end.", false), check(fun() -> if erlang:is_integer(3) -> true ; true -> false end end, "if erlang:is_integer(3) -> true ; true -> false end.", true), check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end, "[X || X <- [1,2,3], erlang:is_integer(X)].", [1,2,3]), check(fun() -> if is_atom(is_integer(a)) -> true ; true -> false end end, "if is_atom(is_integer(a)) -> true ; true -> false end.", true), check(fun() -> if erlang:is_atom(erlang:is_integer(a)) -> true; true -> false end end, "if erlang:is_atom(erlang:is_integer(a)) -> true; " "true -> false end.", true), check(fun() -> if is_atom(3+a) -> true ; true -> false end end, "if is_atom(3+a) -> true ; true -> false end.", false), check(fun() -> if erlang:is_atom(3+a) -> true ; true -> false end end, "if erlang:is_atom(3+a) -> true ; true -> false end.", false), ok. %% Guards with erlang:'=='/2. guard_5(Config) when is_list(Config) -> {ok,Tokens ,_} = erl_scan:string("case 1 of A when erlang:'=='(A, 1) -> true end."), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), true = guard_5_compiled(), {value, true, [{'A',1}]} = erl_eval:expr(Expr, []), ok. guard_5_compiled() -> case 1 of A when erlang:'=='(A, 1) -> true end. %% OTP-4518. lc(Config) when is_list(Config) -> check(fun() -> X = 32, [X || X <- [1,2,3]] end, "begin X = 32, [X || X <- [1,2,3]] end.", [1,2,3]), check(fun() -> X = 32, [X || <> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end, %% "binsize variable" ^ "begin X = 32, [X || <> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end.", [1,2]), check(fun() -> Y = 13,[X || {X,Y} <- [{1,2}]] end, "begin Y = 13,[X || {X,Y} <- [{1,2}]] end.", [1]), error_check("begin [A || X <- [{1,2}], 1 == A] end.", {unbound_var,'A'}), error_check("begin X = 32, [{Y,W} || X <- [1,2,32,Y=4], Z <- [1,2,W=3]] end.", {unbound_var,'Y'}), error_check("begin X = 32,<> = <<100:X>> end.", {unbound_var,'B'}), check(fun() -> [X || X <- [1,2,3,4], not (X < 2)] end, "begin [X || X <- [1,2,3,4], not (X < 2)] end.", [2,3,4]), check(fun() -> [X || X <- [true,false], X] end, "[X || X <- [true,false], X].", [true]), ok. %% Simple cases, just to cover some code. simple_cases(Config) when is_list(Config) -> check(fun() -> A = $C end, "A = $C.", $C), %% check(fun() -> A = 3.14 end, "A = 3.14.", 3.14), check(fun() -> self() ! a, A = receive a -> true end end, "begin self() ! a, A = receive a -> true end end.", true), check(fun() -> c:flush(), self() ! a, self() ! b, self() ! c, receive b -> b end, {messages, [a,c]} = erlang:process_info(self(), messages), c:flush() end, "begin c:flush(), self() ! a, self() ! b, self() ! c," "receive b -> b end," "{messages, [a,c]} =" " erlang:process_info(self(), messages), c:flush() end.", ok), check(fun() -> self() ! a, A = receive a -> true after 0 -> false end end, "begin self() ! a, A = receive a -> true" " after 0 -> false end end.", true), check(fun() -> c:flush(), self() ! a, self() ! b, self() ! c, receive b -> b after 0 -> true end, {messages, [a,c]} = erlang:process_info(self(), messages), c:flush() end, "begin c:flush(), self() ! a, self() ! b, self() ! c," "receive b -> b after 0 -> true end," "{messages, [a,c]} =" " erlang:process_info(self(), messages), c:flush() end.", ok), check(fun() -> receive _ -> true after 10 -> false end end, "receive _ -> true after 10 -> false end.", false), check(fun() -> F = fun(A) -> A end, true = 3 == F(3) end, "begin F = fun(A) -> A end, true = 3 == F(3) end.", true), check(fun() -> F = fun(A) -> A end, true = 3 == apply(F, [3]) end, "begin F = fun(A) -> A end, true = 3 == apply(F,[3]) end.", true), check(fun() -> catch throw(a) end, "catch throw(a).", a), check(fun() -> catch a end, "catch a.", a), check(fun() -> 4 == 3 end, "4 == 3.", false), check(fun() -> not true end, "not true.", false), check(fun() -> -3 end, "-3.", -3), error_check("3.0 = 4.0.", {badmatch,4.0}), check(fun() -> <<(3.0+2.0):32/float>> = <<5.0:32/float>> end, "<<(3.0+2.0):32/float>> = <<5.0:32/float>>.", <<5.0:32/float>>), check(fun() -> false andalso kludd end, "false andalso kludd.", false), check(fun() -> true andalso true end, "true andalso true.", true), check(fun() -> true andalso false end, "true andalso false.", false), check(fun() -> true andalso kludd end, "true andalso kludd.", kludd), error_check("kladd andalso kludd.", {badarg,kladd}), check(fun() -> if false andalso kludd -> a; true -> b end end, "if false andalso kludd -> a; true -> b end.", b), check(fun() -> if true andalso true -> a; true -> b end end, "if true andalso true -> a; true -> b end.", a), check(fun() -> if true andalso false -> a; true -> b end end, "if true andalso false -> a; true -> b end.", b), check(fun() -> true orelse kludd end, "true orelse kludd.", true), check(fun() -> false orelse false end, "false orelse false.", false), check(fun() -> false orelse true end, "false orelse true.", true), check(fun() -> false orelse kludd end, "false orelse kludd.", kludd), error_check("kladd orelse kludd.", {badarg,kladd}), error_check("[X || X <- [1,2,3], begin 1 end].",{bad_filter,1}), error_check("[X || X <- a].",{bad_generator,a}), check(fun() -> if true orelse kludd -> a; true -> b end end, "if true orelse kludd -> a; true -> b end.", a), check(fun() -> if false orelse false -> a; true -> b end end, "if false orelse false -> a; true -> b end.", b), check(fun() -> if false orelse true -> a; true -> b end end, "if false orelse true -> a; true -> b end.", a), check(fun() -> [X || X <- [1,2,3], X+2] end, "[X || X <- [1,2,3], X+2].", []), check(fun() -> [X || X <- [1,2,3], [X] == [X || X <- [2]]] end, "[X || X <- [1,2,3], [X] == [X || X <- [2]]].", [2]), check(fun() -> F = fun(1) -> ett; (2) -> zwei end, ett = F(1), zwei = F(2) end, "begin F = fun(1) -> ett; (2) -> zwei end, ett = F(1), zwei = F(2) end.", zwei), check(fun() -> F = fun(X) when X == 1 -> ett; (X) when X == 2 -> zwei end, ett = F(1), zwei = F(2) end, "begin F = fun(X) when X == 1 -> ett; (X) when X == 2 -> zwei end, ett = F(1), zwei = F(2) end.", zwei), error_check("begin F = fun(1) -> ett end, zwei = F(2) end.", function_clause), check(fun() -> if length([1]) == 1 -> yes; true -> no end end, "if length([1]) == 1 -> yes; true -> no end.", yes), check(fun() -> if is_integer(3) -> true; true -> false end end, "if is_integer(3) -> true; true -> false end.", true), check(fun() -> if integer(3) -> true; true -> false end end, "if integer(3) -> true; true -> false end.", true), check(fun() -> if is_float(3) -> true; true -> false end end, "if is_float(3) -> true; true -> false end.", false), check(fun() -> if float(3) -> true; true -> false end end, "if float(3) -> true; true -> false end.", false), check(fun() -> if is_number(3) -> true; true -> false end end, "if is_number(3) -> true; true -> false end.", true), check(fun() -> if number(3) -> true; true -> false end end, "if number(3) -> true; true -> false end.", true), check(fun() -> if is_atom(a) -> true; true -> false end end, "if is_atom(a) -> true; true -> false end.", true), check(fun() -> if atom(a) -> true; true -> false end end, "if atom(a) -> true; true -> false end.", true), check(fun() -> if is_list([]) -> true; true -> false end end, "if is_list([]) -> true; true -> false end.", true), check(fun() -> if list([]) -> true; true -> false end end, "if list([]) -> true; true -> false end.", true), check(fun() -> if is_tuple({}) -> true; true -> false end end, "if is_tuple({}) -> true; true -> false end.", true), check(fun() -> if tuple({}) -> true; true -> false end end, "if tuple({}) -> true; true -> false end.", true), check(fun() -> if is_pid(self()) -> true; true -> false end end, "if is_pid(self()) -> true; true -> false end.", true), check(fun() -> if pid(self()) -> true; true -> false end end, "if pid(self()) -> true; true -> false end.", true), check(fun() -> R = make_ref(), if is_reference(R) -> true; true -> false end end, "begin R = make_ref(), if is_reference(R) -> true;" "true -> false end end.", true), check(fun() -> R = make_ref(), if reference(R) -> true; true -> false end end, "begin R = make_ref(), if reference(R) -> true;" "true -> false end end.", true), check(fun() -> if is_port(a) -> true; true -> false end end, "if is_port(a) -> true; true -> false end.", false), check(fun() -> if port(a) -> true; true -> false end end, "if port(a) -> true; true -> false end.", false), check(fun() -> if is_function(a) -> true; true -> false end end, "if is_function(a) -> true; true -> false end.", false), check(fun() -> if function(a) -> true; true -> false end end, "if function(a) -> true; true -> false end.", false), check(fun() -> if is_binary(<<>>) -> true; true -> false end end, "if is_binary(<<>>) -> true; true -> false end.", true), check(fun() -> if binary(<<>>) -> true; true -> false end end, "if binary(<<>>) -> true; true -> false end.", true), check(fun() -> if is_integer(a) == true -> yes; true -> no end end, "if is_integer(a) == true -> yes; true -> no end.", no), check(fun() -> if [] -> true; true -> false end end, "if [] -> true; true -> false end.", false), error_check("if lists:member(1,[1]) -> true; true -> false end.", illegal_guard_expr), error_check("if false -> true end.", if_clause), check(fun() -> if a+b -> true; true -> false end end, "if a + b -> true; true -> false end.", false), check(fun() -> if + b -> true; true -> false end end, "if + b -> true; true -> false end.", false), error_check("case foo of bar -> true end.", {case_clause,foo}), error_check("case 4 of 2+a -> true; _ -> false end.", illegal_pattern), error_check("case 4 of +a -> true; _ -> false end.", illegal_pattern), check(fun() -> case a of X when X == b -> one; X when X == a -> two end end, "begin case a of X when X == b -> one; X when X == a -> two end end.", two), error_check("3 = 4.", {badmatch,4}), error_check("a = 3.", {badmatch,3}), %% error_check("3.1 = 2.7.",{badmatch,2.7}), error_check("$c = 4.", {badmatch,4}), check(fun() -> $c = $c end, "$c = $c.", $c), check(fun() -> _ = bar end, "_ = bar.", bar), check(fun() -> A = 14, A = 14 end, "begin A = 14, A = 14 end.", 14), error_check("begin A = 14, A = 16 end.", {badmatch,16}), error_check("\"hej\" = \"san\".", {badmatch,"san"}), check(fun() -> "hej" = "hej" end, "\"hej\" = \"hej\".", "hej"), error_check("[] = [a].", {badmatch,[a]}), check(fun() -> [] = [] end, "[] = [].", []), error_check("[a] = [].", {badmatch,[]}), error_check("{a,b} = 34.", {badmatch,34}), check(fun() -> <> = <<8:7>>, X end, "begin <> = <<8:7>>, X end.", 8), error_check("<<34:32>> = \"hej\".", {badmatch,"hej"}), check(fun() -> trunc((1 * 3 div 3 + 4 - 3) / 1) rem 2 end, "begin trunc((1 * 3 div 3 + 4 - 3) / 1) rem 2 end.", 0), check(fun() -> (2#101 band 2#10101) bor (2#110 bxor 2#010) end, "(2#101 band 2#10101) bor (2#110 bxor 2#010).", 5), check(fun() -> (2#1 bsl 4) + (2#10000 bsr 3) end, "(2#1 bsl 4) + (2#10000 bsr 3).", 18), check(fun() -> ((1<3) and ((1 =:= 2) or (1 =/= 2))) xor (1=<2) end, "((1<3) and ((1 =:= 2) or (1 =/= 2))) xor (1=<2).", false), check(fun() -> (a /= b) or (2 > 4) or (3 >= 3) end, "(a /= b) or (2 > 4) or (3 >= 3).", true), check(fun() -> "hej" ++ "san" =/= "hejsan" -- "san" end, "\"hej\" ++ \"san\" =/= \"hejsan\" -- \"san\".", true), check(fun() -> (bnot 1) < -0 end, "(bnot (+1)) < -0.", true), ok. %% OTP-4929. Unary plus rejects non-numbers. unary_plus(Config) when is_list(Config) -> check(fun() -> F = fun(X) -> + X end, true = -1 == F(-1) end, "begin F = fun(X) -> + X end," " true = -1 == F(-1) end.", true, ['F'], none, none), error_check("+a.", badarith), ok. %% OTP-5064. Can no longer apply atoms. apply_atom(Config) when is_list(Config) -> error_check("[X || X <- [[1],[2]], begin L = length, L(X) =:= 1 end].", {badfun,length}), ok. %% 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, "begin L = 8, F = fun(<>) -> B end, F(<<16:8, 7:16>>) end.", 7), check(fun() -> L = 8, F = fun(<>) -> B end, F(<<16:8, 7:16>>) end, "begin L = 8, F = fun(<>) -> B end, F(<<16:8, 7:16>>) end.", 7), check(fun() -> L = 8, <> = <<16:8, 7:16>>, B end, "begin L = 8, <> = <<16:8, 7:16>>, B end.", 7), error_check("begin L = 8, <> = <<16:8, 7:16>> end.", {badmatch,<<16:8,7:16>>}), error_check("begin <> = <<16:16,8:16>>, L end.", {badmatch, <<16:16,8:16>>}), check(fun() -> U = 8, (fun(<>) -> U end)(<<32:8>>) end, "begin U = 8, (fun(<>) -> U end)(<<32:8>>) end.", 32), check(fun() -> U = 8, [U || <> <- [<<32:8>>]] end, "begin U = 8, [U || <> <- [<<32:8>>]] end.", [32]), error_check("(fun({3,<>}) -> a end) ({3,<<17:32,19:32>>}).", function_clause), check(fun() -> [X || <> <- [<<16:8,19:16>>], <> <- [<>]] end, "[X || <> <- [<<16:8,19:16>>], <> <- [<>]].", [19]), check(fun() -> (fun (<>) -> case A of B -> wrong; _ -> ok end end)(<<1,2,3,4>>) end, "(fun(<>) ->" " case A of B -> wrong; _ -> ok end" " end)(<<1, 2, 3, 4>>).", ok), ok. %% OTP-6539. try/catch bugs. otp_6539(Config) when is_list(Config) -> check(fun() -> F = fun(A,B) -> try A+B catch _:_ -> dontthinkso end end, lists:zipwith(F, [1,2], [2,3]) end, "begin F = fun(A,B) -> try A+B catch _:_ -> dontthinkso end end, lists:zipwith(F, [1,2], [2,3]) end.", [3, 5]), ok. %% OTP-6543. bitlevel binaries. otp_6543(Config) when is_list(Config) -> check(fun() -> << <> || <> <- [1,2,3] >> end, "<< <> || <> <- [1,2,3] >>.", <<>>), check(fun() -> << <> || X <- [1,2,3] >> end, "<< <> || X <- [1,2,3] >>.", <<1,2,3>>), check(fun() -> << <> || <> <= <<"hej">> >> end, "<< <> || <> <= <<\"hej\">> >>.", <<1,2,2,0,1,2,1,1,1,2,2,2>>), check(fun() -> << <> || <<65,X:4>> <= <<65,7:4,65,3:4,66,8:4>> >> end, "<< <> || <<65,X:4>> <= <<65,7:4,65,3:4,66,8:4>> >>.", <<7,3>>), check(fun() -> <<34:18/big>> end, "<<34:18/big>>.", <<0,8,2:2>>), check(fun() -> <<34:18/big-unit:2>> end, "<<34:18/big-unit:2>>.", <<0,0,0,2,2:4>>), check(fun() -> <<34:18/little>> end, "<<34:18/little>>.", <<34,0,0:2>>), case eval_string("<<34:18/native>>.") of <<0,8,2:2>> -> ok; <<34,0,0:2>> -> ok end, check(fun() -> <<34:18/big-signed>> end, "<<34:18/big-signed>>.", <<0,8,2:2>>), check(fun() -> <<34:18/little-signed>> end, "<<34:18/little-signed>>.", <<34,0,0:2>>), case eval_string("<<34:18/native-signed>>.") of <<0,8,2:2>> -> ok; <<34,0,0:2>> -> ok end, check(fun() -> <<34:18/big-unsigned>> end, "<<34:18/big-unsigned>>.", <<0,8,2:2>>), check(fun() -> <<34:18/little-unsigned>> end, "<<34:18/little-unsigned>>.", <<34,0,0:2>>), case eval_string("<<34:18/native-unsigned>>.") of <<0,8,2:2>> -> ok; <<34,0,0:2>> -> ok end, check(fun() -> <<3.14:32/float-big>> end, "<<3.14:32/float-big>>.", <<64,72,245,195>>), check(fun() -> <<3.14:32/float-little>> end, "<<3.14:32/float-little>>.", <<195,245,72,64>>), case eval_string("<<3.14:32/float-native>>.") of <<64,72,245,195>> -> ok; <<195,245,72,64>> -> ok end, error_check("<<(<<17,3:2>>)/binary>>.", badarg), check(fun() -> <<(<<17,3:2>>)/bitstring>> end, "<<(<<17,3:2>>)/bitstring>>.", <<17,3:2>>), check(fun() -> <<(<<17,3:2>>):10/bitstring>> end, "<<(<<17,3:2>>):10/bitstring>>.", <<17,3:2>>), check(fun() -> <<<<344:17>>/binary-unit:17>> end, "<<<<344:17>>/binary-unit:17>>.", <<344:17>>), check(fun() -> <> = <<34:18/big>>, X end, "begin <> = <<34:18/big>>, X end.", 34), check(fun() -> <> = <<34:18/big-unit:2>>, X end, "begin <> = <<34:18/big-unit:2>>, X end.", 34), check(fun() -> <> = <<34:18/little>>, X end, "begin <> = <<34:18/little>>, X end.", 34), check(fun() -> <> = <<34:18/native>>, X end, "begin <> = <<34:18/native>>, X end.", 34), check(fun() -> <> = <<34:18/big-signed>>, X end, "begin <> = <<34:18/big-signed>>, X end.", 34), check(fun() -> <> = <<34:18/little-signed>>, X end, "begin <> = <<34:18/little-signed>>, X end.", 34), check(fun() -> <> = <<34:18/native-signed>>, X end, "begin <> = <<34:18/native-signed>>, X end.", 34), check(fun() -> <> = <<34:18/big-unsigned>>, X end, "begin <> = <<34:18/big-unsigned>>, X end.", 34), check(fun() -> <> = <<34:18/little-unsigned>>, X end, "begin <> = <<34:18/little-unsigned>>, X end.", 34), check(fun() -> <> = <<34:18/native-unsigned>>, X end, "begin <> = <<34:18/native-unsigned>>, X end.", 34), check(fun() -> <> = <<2.0:32/float-big>>, X end, "begin <> = <<2.0:32/float-big>>, X end.", 2.0), check(fun() -> <> = <<2.0:32/float-little>>, X end, "begin <> = <<2.0:32/float-little>>, X end.", 2.0), check(fun() -> <> = <<2.0:32/float-native>>, X end, "begin <> = <<2.0:32/float-native>>, X end.", 2.0), check( fun() -> [X || <<"hej",X:8>> <= <<"hej",8,"san",9,"hej",17,"hej">>] end, "[X || <<\"hej\",X:8>> <= <<\"hej\",8,\"san\",9,\"hej\",17,\"hej\">>].", [8,17]), check( fun() -> L = 8, << <> || <> <= <<16:8, 7:16>> >> end, "begin L = 8, << <> || <> <= <<16:8, 7:16>> >> end.", <<0,0,0,7>>), %% Test the Value part of a binary segment. %% "Old" bugs have been fixed (partial_eval is called on Value). check(fun() -> [ 3 || <<17/float>> <= <<17.0/float>>] end, "[ 3 || <<17/float>> <= <<17.0/float>>].", [3]), check(fun() -> [ 3 || <<17/float>> <- [<<17.0/float>>]] end, "[ 3 || <<17/float>> <- [<<17.0/float>>]].", [3]), check(fun() -> [ X || <<17/float,X:3>> <= <<17.0/float,2:3>>] end, "[ X || <<17/float,X:3>> <= <<17.0/float,2:3>>].", [2]), check(fun() -> [ foo || <<(1 bsl 1023)/float>> <= <<(1 bsl 1023)/float>>] end, "[ foo || <<(1 bsl 1023)/float>> <= <<(1 bsl 1023)/float>>].", [foo]), check(fun() -> [ foo || <<(1 bsl 1023)/float>> <- [<<(1 bsl 1023)/float>>]] end, "[ foo || <<(1 bsl 1023)/float>> <- [<<(1 bsl 1023)/float>>]].", [foo]), error_check("[ foo || <<(1 bsl 1024)/float>> <- [<<(1 bsl 1024)/float>>]].", badarg), check(fun() -> [ foo || <<(1 bsl 1024)/float>> <- [<<(1 bsl 1023)/float>>]] end, "[ foo || <<(1 bsl 1024)/float>> <- [<<(1 bsl 1023)/float>>]].", []), check(fun() -> [ foo || <<(1 bsl 1024)/float>> <= <<(1 bsl 1023)/float>>] end, "[ foo || <<(1 bsl 1024)/float>> <= <<(1 bsl 1023)/float>>].", []), check(fun() -> L = 8, [{L,B} || <> <= <<32:8,7:32/float>>] end, "begin L = 8, [{L,B} || <> <= <<32:8,7:32/float>>] end.", [{32,7.0}]), check(fun() -> L = 8, [{L,B} || <> <- [<<32:8,7:32/float>>]] end, "begin L = 8, [{L,B} || <> <- [<<32:8,7:32/float>>]] end.", [{32,7.0}]), check(fun() -> [foo || <<"s">> <= <<"st">>] end, "[foo || <<\"s\">> <= <<\"st\">>].", [foo]), check(fun() -> <<_:32>> = <<17:32>> end, "<<_:32>> = <<17:32>>.", <<17:32>>), check(fun() -> [foo || <<_:32>> <= <<17:32,20:32>>] end, "[foo || <<_:32>> <= <<17:32,20:32>>].", [foo,foo]), check(fun() -> << <> || X <- [1,2,3], X > 1 >> end, "<< <> || X <- [1,2,3], X > 1 >>.", <<0,0,0,2,0,0,0,3>>), error_check("[X || <> <= [a,b]].",{bad_generator,[a,b]}), ok. %% OTP-6787. bitlevel binaries. otp_6787(Config) when is_list(Config) -> check( fun() -> <<16:(1024*1024)>> = <<16:(1024*1024)>> end, "<<16:(1024*1024)>> = <<16:(1024*1024)>>.", <<16:1048576>>), ok. %% OTP-6977. ++ bug. otp_6977(Config) when is_list(Config) -> check( fun() -> (fun([$X] ++ _) -> ok end)("X") end, "(fun([$X] ++ _) -> ok end)(\"X\").", ok), ok. %% OTP-7550. Support for UTF-8, UTF-16, UTF-32. otp_7550(Config) when is_list(Config) -> %% UTF-8. check( fun() -> <<65>> = <<65/utf8>> end, "<<65>> = <<65/utf8>>.", <<65>>), check( fun() -> <<350/utf8>> = <<197,158>> end, "<<350/utf8>> = <<197,158>>.", <<197,158>>), check( fun() -> <<$b,$j,$\303,$\266,$r,$n>> = <<"bj\366rn"/utf8>> end, "<<$b,$j,$\303,$\266,$r,$n>> = <<\"bj\366rn\"/utf8>>.", <<$b,$j,$\303,$\266,$r,$n>>), %% UTF-16. check( fun() -> <<0,65>> = <<65/utf16>> end, "<<0,65>> = <<65/utf16>>.", <<0,65>>), check( fun() -> <<16#D8,16#08,16#DF,16#45>> = <<16#12345/utf16>> end, "<<16#D8,16#08,16#DF,16#45>> = <<16#12345/utf16>>.", <<16#D8,16#08,16#DF,16#45>>), check( fun() -> <<16#08,16#D8,16#45,16#DF>> = <<16#12345/little-utf16>> end, "<<16#08,16#D8,16#45,16#DF>> = <<16#12345/little-utf16>>.", <<16#08,16#D8,16#45,16#DF>>), check( fun() -> <<350/utf16>> = <<1,94>> end, "<<350/utf16>> = <<1,94>>.", <<1,94>>), check( fun() -> <<350/little-utf16>> = <<94,1>> end, "<<350/little-utf16>> = <<94,1>>.", <<94,1>>), check( fun() -> <<16#12345/utf16>> = <<16#D8,16#08,16#DF,16#45>> end, "<<16#12345/utf16>> = <<16#D8,16#08,16#DF,16#45>>.", <<16#D8,16#08,16#DF,16#45>>), check( fun() -> <<16#12345/little-utf16>> = <<16#08,16#D8,16#45,16#DF>> end, "<<16#12345/little-utf16>> = <<16#08,16#D8,16#45,16#DF>>.", <<16#08,16#D8,16#45,16#DF>>), %% UTF-32. check( fun() -> <<16#12345/utf32>> = <<16#0,16#01,16#23,16#45>> end, "<<16#12345/utf32>> = <<16#0,16#01,16#23,16#45>>.", <<16#0,16#01,16#23,16#45>>), check( fun() -> <<16#0,16#01,16#23,16#45>> = <<16#12345/utf32>> end, "<<16#0,16#01,16#23,16#45>> = <<16#12345/utf32>>.", <<16#0,16#01,16#23,16#45>>), check( fun() -> <<16#12345/little-utf32>> = <<16#45,16#23,16#01,16#00>> end, "<<16#12345/little-utf32>> = <<16#45,16#23,16#01,16#00>>.", <<16#45,16#23,16#01,16#00>>), check( fun() -> <<16#12345/little-utf32>> end, "<<16#12345/little-utf32>>.", <<16#45,16#23,16#01,16#00>>), %% Mixed. check( fun() -> <<16#41,16#12345/utf32,16#0391:16,16#2E:8>> end, "<<16#41,16#12345/utf32,16#0391:16,16#2E:8>>.", <<16#41,16#00,16#01,16#23,16#45,16#03,16#91,16#2E>>), ok. %% OTP-8133. Bit comprehension bug. otp_8133(Config) when is_list(Config) -> check( fun() -> E = fun(N) -> if is_integer(N) -> <>; true -> throw(foo) end end, try << << (E(V))/binary >> || V <- [1,2,3,a] >> catch foo -> ok end end, "begin E = fun(N) -> if is_integer(N) -> <>; true -> throw(foo) end end, try << << (E(V))/binary >> || V <- [1,2,3,a] >> catch foo -> ok end end.", ok), check( fun() -> E = fun(N) -> if is_integer(N) -> <>; true -> erlang:error(foo) end end, try << << (E(V))/binary >> || V <- [1,2,3,a] >> catch error:foo -> ok end end, "begin E = fun(N) -> if is_integer(N) -> <>; true -> erlang:error(foo) end end, try << << (E(V))/binary >> || V <- [1,2,3,a] >> catch error:foo -> ok end end.", ok), ok. %% OTP-10622. Bugs. otp_10622(Config) when is_list(Config) -> check(fun() -> <<0>> = <<"\x{400}">> end, "<<0>> = <<\"\\x{400}\">>. ", <<0>>), check(fun() -> <<"\x{aa}ff"/utf8>> = <<"\x{aa}ff"/utf8>> end, "<<\"\\x{aa}ff\"/utf8>> = <<\"\\x{aa}ff\"/utf8>>. ", <<"Â\xaaff">>), %% The same bug as last example: check(fun() -> case <<"foo"/utf8>> of <<"foo"/utf8>> -> true end end, "case <<\"foo\"/utf8>> of <<\"foo\"/utf8>> -> true end.", true), check(fun() -> <<"\x{400}"/utf8>> = <<"\x{400}"/utf8>> end, "<<\"\\x{400}\"/utf8>> = <<\"\\x{400}\"/utf8>>. ", <<208,128>>), error_check("<<\"\\x{aaa}\">> = <<\"\\x{aaa}\">>.", {badmatch,<<"\xaa">>}), check(fun() -> [a || <<"\x{aaa}">> <= <<2703:16>>] end, "[a || <<\"\\x{aaa}\">> <= <<2703:16>>]. ", []), check(fun() -> [a || <<"\x{aa}"/utf8>> <= <<"\x{aa}"/utf8>>] end, "[a || <<\"\\x{aa}\"/utf8>> <= <<\"\\x{aa}\"/utf8>>]. ", [a]), check(fun() -> [a || <<"\x{aa}x"/utf8>> <= <<"\x{aa}y"/utf8>>] end, "[a || <<\"\\x{aa}x\"/utf8>> <= <<\"\\x{aa}y\"/utf8>>]. ", []), check(fun() -> [a || <<"\x{aaa}">> <= <<"\x{aaa}">>] end, "[a || <<\"\\x{aaa}\">> <= <<\"\\x{aaa}\">>]. ", []), check(fun() -> [a || <<"\x{aaa}"/utf8>> <= <<"\x{aaa}"/utf8>>] end, "[a || <<\"\\x{aaa}\"/utf8>> <= <<\"\\x{aaa}\"/utf8>>]. ", [a]), ok. %% OTP-13228. ERL-32: non-local function handler bug. otp_13228(_Config) -> LFH = {value, fun(foo, [io_fwrite]) -> worked end}, EFH = {value, fun({io, fwrite}, [atom]) -> io_fwrite end}, {value, worked, []} = parse_and_run("foo(io:fwrite(atom)).", LFH, EFH). %% OTP-14826: more accurate stacktrace. otp_14826(_Config) -> backtrace_check("fun(P) when is_pid(P) -> true end(a).", function_clause, [{erl_eval,'-inside-an-interpreted-fun-',[a],[]}, {erl_eval,eval_fun,6}, ?MODULE]), backtrace_check("B.", {unbound_var, 'B'}, [{erl_eval,expr,2}, ?MODULE]), backtrace_check("B.", {unbound, 'B'}, [{erl_eval,expr,5}, ?MODULE], none, none), backtrace_check("1/0.", badarith, [{erlang,'/',[1,0],[]}, {erl_eval,do_apply,6}]), backtrace_catch("catch 1/0.", badarith, [{erlang,'/',[1,0],[]}, {erl_eval,do_apply,6}]), check(fun() -> catch exit(foo) end, "catch exit(foo).", {'EXIT', foo}), check(fun() -> catch throw(foo) end, "catch throw(foo).", foo), backtrace_check("try 1/0 after foo end.", badarith, [{erlang,'/',[1,0],[]}, {erl_eval,do_apply,6}]), backtrace_catch("catch (try 1/0 after foo end).", badarith, [{erlang,'/',[1,0],[]}, {erl_eval,do_apply,6}]), backtrace_catch("try catch 1/0 after foo end.", badarith, [{erlang,'/',[1,0],[]}, {erl_eval,do_apply,6}]), backtrace_check("try a of b -> bar after foo end.", {try_clause,a}, [{erl_eval,try_clauses,8}]), check(fun() -> X = try foo:bar() catch A:B:C -> {A,B} end, X end, "try foo:bar() catch A:B:C -> {A,B} end.", {error, undef}), backtrace_check("C = 4, try foo:bar() catch A:B:C -> {A,B,C} end.", stacktrace_bound, [{erl_eval,check_stacktrace_vars,2}, {erl_eval,try_clauses,8}], none, none), backtrace_catch("catch (try a of b -> bar after foo end).", {try_clause,a}, [{erl_eval,try_clauses,8}]), backtrace_check("try 1/0 catch exit:a -> foo end.", badarith, [{erlang,'/',[1,0],[]}, {erl_eval,do_apply,6}]), Es = [{'try',1,[{call,1,{remote,1,{atom,1,foo},{atom,1,bar}},[]}], [], [{clause,1,[{tuple,1,[{var,1,'A'},{var,1,'B'},{atom,1,'C'}]}], [],[{tuple,1,[{var,1,'A'},{var,1,'B'},{atom,1,'C'}]}]}],[]}], try erl_eval:exprs(Es, [], none, none), ct:fail(stacktrace_variable) catch error:{illegal_stacktrace_variable,{atom,1,'C'}}:S -> [{erl_eval,check_stacktrace_vars,2,_}, {erl_eval,try_clauses,8,_}|_] = S end, backtrace_check("{1,1} = {A = 1, A = 2}.", {badmatch, 1}, [erl_eval, {lists,foldl,3}]), backtrace_check("case a of a when foo:bar() -> x end.", guard_expr, [{erl_eval,guard0,4}], none, none), backtrace_check("case a of foo() -> ok end.", {illegal_pattern,{call,1,{atom,1,foo},[]}}, [{erl_eval,match,4}], none, none), backtrace_check("case a of b -> ok end.", {case_clause,a}, [{erl_eval,case_clauses,6}, ?MODULE]), backtrace_check("if a =:= b -> ok end.", if_clause, [{erl_eval,if_clauses,5}, ?MODULE]), backtrace_check("fun A(b) -> ok end(a).", function_clause, [{erl_eval,'-inside-an-interpreted-fun-',[a],[]}, {erl_eval,eval_named_fun,8}, ?MODULE]), backtrace_check("[A || A <- a].", {bad_generator, a}, [{erl_eval,eval_generate,7}, {erl_eval, eval_lc, 6}]), backtrace_check("<< <> || <> <= a>>.", {bad_generator, a}, [{erl_eval,eval_b_generate,7}, {erl_eval, eval_bc, 6}]), backtrace_check("[A || A <- [1], begin a end].", {bad_filter, a}, [{erl_eval,eval_filter,6}, {erl_eval, eval_generate, 7}]), fun() -> {'EXIT', {{badarity, {_Fun, []}}, BT}} = (catch parse_and_run("fun(A) -> A end().")), check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT) end(), fun() -> {'EXIT', {{badarity, {_Fun, []}}, BT}} = (catch parse_and_run("fun F(A) -> A end().")), check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT) end(), backtrace_check("foo().", undef, [{erl_eval,foo,0},{erl_eval,local_func,6}], none, none), backtrace_check("a orelse false.", {badarg, a}, [{erl_eval,expr,5}, ?MODULE]), backtrace_check("a andalso false.", {badarg, a}, [{erl_eval,expr,5}, ?MODULE]), backtrace_check("t = u.", {badmatch, u}, [{erl_eval,expr,5}, ?MODULE]), backtrace_check("{math,sqrt}(2).", {badfun, {math,sqrt}}, [{erl_eval,expr,5}, ?MODULE]), backtrace_check("erl_eval_SUITE:simple().", simple, [{?MODULE,simple1,0},{?MODULE,simple,0},erl_eval]), Args = [{integer,1,I} || I <- lists:seq(1, 30)], backtrace_check("fun(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18," "19,20,21,22,23,24,25,26,27,28,29,30) -> a end.", {argument_limit, {'fun',1,[{clause,1,Args,[],[{atom,1,a}]}]}}, [{erl_eval,expr,5}, ?MODULE]), backtrace_check("fun F(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18," "19,20,21,22,23,24,25,26,27,28,29,30) -> a end.", {argument_limit, {named_fun,1,'F',[{clause,1,Args,[],[{atom,1,a}]}]}}, [{erl_eval,expr,5}, ?MODULE]), backtrace_check("#r{}.", {undef_record,r}, [{erl_eval,expr,5}, ?MODULE], none, none), %% eval_bits backtrace_check("<<100:8/bitstring>>.", badarg, [{eval_bits,eval_exp_field1,6}, eval_bits,eval_bits,erl_eval]), backtrace_check("<<100:8/foo>>.", {undefined_bittype,foo}, [{eval_bits,make_bit_type,3},eval_bits, eval_bits,erl_eval], none, none), backtrace_check("B = <<\"foo\">>, <>.", badarg, [{eval_bits,eval_exp_field1,6}, eval_bits,eval_bits,erl_eval], none, none), ok. simple() -> A = simple1(), {A}. simple1() -> %% If the compiler could see that this function would always %% throw an error exception, it would rewrite simple() like this: %% %% simple() -> simple1(). %% %% That would change the stacktrace. To prevent the compiler from %% doing that optimization, we must obfuscate the code. case get(a_key_that_is_not_defined) of undefined -> erlang:error(simple); WillNeverHappen -> WillNeverHappen end. %% Simple cases, just to cover some code. funs(Config) when is_list(Config) -> do_funs(none, none), do_funs(lfh(), none), do_funs(lfh(), efh()), error_check("nix:foo().", {access_not_allowed,nix}, lfh(), efh()), error_check("bar().", undef, none, none), check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end, F1(F1, 1000) end, "begin F1 = fun(F,N) -> count_down(F, N) end," "F1(F1,1000) end.", 0, ['F1'], lfh(), none), check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end, F1(F1, 1000) end, "begin F1 = fun(F,N) -> count_down(F, N) end," "F1(F1,1000) end.", 0, ['F1'], lfh_value(), none), check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end, F1(F1, 1000) end, "begin F1 = fun(F,N) -> count_down(F, N) end," "F1(F1,1000) end.", 0, ['F1'], lfh_value_extra(), none), check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end, F1(F1, 1000) end, "begin F1 = fun(F,N) -> count_down(F, N) end," "F1(F1,1000) end.", 0, ['F1'], {?MODULE,local_func_value}, none), %% This is not documented, and only for backward compatibility (good!). B0 = erl_eval:new_bindings(), check(fun() -> is_function(?MODULE:count_down_fun()) end, "begin is_function(count_down_fun()) end.", true, [], {?MODULE,local_func,[B0]},none), EF = fun({timer,sleep}, As) when length(As) == 1 -> exit({got_it,sleep}); ({M,F}, As) -> apply(M, F, As) end, EFH = {value, EF}, error_check("apply(timer, sleep, [1]).", got_it, none, EFH), error_check("begin F = fun(T) -> timer:sleep(T) end,F(1) end.", got_it, none, EFH), error_check("fun c/1.", undef), error_check("fun a:b/0().", undef), MaxArgs = 20, [true] = lists:usort([run_many_args(SAs) || SAs <- many_args(MaxArgs)]), {'EXIT',{{argument_limit,_},_}} = (catch run_many_args(many_args1(MaxArgs+1))), check(fun() -> M = lists, F = fun M:reverse/1, [1,2] = F([2,1]), ok end, "begin M = lists, F = fun M:reverse/1," " [1,2] = F([2,1]), ok end.", ok), %% Test that {M,F} is not accepted as a fun. error_check("{" ?MODULE_STRING ",module_info}().", {badfun,{?MODULE,module_info}}), ok. run_many_args({S, As}) -> apply(eval_string(S), As) =:= As. many_args(N) -> [many_args1(I) || I <- lists:seq(1, N)]. many_args1(N) -> F = fun(L, P) -> tl(lists:flatten([","++P++integer_to_list(E) || E <- L])) end, L = lists:seq(1, N), T = F(L, "V"), S = lists:flatten(io_lib:format("fun(~s) -> [~s] end.", [T, T])), {S, L}. do_funs(LFH, EFH) -> %% LFH is not really used by these examples... %% These tests do not prove that tail recursive functions really %% work (that the process does not grow); one should also run them %% manually with 1000 replaced by 1000000. M = atom_to_list(?MODULE), check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end, F1(F1, 1000) end, concat(["begin F1 = fun(F,N) -> ", M, ":count_down(F, N) end, F1(F1,1000) end."]), 0, ['F1'], LFH, EFH), check(fun() -> F1 = fun(F,N) -> apply(?MODULE,count_down,[F,N]) end, F1(F1, 1000) end, concat(["begin F1 = fun(F,N) -> apply(", M, ",count_down,[F, N]) end, F1(F1,1000) end."]), 0, ['F1'], LFH, EFH), check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]); (_F,0) -> ok end, F(F, 1000) end, "begin F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);" "(_F,0) -> ok end," "F(F, 1000) end.", ok, ['F'], LFH, EFH), check(fun() -> F = fun(F,N) when N > 0 -> apply(erlang,apply,[F,[F,N-1]]); (_F,0) -> ok end, F(F, 1000) end, "begin F = fun(F,N) when N > 0 ->" "apply(erlang,apply,[F,[F,N-1]]);" "(_F,0) -> ok end," "F(F, 1000) end.", ok, ['F'], LFH, EFH), check(fun() -> F = count_down_fun(), SF = fun(SF, F1, N) -> F(SF, F1, N) end, SF(SF, F, 1000) end, concat(["begin F = ", M, ":count_down_fun()," "SF = fun(SF, F1, N) -> F(SF, F1, N) end," "SF(SF, F, 1000) end."]), ok, ['F','SF'], LFH, EFH), check(fun() -> F = fun(X) -> A = 1+X, {X,A} end, true = {2,3} == F(2) end, "begin F = fun(X) -> A = 1+X, {X,A} end, true = {2,3} == F(2) end.", true, ['F'], LFH, EFH), check(fun() -> F = fun(X) -> erlang:'+'(X,2) end, true = 3 == F(1) end, "begin F = fun(X) -> erlang:'+'(X,2) end," " true = 3 == F(1) end.", true, ['F'], LFH, EFH), check(fun() -> F = fun(X) -> byte_size(X) end, ?MODULE:do_apply(F,<<"hej">>) end, concat(["begin F = fun(X) -> size(X) end,", M,":do_apply(F,<<\"hej\">>) end."]), 3, ['F'], LFH, EFH), check(fun() -> F1 = fun(X, Z) -> {X,Z} end, Z = 5, F2 = fun(X, Y) -> F1(Z,{X,Y}) end, F3 = fun(X, Y) -> {a,F1(Z,{X,Y})} end, {5,{x,y}} = F2(x,y), {a,{5,{y,x}}} = F3(y,x), {5,{5,y}} = F2(Z,y), true = {5,{x,5}} == F2(x,Z) end, "begin F1 = fun(X, Z) -> {X,Z} end, Z = 5, F2 = fun(X, Y) -> F1(Z,{X,Y}) end, F3 = fun(X, Y) -> {a,F1(Z,{X,Y})} end, {5,{x,y}} = F2(x,y), {a,{5,{y,x}}} = F3(y,x), {5,{5,y}} = F2(Z,y), true = {5,{x,5}} == F2(x,Z) end.", true, ['F1','Z','F2','F3'], LFH, EFH), check(fun() -> F = fun(X) -> byte_size(X) end, F2 = fun(Y) -> F(Y) end, ?MODULE:do_apply(F2,<<"hej">>) end, concat(["begin F = fun(X) -> size(X) end,", "F2 = fun(Y) -> F(Y) end,", M,":do_apply(F2,<<\"hej\">>) end."]), 3, ['F','F2'], LFH, EFH), check(fun() -> Z = 5, F = fun(X) -> {Z,X} end, F2 = fun(Z) -> F(Z) end, F2(3) end, "begin Z = 5, F = fun(X) -> {Z,X} end, F2 = fun(Z) -> F(Z) end, F2(3) end.", {5,3},['F','F2','Z'], LFH, EFH), check(fun() -> F = fun(Z) -> Z end, F2 = fun(X) -> F(X), Z = {X,X}, Z end, {1,1} = F2(1), Z = 7, Z end, "begin F = fun(Z) -> Z end, F2 = fun(X) -> F(X), Z = {X,X}, Z end, {1,1} = F2(1), Z = 7, Z end.", 7, ['F','F2','Z'], LFH, EFH), check(fun() -> F = fun(F, N) -> [?MODULE:count_down(F,N) || X <-[1]] end, F(F,2) end, concat(["begin F = fun(F, N) -> [", M, ":count_down(F,N) || X <-[1]] end, F(F,2) end."]), [[[0]]], ['F'], LFH, EFH), ok. count_down(F, N) when N > 0 -> F(F, N-1); count_down(_F, N) -> N. count_down_fun() -> fun(SF,F,N) when N > 0 -> SF(SF,F,N-1); (_SF,_F,_N) -> ok end. do_apply(F, V) -> F(V). lfh() -> {eval, fun(F, As, Bs) -> local_func(F, As, Bs) end}. local_func(F, As0, Bs0) when is_atom(F) -> {As,Bs} = erl_eval:expr_list(As0, Bs0, lfh()), case erlang:function_exported(?MODULE, F, length(As)) of true -> {value,apply(?MODULE, F, As),Bs}; false -> {value,apply(shell_default, F, As),Bs} end. lfh_value_extra() -> %% Not documented. {value, fun(F, As, a1, a2) -> local_func_value(F, As) end, [a1, a2]}. lfh_value() -> {value, fun(F, As) -> local_func_value(F, As) end}. local_func_value(F, As) when is_atom(F) -> case erlang:function_exported(?MODULE, F, length(As)) of true -> apply(?MODULE, F, As); false -> apply(shell_default, F, As) end. efh() -> {value, fun(F, As) -> external_func(F, As) end}. external_func({M,_}, _As) when M == nix -> exit({{access_not_allowed,M},[mfa]}); external_func(F, As) when is_function(F) -> apply(F, As); external_func({M,F}, As) -> apply(M, F, As). %% Test try-of-catch-after-end statement. try_catch(Config) when is_list(Config) -> %% Match in of with catch check(fun() -> try 1 of 1 -> 2 catch _:_ -> 3 end end, "try 1 of 1 -> 2 catch _:_ -> 3 end.", 2), check(fun() -> try 1 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end end, "try 1 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end.", 2), check(fun() -> try 3 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end end, "try 3 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end.", 4), %% Just after check(fun () -> X = try 1 after put(try_catch, 2) end, {X,get(try_catch)} end, "begin X = try 1 after put(try_catch, 2) end, " "{X,get(try_catch)} end.", {1,2}), %% Match in of with after check(fun() -> X = try 1 of 1 -> 2 after put(try_catch, 3) end, {X,get(try_catch)} end, "begin X = try 1 of 1 -> 2 after put(try_catch, 3) end, " "{X,get(try_catch)} end.", {2,3}), check(fun() -> X = try 1 of 1 -> 2; 3 -> 4 after put(try_catch, 5) end, {X,get(try_catch)} end, "begin X = try 1 of 1 -> 2; 3 -> 4 " " after put(try_catch, 5) end, " " {X,get(try_catch)} end.", {2,5}), check(fun() -> X = try 3 of 1 -> 2; 3 -> 4 after put(try_catch, 5) end, {X,get(try_catch)} end, "begin X = try 3 of 1 -> 2; 3 -> 4 " " after put(try_catch, 5) end, " " {X,get(try_catch)} end.", {4,5}), %% Nomatch in of error_check("try 1 of 2 -> 3 catch _:_ -> 4 end.", {try_clause,1}), %% Nomatch in of with after check(fun () -> {'EXIT',{{try_clause,1},_}} = begin catch try 1 of 2 -> 3 after put(try_catch, 4) end end, get(try_catch) end, "begin {'EXIT',{{try_clause,1},_}} = " " begin catch try 1 of 2 -> 3 " " after put(try_catch, 4) end end, " " get(try_catch) end. ", 4), %% Exception in try check(fun () -> try 1=2 catch error:{badmatch,2} -> 3 end end, "try 1=2 catch error:{badmatch,2} -> 3 end.", 3), check(fun () -> try 1=2 of 3 -> 4 catch error:{badmatch,2} -> 5 end end, "try 1=2 of 3 -> 4 " "catch error:{badmatch,2} -> 5 end.", 5), %% Exception in try with after check(fun () -> X = try 1=2 catch error:{badmatch,2} -> 3 after put(try_catch, 4) end, {X,get(try_catch)} end, "begin X = try 1=2 " " catch error:{badmatch,2} -> 3 " " after put(try_catch, 4) end, " " {X,get(try_catch)} end. ", {3,4}), check(fun () -> X = try 1=2 of 3 -> 4 catch error:{badmatch,2} -> 5 after put(try_catch, 6) end, {X,get(try_catch)} end, "begin X = try 1=2 of 3 -> 4" " catch error:{badmatch,2} -> 5 " " after put(try_catch, 6) end, " " {X,get(try_catch)} end. ", {5,6}), %% Uncaught exception error_check("try 1=2 catch error:undefined -> 3 end. ", {badmatch,2}), error_check("try 1=2 of 3 -> 4 catch error:undefined -> 5 end. ", {badmatch,2}), %% Uncaught exception with after check(fun () -> {'EXIT',{{badmatch,2},_}} = begin catch try 1=2 after put(try_catch, 3) end end, get(try_catch) end, "begin {'EXIT',{{badmatch,2},_}} = " " begin catch try 1=2 " " after put(try_catch, 3) end end, " " get(try_catch) end. ", 3), check(fun () -> {'EXIT',{{badmatch,2},_}} = begin catch try 1=2 of 3 -> 4 after put(try_catch, 5) end end, get(try_catch) end, "begin {'EXIT',{{badmatch,2},_}} = " " begin catch try 1=2 of 3 -> 4" " after put(try_catch, 5) end end, " " get(try_catch) end. ", 5), check(fun () -> {'EXIT',{{badmatch,2},_}} = begin catch try 1=2 catch error:undefined -> 3 after put(try_catch, 4) end end, get(try_catch) end, "begin {'EXIT',{{badmatch,2},_}} = " " begin catch try 1=2 catch error:undefined -> 3 " " after put(try_catch, 4) end end, " " get(try_catch) end. ", 4), check(fun () -> {'EXIT',{{badmatch,2},_}} = begin catch try 1=2 of 3 -> 4 catch error:undefined -> 5 after put(try_catch, 6) end end, get(try_catch) end, "begin {'EXIT',{{badmatch,2},_}} = " " begin catch try 1=2 of 3 -> 4 " " catch error:undefined -> 5 " " after put(try_catch, 6) end end, " " get(try_catch) end. ", 6), ok. %% OTP-7933. eval_expr_5(Config) when is_list(Config) -> {ok,Tokens ,_} = erl_scan:string("if a+4 == 4 -> yes; true -> no end. "), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), {value, no, []} = erl_eval:expr(Expr, [], none, none, none), no = erl_eval:expr(Expr, [], none, none, value), try erl_eval:expr(Expr, [], none, none, 4711), function_clause = should_never_reach_here catch error:function_clause -> ok end. zero_width(Config) when is_list(Config) -> check(fun() -> {'EXIT',{badarg,_}} = (catch <>), ok end, "begin {'EXIT',{badarg,_}} = (catch <>), " "ok end.", ok), ok. eep37(Config) when is_list(Config) -> check(fun () -> (fun _(X) -> X end)(42) end, "(fun _(X) -> X end)(42).", 42), check(fun () -> (fun _Id(X) -> X end)(42) end, "(fun _Id(X) -> X end)(42).", 42), check(fun () -> is_function((fun Self() -> Self end)(), 0) end, "is_function((fun Self() -> Self end)(), 0).", true), check(fun () -> F = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end, F(6) end, "(fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end)(6).", 720), ok. eep43(Config) when is_list(Config) -> check(fun () -> #{} end, " #{}.", #{}), check(fun () -> #{a => b} end, "#{a => b}.", #{a => b}), check(fun () -> Map = #{a => b}, {Map#{a := b},Map#{a => c},Map#{d => e}} end, "begin " " Map = #{a => B=b}, " " {Map#{a := B},Map#{a => c},Map#{d => e}} " "end.", {#{a => b},#{a => c},#{a => b,d => e}}), check(fun () -> lists:map(fun (X) -> X#{price := 0} end, [#{hello => 0, price => nil}]) end, "lists:map(fun (X) -> X#{price := 0} end, [#{hello => 0, price => nil}]).", [#{hello => 0, price => 0}]), check(fun () -> Map = #{ <<33:333>> => "wat" }, #{ <<33:333>> := "wat" } = Map end, "begin " " Map = #{ <<33:333>> => \"wat\" }, " " #{ <<33:333>> := \"wat\" } = Map " "end.", #{ <<33:333>> => "wat" }), check(fun () -> K1 = 1, K2 = <<42:301>>, K3 = {3,K2}, Map = #{ K1 => 1, K2 => 2, K3 => 3, {2,2} => 4}, #{ K1 := 1, K2 := 2, K3 := 3, {2,2} := 4} = Map end, "begin " " K1 = 1, " " K2 = <<42:301>>, " " K3 = {3,K2}, " " Map = #{ K1 => 1, K2 => 2, K3 => 3, {2,2} => 4}, " " #{ K1 := 1, K2 := 2, K3 := 3, {2,2} := 4} = Map " "end.", #{ 1 => 1, <<42:301>> => 2, {3,<<42:301>>} => 3, {2,2} => 4}), check(fun () -> X = key, (fun(#{X := value}) -> true end)(#{X => value}) end, "begin " " X = key, " " (fun(#{X := value}) -> true end)(#{X => value}) " "end.", true), error_check("[camembert]#{}.", {badmap,[camembert]}), error_check("[camembert]#{nonexisting:=v}.", {badmap,[camembert]}), error_check("#{} = 1.", {badmatch,1}), error_check("[]#{a=>error(bad)}.", bad), error_check("(#{})#{nonexisting:=value}.", {badkey,nonexisting}), ok. otp_15035(Config) when is_list(Config) -> check(fun() -> fun() when #{} -> a; () when #{a => b} -> b; () when #{a => b} =:= #{a => b} -> c end() end, "fun() when #{} -> a; () when #{a => b} -> b; () when #{a => b} =:= #{a => b} -> c end().", c), check(fun() -> F = fun(M) when M#{} -> a; (M) when M#{a => b} -> b; (M) when M#{a := b} -> c; (M) when M#{a := b} =:= M#{a := b} -> d; (M) when M#{a => b} =:= M#{a => b} -> e end, {F(#{}), F(#{a => b})} end, "fun() -> F = fun(M) when M#{} -> a; (M) when M#{a => b} -> b; (M) when M#{a := b} -> c; (M) when M#{a := b} =:= M#{a := b} -> d; (M) when M#{a => b} =:= M#{a => b} -> e end, {F(#{}), F(#{a => b})} end().", {e, d}), ok. %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), FunString = concat(["fun() -> ", no_final_dot(String), " end(). "]), check1(F, FunString, Result), CompileString = concat(["hd(lists:map(fun(_) -> ", no_final_dot(String), " end, [foo])). "]), check1(F, CompileString, Result). check1(F, String, Result) -> Result = F(), case catch parse_and_run(String) of {value, Result, _} -> ok; Other -> ct:fail({eval, Other, Result}) end. check(F, String, Result, BoundVars, LFH, EFH) -> Result = F(), case catch parse_and_run(String, LFH, EFH) of {value, Result, Bs} -> %% We just assume that Bs is an orddict... Keys = orddict:fetch_keys(Bs), case sort(BoundVars) == Keys of true -> ok; false -> ct:fail({check, BoundVars, Keys}) end, ok; Other -> ct:fail({check, Other, Result}) end. error_check(String, Result) -> case catch parse_and_run(String) of {'EXIT', {Result,_}} -> ok; Other -> ct:fail({eval, Other, Result}) end. error_check(String, Result, LFH, EFH) -> case catch parse_and_run(String, LFH, EFH) of {'EXIT', {Result,_}} -> ok; Other -> ct:fail({eval, Other, Result}) end. backtrace_check(String, Result, Backtrace) -> case catch parse_and_run(String) of {'EXIT', {Result, BT}} -> check_backtrace(Backtrace, BT); Other -> ct:fail({eval, Other, Result}) end. backtrace_check(String, Result, Backtrace, LFH, EFH) -> case catch parse_and_run(String, LFH, EFH) of {'EXIT', {Result, BT}} -> check_backtrace(Backtrace, BT); Other -> ct:fail({eval, Other, Result}) end. backtrace_catch(String, Result, Backtrace) -> case parse_and_run(String) of {value, {'EXIT', {Result, BT}}, _Bindings} -> check_backtrace(Backtrace, BT); Other -> ct:fail({eval, Other, Result}) end. check_backtrace([B1|Backtrace], [B2|BT]) -> case {B1, B2} of {M, {M,_,_,_}} -> ok; {{M,F,A}, {M,F,A,_}} -> ok; {B, B} -> ok end, check_backtrace(Backtrace, BT); check_backtrace([], _) -> ok. eval_string(String) -> {value, Result, _} = parse_and_run(String), Result. parse_and_run(String) -> {ok,Tokens,_} = erl_scan:string(String), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), erl_eval:expr(Expr, []). parse_and_run(String, LFH, EFH) -> {ok,Tokens,_} = erl_scan:string(String), {ok, Exprs} = erl_parse:parse_exprs(Tokens), erl_eval:exprs(Exprs, [], LFH, EFH). no_final_dot(S) -> case lists:reverse(S) of " ." ++ R -> lists:reverse(R); "." ++ R -> lists:reverse(R); _ -> S end.