%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2004-2019. 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(shell_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([forget/1, records/1, known_bugs/1, otp_5226/1, otp_5327/1, otp_5435/1, otp_5195/1, otp_5915/1, otp_5916/1, bs_match_misc_SUITE/1, bs_match_int_SUITE/1, bs_match_tail_SUITE/1, bs_match_bin_SUITE/1, bs_construct_SUITE/1, refman_bit_syntax/1, progex_bit_syntax/1, progex_records/1, progex_lc/1, progex_funs/1, otp_5990/1, otp_6166/1, otp_6554/1, otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1, otp_14285/1, otp_14296/1, typed_records/1]). -export([ start_restricted_from_shell/1, start_restricted_on_command_line/1,restricted_local/1]). %% Internal export. -export([otp_5435_2/0, prompt1/1, prompt2/1, prompt3/1, prompt4/1, prompt5/1]). %% %% Define to run outside of test server %% %% -define(STANDALONE,1). -ifdef(STANDALONE). -define(config(A,B),config(A,B)). -define(t,test_server). -export([config/2]). -define(line, noop, ). config(priv_dir,_) -> ".". -else. -include_lib("common_test/include/ct.hrl"). -export([init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Case, Config) -> OrigPath = code:get_path(), code:add_patha(proplists:get_value(priv_dir,Config)), [{orig_path,OrigPath} | Config]. end_per_testcase(_Case, Config) -> OrigPath = proplists:get_value(orig_path,Config), code:set_path(OrigPath), application:unset_env(stdlib, restricted_shell), (catch code:purge(user_default)), (catch code:delete(user_default)), ok. -endif. suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,10}}]. all() -> [forget, known_bugs, otp_5226, otp_5327, otp_5435, otp_5195, otp_5915, otp_5916, {group, bits}, {group, refman}, {group, progex}, {group, tickets}, {group, restricted}, {group, records}]. groups() -> [{restricted, [], [start_restricted_from_shell, start_restricted_on_command_line, restricted_local]}, {bits, [], [bs_match_misc_SUITE, bs_match_tail_SUITE, bs_match_bin_SUITE, bs_construct_SUITE]}, {records, [], [records, typed_records]}, {refman, [], [refman_bit_syntax]}, {progex, [], [progex_bit_syntax, progex_records, progex_lc, progex_funs]}, {tickets, [], [otp_5990, otp_6166, otp_6554, otp_7184, otp_7232, otp_8393, otp_10302, otp_13719, otp_14285, otp_14296]}]. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. -record(state, {bin, reply, leader, unic = latin1}). %% Test that a restricted shell can be started from the normal shell. start_restricted_from_shell(Config) when is_list(Config) -> [{error,nofile}] = scan(<<"begin shell:start_restricted(" "nonexisting_module) end.">>), Test = filename:join(proplists:get_value(priv_dir, Config), "test_restricted.erl"), Contents = <<"-module(test_restricted). -export([local_allowed/3, non_local_allowed/3]). local_allowed(m,[],State) -> {true,State}; local_allowed(ugly,[],_State) -> non_conforming_reply; local_allowed(_,_,State) -> {false,State}. non_local_allowed({shell,stop_restricted},[],State) -> {true,State}; non_local_allowed({erlang,'+'},[_],State) -> {true,State}; non_local_allowed({erlang,'-'},[_,_],_State) -> non_conforming_reply; non_local_allowed({h, d}, [Arg], S) -> {{redirect, {erlang,hd}, [Arg]}, S}; non_local_allowed(_,_,State) -> {false,State}. ">>, ok = compile_file(Config, Test, Contents, []), "exception exit: restricted shell starts now" = comm_err(<<"begin shell:start_restricted(" "test_restricted) end.">>), {ok, test_restricted} = application:get_env(stdlib, restricted_shell), "Module" ++ _ = t({<<"begin m() end.">>, utf8}), "exception exit: restricted shell does not allow c(foo)" = comm_err(<<"begin c(foo) end.">>), "exception exit: restricted shell does not allow init:stop()" = comm_err(<<"begin init:stop() end.">>), "exception exit: restricted shell does not allow init:stop()" = comm_err(<<"begin F = fun() -> init:stop() end, F() end.">>), "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin +a end.">>), "exception exit: restricted shell does not allow a + b" = comm_err(<<"begin a+b end.">>), "exception exit: restricted shell does not allow - b" = comm_err(<<"begin -b end.">>), "exception exit: restricted shell does not allow 1 + 2" = comm_err(<<"begin if atom(1 + 2> 0) -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow 1 + 2" = comm_err(<<"begin if is_atom(1 + 2> 0) -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow - 2" = comm_err(<<"begin if - 2 -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow - 2" = comm_err(<<"begin if (- 2 > 0) andalso true -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow - 2" = comm_err(<<"begin if (- 2 > 0) orelse true -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow 1 + 2" = comm_err(<<"begin if 1 + 2 > 0 -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow 1 + 2" = comm_err(<<"begin if erlang:is_atom(1 + 2> 0) -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow is_integer(1)" = comm_err(<<"begin if is_integer(1) -> 1; true -> 2 end end.">>), "exception exit: restricted shell does not allow is_integer(1)" = comm_err(<<"begin if integer(1) -> 1; true -> 2 end end.">>), "exception exit: " "restricted shell module returned bad value non_conforming_reply" = comm_err(<<"ugly().">>), [one] = scan(<<"h:d([one,two]).">>), "exception exit: " "restricted shell module returned bad value non_conforming_reply" = comm_err(<<"1 - 2.">>), "exception exit: restricted shell stopped"= comm_err(<<"begin shell:stop_restricted() end.">>), undefined = application:get_env(stdlib, restricted_shell), ok. %% Check restricted shell when started from the command line. start_restricted_on_command_line(Config) when is_list(Config) -> {ok,Node} = start_node(shell_suite_helper_1, "-pa "++proplists:get_value(priv_dir,Config)++ " -stdlib restricted_shell foo"), "Warning! Restricted shell module foo not found: nofile"++_ = t({Node, <<"begin m() end.">>}), "exception exit: restricted shell does not allow m()" = comm_err({Node, <<"begin m() end.">>}), [ok] = (catch scan({Node, <<"begin q() end.">>})), test_server:stop_node(Node), Test = filename:join(proplists:get_value(priv_dir, Config), "test_restricted2.erl"), Contents = <<"-module(test_restricted2). -export([local_allowed/3, non_local_allowed/3]). local_allowed(m,[],State) -> {true,State}; local_allowed(_,_,State) -> {false,State}. non_local_allowed({shell,stop_restricted},[],State) -> {true,State}; non_local_allowed({erlang,node},[],State) -> {true,State}; non_local_allowed(_,_,State) -> {false,State}. ">>, ok = compile_file(Config, Test, Contents, []), {ok,Node2} = start_node(shell_suite_helper_2, "-pa "++proplists:get_value(priv_dir,Config)++ " -stdlib restricted_shell test_restricted2"), "Module" ++ _ = t({Node2,<<"begin m() end.">>, utf8}), "exception exit: restricted shell does not allow c(foo)" = comm_err({Node2,<<"begin c(foo) end.">>}), "exception exit: restricted shell does not allow init:stop()" = comm_err({Node2,<<"begin init:stop() end.">>}), "exception exit: restricted shell does not allow init:stop()" = comm_err({Node2,<<"begin F = fun() -> init:stop() end, F() end.">>}), [Node2] = scan({Node2, <<"begin erlang:node() end.">>}), [Node2] = scan({Node2, <<"begin node() end.">>}), "exception exit: restricted shell stopped"= comm_err({Node2,<<"begin shell:stop_restricted() end.">>}), [ok] = scan({Node2, <<"begin q() end.">>}), test_server:stop_node(Node2), ok. %% Tests calling local shell functions with spectacular arguments in %% restricted shell. restricted_local(Config) when is_list(Config) -> [{error,nofile}] = scan(<<"begin shell:start_restricted(" "nonexisting_module) end.">>), Test = filename:join(proplists:get_value(priv_dir, Config), "test_restricted_local.erl"), Contents = <<"-module(test_restricted_local). -export([local_allowed/3, non_local_allowed/3]). local_allowed(m,[],State) -> {true,State}; local_allowed(banan,_,State) -> {true,State}; local_allowed(funkis,_,State) -> {true,State}; local_allowed(c,_,State) -> {true,State}; local_allowed(_,_,State) -> {false,State}. non_local_allowed({shell,stop_restricted},[],State) -> {true,State}; non_local_allowed(_,_,State) -> {false,State}. ">>, ok = compile_file(Config, Test, Contents, []), Test2 = filename:join(proplists:get_value(priv_dir, Config), "user_default.erl"), Contents2 = <<"-module(user_default). -export([funkis/1,apple/1]). funkis(F) when is_function(F) -> funkis; funkis(_) -> nofunkis. apple(_) -> apple. ">>, ok = compile_file(Config, Test2, Contents2, []), "exception exit: restricted shell starts now" = comm_err(<<"begin shell:start_restricted(" "test_restricted_local) end.">>), {ok, test_restricted_local} = application:get_env(stdlib, restricted_shell), "exception exit: restricted shell does not allow foo(" ++ _ = comm_err(<<"begin F=fun() -> hello end, foo(F) end.">>), "exception error: undefined shell command banan/1" = comm_err(<<"begin F=fun() -> hello end, banan(F) end.">>), "Recompiling "++_ = t(<<"c(shell_SUITE).">>), "exception exit: restricted shell does not allow l(" ++ _ = comm_err(<<"begin F=fun() -> hello end, l(F) end.">>), "exception error: variable 'F' is unbound" = comm_err(<<"begin F=fun() -> hello end, f(F), F end.">>), [funkis] = scan(<<"begin F=fun() -> hello end, funkis(F) end.">>), "exception exit: restricted shell does not allow apple(" ++ _ = comm_err(<<"begin F=fun() -> hello end, apple(F) end.">>), "exception exit: restricted shell stopped"= comm_err(<<"begin shell:stop_restricted() end.">>), undefined = application:get_env(stdlib, restricted_shell), (catch code:purge(user_default)), true = (catch code:delete(user_default)), ok. %% f/0 and f/1. forget(Config) when is_list(Config) -> %% f/0 [ok] = scan(<<"begin f() end.">>), "1: variable 'A' is unbound" = comm_err(<<"A = 3, f(), A.">>), [ok] = scan(<<"A = 3, A = f(), A.">>), %% f/1 [ok] = scan(<<"begin f(A) end.">>), "1: variable 'A' is unbound" = comm_err(<<"A = 3, f(A), A.">>), [ok] = scan(<<"A = 3, A = f(A), A.">>), "exception error: no function clause matching call to f/1" = comm_err(<<"f(a).">>), ok. %% Test of the record support. OTP-5063. records(Config) when is_list(Config) -> %% rd/2 [{attribute,_,record,{bar,_}},ok] = scan(<<"rd(foo,{bar}), rd(bar,{foo = (#foo{})#foo.bar}), rl(bar).">>), "variable 'R' is unbound" = % used to work (before OTP-5878, R11B) exit_string(<<"rd(foo,{bar}), R = #foo{}, rd(bar,{foo = R#foo.bar}).">>), "exception error: no function clause matching call to rd/2" = comm_err(<<"rd({foo},{bar}).">>), "bad record declaration" = exit_string(<<"A = bar, rd(foo,A).">>), [foo] = scan(<<"begin rd(foo,{bar}) end.">>), "1: record foo undefined" = comm_err(<<"begin rd(foo,{bar}), #foo{} end.">>), ['f o o'] = scan(<<"rd('f o o', {bar}).">>), [foo] = scan(<<"rd(foo,{bar}), rd(foo,{foo = #foo{}}).">>), %% rf/0,1 [_, {attribute,_,record,{foo,_}},ok] = scan(<<"rf('_'). rd(foo,{bar}),rl().">>), "1: record foo undefined" = comm_err(<<"rd(foo,{bar}), #foo{}, rf(foo), #foo{}.">>), [ok,{foo,undefined}] = scan(<<"rd(foo,{bar}), A = #foo{}, rf(foo). A.">>), [_] = scan(<<"begin rf() end.">>), [ok] = scan(<<"begin rf(foo) end.">>), %% rp/1 "#foo{bar = undefined}.\nok.\n" = t(<<"rd(foo,{bar}), rp(#foo{}).">>), [{foo,3,4,3},ok] = scan(<<"rd(foo,{a = 3, b}), rp({foo,3,4,3}).">>), "#foo{a = 12}.\nok.\n" = t(<<"rd(foo,{a = 3}), rp({foo,12}).">>), [{[{foo}],12},ok] = scan(<<"rd(foo,{a = 3}), rp({[{foo}],12}).">>), %% rr/1,2,3 MS = ?MODULE_STRING, RR1 = "rr(" ++ MS ++ "). #state{}.", "[state]\n" "#state{bin = undefined,reply = undefined,leader = undefined,\n" " unic = latin1}.\n" = t(RR1), RR2 = "rr(" ++ MS ++ ",[state]). #state{}.", "[state]\n" "#state{bin = undefined,reply = undefined,leader = undefined,\n" " unic = latin1}.\n" = t(RR2), RR3 = "rr(" ++ MS ++ ",'_'). #state{}.", "[state]\n" "#state{bin = undefined,reply = undefined,leader = undefined,\n" " unic = latin1}.\n" = t(RR3), RR4 = "rr(" ++ MS ++ ", '_', {d,test1}).", [[state]] = scan(RR4), Test = filename:join(proplists:get_value(priv_dir, Config), "test.erl"), BeamDir = filename:join(proplists:get_value(priv_dir, Config), "beam"), BeamFile = filename:join(BeamDir, "test"), ok = file:make_dir(BeamDir), Contents = <<"-module(test). -record(state, {bin :: binary(), reply = no, leader = some :: atom()}). -ifdef(test1). -record(test1, {f}). -endif. -ifdef(test2). -record(test2, {g}). -endif. ">>, ok = file:write_file(Test, Contents), {ok, test} = compile:file(Test, [{outdir, BeamDir}]), RR5 = "rr(\"" ++ Test ++ "\", '_', {d,test1}), rl([test1,test2]).", A1 = erl_anno:new(1), [{attribute,A1,record,{test1,_}},ok] = scan(RR5), RR6 = "rr(\"" ++ Test ++ "\", '_', {d,test2}), rl([test1,test2]).", [{attribute,A1,record,{test2,_}},ok] = scan(RR6), RR7 = "rr(\"" ++ Test ++ "\", '_', [{d,test1},{d,test2,17}]), rl([test1,test2]).", [{attribute,A1,record,{test1,_}},{attribute,A1,record,{test2,_}},ok] = scan(RR7), PreReply = scan(<<"rr(prim_file).">>), % preloaded... true = is_list(PreReply), Dir = filename:join(proplists:get_value(priv_dir, Config), "*.erl"), RR8 = "rp(rr(\"" ++ Dir ++ "\")).", [_,ok] = scan(RR8), {module, test} = code:load_abs(BeamFile), [[state]] = scan(<<"rr(test).">>), file:delete(Test), file:delete(BeamFile++".beam"), RR1000 = "begin rr(" ++ MS ++ ") end.", [_] = scan(RR1000), RR1001 = "begin rr(" ++ MS ++ ", state) end.", [_] = scan(RR1001), RR1002 = "begin rr(" ++ MS ++ ", state,{i,'.'}) end.", [_] = scan(RR1002), [{error,nofile}] = scan(<<"rr(not_a_module).">>), [{error,invalid_filename}] = scan(<<"rr({foo}).">>), [[]] = scan(<<"rr(\"not_a_file\").">>), %% using records [2] = scan(<<"rd(foo,{bar}), record_info(size, foo).">>), [true] = scan(<<"rd(foo,{bar}), is_record(#foo{}, foo).">>), [true] = scan(<<"rd(foo,{bar}), erlang:is_record(#foo{}, foo).">>), [true] = scan(<<"rd(foo,{bar}), fun() when record(#foo{},foo) -> true end().">>), [2] = scan(<<"rd(foo,{bar}), #foo.bar.">>), "#foo{bar = 17}.\n" = t(<<"rd(foo,{bar}), A = #foo{}, A#foo{bar = 17}.">>), %% test of is_record/2 in lc "[#foo{bar = 3}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "is_record(X, foo)].">>), "[x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "not is_record(X, foo)].">>), "[#foo{bar = 3}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "begin is_record(X, foo) end].">>), "[x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "begin not is_record(X, foo) end].">>), "[#foo{bar = 3},x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "is_record(X, foo) or not is_binary(X)].">>), "[#foo{bar = 3},x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "not is_record(X, foo) or not is_binary(X)].">>), "[#foo{bar = 3}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "is_record(X, foo) or is_reference(X)].">>), "[x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "not is_record(X, foo) or is_reference(X)].">>), "[#foo{bar = 3},x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "begin is_record(X, foo) or not is_binary(X) end].">>), "[#foo{bar = 3},x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "begin not is_record(X, foo) or not is_binary(X) end].">>), "[#foo{bar = 3}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "begin is_record(X, foo) or is_reference(X) end].">>), "[x,[],{a,b}].\n" = t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," "begin not is_record(X, foo) or is_reference(X) end].">>), [ok] = scan(<<"rd(a,{}), is_record({a},a) andalso true, b().">>), %% nested record defs "#b{a = #a{}}.\n" = t(<<"rd(a,{}), rd(b, {a = #a{}}), #b{}.">>), [ok,ok,ok] = scan(<<"rf('_'), rp(rp(rl(rf(rf(rf(rl())))))).">>), ok. %% Test of typed record support. typed_records(Config) when is_list(Config) -> Test = filename:join(proplists:get_value(priv_dir, Config), "test.hrl"), Contents = <<"-module(test). -record(r0,{f :: any()}). -record(r1,{f1 :: #r1{} | undefined, f2 :: #r0{} | atom()}). -record(r2,{f :: #r2{} | undefined}). ">>, ok = file:write_file(Test, Contents), RR1 = "rr(\"" ++ Test ++ "\"), #r1{} = (#r1{f1=#r1{f1=undefined, f2=x}, f2 = #r0{}})#r1.f1, ok.", RR2 = "rr(\"" ++ Test ++ "\"), #r0{} = (#r1{f1=#r1{f1=undefined, f2=x}, f2 = #r0{}})#r1.f2, ok. ", RR3 = "rr(\"" ++ Test ++ "\"), #r1{f2=#r0{}} = (#r1{f1=#r1{f1=undefined, f2=#r0{}}, f2 = x})#r1.f1, ok.", RR4 = "rr(\"" ++ Test ++ "\"), (#r1{f2 = #r0{}})#r1{f2 = x}, ok. ", RR5 = "rr(\"" ++ Test ++ "\"), (#r1{f2 = #r0{}})#r1{f1 = #r1{}}, ok. ", RR6 = "rr(\"" ++ Test ++ "\"), (#r2{f=#r2{f=undefined}})#r2.f, ok.", RR7 = "rr(\"" ++ Test ++ "\"), #r2{} = (#r2{f=#r2{f=undefined}})#r2.f, ok.", [ok] = scan(RR1), [ok] = scan(RR2), [ok] = scan(RR3), [ok] = scan(RR4), [ok] = scan(RR5), [ok] = scan(RR6), [ok] = scan(RR7), file:delete(Test), ok. %% Known bugs. known_bugs(Config) when is_list(Config) -> %% erl_eval:merge_bindings/2 cannot handle _removal_ of bindings. [3] = scan(<<"A = 3, length(begin f(A), [3] end), A.">>), ok. %% OTP-5226. Wildcards accepted when reading BEAM files using rr/1,2,3. otp_5226(Config) when is_list(Config) -> Test1 = <<"-module(test1). -record('_test1', {a,b}).">>, Test2 = <<"-module(test2). -record('_test2', {c,d}).">>, File1 = filename("test1.erl", Config), File2 = filename("test2.erl", Config), Beam = filename("*.beam", Config), ok = compile_file(Config, File1, Test1, [no_debug_info]), ok = compile_file(Config, File2, Test2, [no_debug_info]), RR = "rr(\"" ++ Beam ++ "\").", [Recs] = scan(RR), true = lists:member('_test1', Recs), true = lists:member('_test2', Recs), file:delete(filename("test1.beam", Config)), file:delete(filename("test2.beam", Config)), file:delete(File1), file:delete(File2), ok. %% OTP-5226. Test of eval_bits, mostly. otp_5327(Config) when is_list(Config) -> "exception error: bad argument" = comm_err(<<"<<\"hej\":default>>.">>), L1 = erl_anno:new(1), <<"abc">> = erl_parse:normalise({bin,L1,[{bin_element,L1,{string,L1,"abc"}, default,default}]}), [<<"abc">>] = scan(<<"<<(<<\"abc\">>):3/binary>>.">>), [<<"abc">>] = scan(<<"<<(<<\"abc\">>)/binary>>.">>), "exception error: bad argument" = comm_err(<<"<<(<<\"abc\">>):4/binary>>.">>), true = byte_size(hd(scan("<<3.14:64/float>>."))) =:= 8, true = byte_size(hd(scan("<<3.14:32/float>>."))) =:= 4, "exception error: bad argument" = comm_err(<<"<<3.14:128/float>>.">>), "exception error: bad argument" = comm_err(<<"<<10:default>>.">>), [<<98,1:1>>] = scan(<<"<<3:3,5:6>>.">>), {'EXIT',{badarg,_}} = (catch erl_parse:normalise({bin,L1,[{bin_element,L1,{integer,L1,17}, {atom,L1,all}, default}]})), [<<-20/signed>>] = scan(<<"<<-20/signed>> = <<-20>>.">>), [<<-300:16/signed>>] = scan(<<"<<-300:16/signed>> = <<-300:16>>.">>), [<<-1000:24/signed>>] = scan(<<"<<-1000:24/signed>> = <<-1000:24>>.">>), [<<-(1 bsl 29):32/signed>>] = scan(<<"<<-(1 bsl 29):32/signed>> = <<-(1 bsl 29):32>>.">>), "exception error: no match of right hand side value <<0,0,0>>" = comm_err(<<"<> = <<0:24>>.">>), true = [<<103133:64/float>>] =:= scan(<<"<<103133:64/float>> = <<103133:64/float>>.">>), true = [<<103133.0:64/float>>] =:= scan(<<"<<103133.0:64/float>> = <<103133:64/float>>.">>), true = [<<103133:64/float>>] =:= scan(<<"<<103133:64/float>>.">>), Int = 17, true = [<>] =:= scan(<<"Int = 17, <>.">>), "exception error: no match of right hand side value" ++ _ = comm_err(<<"<<103133:64/binary>> = <<103133:64/float>>.">>), "exception error: interpreted function with arity 1 called with two arguments" = comm_err(<<"(fun(X) -> X end)(a,b).">>), {'EXIT', {{illegal_pattern,_}, _}} = (catch evaluate("<> = <<17:32>>.", [])), C = <<" <> = <<\"hej\">>, case <<7:4,A:4,B:4,C:4,D:4,E:4,F:4,3:4>> of <<_:4,\"hej\",3:4>> -> 1; _ -> 2 end. ">>, 1 = evaluate(C, []), %% unbound_var would be nicer... {'EXIT',{{illegal_pattern,_},_}} = (catch evaluate(<<"<> = <<17:32>>.">>, [])), %% undefined_bittype is turned into badmatch: {'EXIT',{{badmatch,<<17:32>>},_}} = (catch evaluate(<<"<> = <<17:32>>.">>, [])), {'EXIT',_} = (catch evaluate(<<"<<17/binary-unit:8-unit:16>>.">>, [])), {'EXIT',_} = (catch evaluate(<<"<<17:32/unsigned-signed>> = <<17:32>>.">>, [])), {'EXIT',_} = (catch evaluate(<<"<<17:32/unsigned-signed>>.">>, [])), <<17:32>> = evaluate(<<"<<17:32/signed-signed>>.">>, []), {'EXIT',_} = (catch evaluate(<<"<<32/unit:8>>.">>, [])), ok. %% OTP-5435. compiler application not in the path. otp_5435(Config) when is_list(Config) -> true = <<103133:64/float>> =:= evaluate(<<"<<103133:64/float>> = <<103133:64/float>>.">>, []), true = <<103133.0:64/float>> =:= evaluate(<<"<<103133.0:64/float>> = <<103133:64/float>>.">>, []), true = is_alive(), {ok, Node} = start_node(shell_SUITE_otp_5435), ok = rpc:call(Node, ?MODULE, otp_5435_2, []), test_server:stop_node(Node), ok. start_node(Name) -> PA = filename:dirname(code:which(?MODULE)), test_server:start_node(Name, slave, [{args, "-pa " ++ PA}]). otp_5435_2() -> true = code:del_path(compiler), %% Make sure record evaluation is not dependent on the compiler %% application being in the path. %% OTP-5876. [{attribute,_,record,{bar,_}},ok] = scan(<<"rd(foo,{bar}), rd(bar,{foo = (#foo{})#foo.bar}), rl(bar).">>), ok. %% OTP-5195. QLC, mostly. otp_5195(Config) when is_list(Config) -> %% QLC. It was easier to put these cases here than in qlc_SUITE. "[#a{b = undefined}].\n" = t(<<"rd(a,{b}), qlc:e(qlc:q([X || X <- [#a{}],is_record(X, a)])).">>), %% An experimental shell used to translate error tuples: %% "(qlc) \"1: generated variable 'X' must not be used in " %% "list expression\".\n" = %% t(<<"qlc:q([X || X <- [{a}], Y <- [X]]).">>), %% Same as last one (if the shell does not translate error tuples): [{error,qlc,{1,qlc,{used_generator_variable,'X'}}}] = scan(<<"qlc:q([X || X <- [{a}], Y <- [X]]).">>), {error,qlc,{1,qlc,{used_generator_variable,'X'}}} = evaluate(<<"qlc:q([X || X <- [{a}], Y <- [X]]).">>, []), Ugly = <<"qlc:e(qlc:q([X || X <- qlc:append([[1,2,3],ugly()])])).">>, "undefined shell command ugly/0" = error_string(Ugly), {'EXIT',{undef,_}} = (catch evaluate(Ugly, [])), V_1 = <<"qlc:e(qlc:q([X || X <- qlc:append([[1,2,3],v(-1)])])).">>, "- 1: command not found" = comm_err(V_1), {'EXIT', {undef,_}} = (catch evaluate(V_1, [])), "1\n2\n3\n3.\n" = t(<<"1. 2. 3. 3 = fun(A) when A =:= 2 -> v(3) end(v(2)).">>), List4 = t(<<"[a,list]. A = [1,2]. " "qlc:q([X || X <- qlc:append(A, v(1))]). " "[1,2,a,list] = qlc:e(v(-1)).">>), "[1,2,a,list].\n" = string:substr(List4, string:len(List4)-13), ok. %% OTP-5915. Strict record tests in guards. otp_5915(Config) when is_list(Config) -> C = <<" rd(r, {a = 4,b}), rd(r1, {a,b}), rd(r2, {a = #r1{},b,c=length([1,2,3])}), rd(r3, {a = fun(_) -> #r1{} end(1), b}), foo = fun(A) when A#r1.a > A#r1.b -> foo end(#r1{b = 2}), 0 = fun(A) when A#r2.a -> 0 end(#r2{a = true}), 1 = fun(A) when (#r1{a = A})#r1.a > 2 -> 1 end(3), 2 = fun(N) when ((#r2{a = #r{a = 4}, b = length([a,b,c])})#r2.a)#r.a > N -> 2 end(2), 3 = fun(A) when (A#r2.a)#r1.a =:= 3 -> 3 end(#r2{a = #r1{a = 3}}), ok = fun() -> F = fun(A) when record(A#r.a, r1) -> 4; (A) when record(A#r1.a, r1) -> 5 end, 5 = F(#r1{a = #r1{}}), 4 = F(#r{a = #r1{}}), ok end(), 3 = fun(A) when record(A#r1.a, r), (A#r1.a)#r.a > 3 -> 3 end(#r1{a = #r{a = 4}}), 7 = fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}), [#r1{a = 2,b = 1}] = fun() -> [A || A <- [#r1{a = 1, b = 3}, #r2{a = 2,b = 1}, #r1{a = 2, b = 1}], A#r1.a > A#r1.b] end(), {[_],b} = fun(L) -> %% A is checked only once: R1 = [{A,B} || A <- L, A#r1.a, B <- L, A#r1.b], A = #r2{a = true}, %% A is checked again: B = if A#r1.a -> a; true -> b end, {R1,B} end([#r1{a = true, b = true}]), p = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; (_) -> p end(#r1{a = 2}), o = fun(A) when (A#r1.a =:= 2) orelse (A#r2.a =:= 1) -> o; (_) -> p end(#r1{a = 2}), 3 = fun(A) when A#r1.a > 3, record(A, r1) -> 3 end(#r1{a = 5}), ok = fun() -> F = fun(A) when (A#r2.a =:= 1) orelse (A#r2.a) -> 2; (A) when (A#r1.a =:= 1) orelse (A#r1.a) -> 1; (A) when (A#r2.a =:= 2) andalso (A#r2.b) -> 3 end, 1 = F(#r1{a = 1}), 2 = F(#r2{a = true}), 3 = F(#r2{a = 2, b = true}), ok end(), b = fun(A) when false or not (A#r.a =:= 1) -> a; (_) -> b end(#r1{a = 1}), b = fun(A) when not (A#r.a =:= 1) or false -> a; (_) -> b end(#r1{a = 1}), ok = fun() -> F = fun(A) when not (A#r.a =:= 1) -> yes; (_) -> no end, no = F(#r1{a = 2}), yes = F(#r{a = 2}), no = F(#r{a = 1}), ok end(), a = fun(A) when record(A, r), A#r.a =:= 1, A#r.b =:= 2 ->a end(#r{a = 1, b = 2}), a = fun(A) when erlang:is_record(A, r), A#r.a =:= 1, A#r.b =:= 2 -> a end(#r{a = 1, b = 2}), a = fun(A) when is_record(A, r), A#r.a =:= 1, A#r.b =:= 2 -> a end(#r{a = 1, b = 2}), nop = fun(A) when (is_record(A, r1) and (A#r1.a > 3)) or (A#r2.a < 1) -> japp; (_) -> nop end(#r2{a = 0}), nop = fun(A) when (A#r1.a > 3) or (A#r2.a < 1) -> japp; (_) -> nop end(#r2{a = 0}), ok = fun() -> F = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; (_) -> p end, p = F(#r2{a = 1}), p = F(#r1{a = 2}), ok end(), ok = fun() -> F = fun(A) when fail, A#r1.a; A#r1.a -> ab; (_) -> bu end, ab = F(#r1{a = true}), bu = F(#r2{a = true}), ok end(), both = fun(A) when A#r.a, A#r.b -> both end(#r{a = true, b = true}), ok = fun() -> F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) or (B#r2.b) or (A#r1.b) -> true; (_, _) -> false end, true = F(#r1{a = false, b = false}, #r2{a = false, b = true}), false = F(#r1{a = true, b = true}, #r1{a = false, b = true}), ok end(), ok.">>, [ok] = scan(C), ok. %% OTP-5916. erlang:is_record/3 allowed in guards. otp_5916(Config) when is_list(Config) -> C = <<" rd(r1, {a,b}), rd(r2, {a,b}), true = if erlang:is_record(#r1{},r1,3) -> true; true -> false end, false = if erlang:is_record(#r2{},r1,3) -> true; true -> false end, true = if is_record(#r1{},r1,3) -> true; true -> false end, false = if is_record(#r2{},r1,3) -> true; true -> false end, ok.">>, [ok] = scan(C), ok. %% OTP-5327. Adopted from parts of emulator/test/bs_match_misc_SUITE.erl. bs_match_misc_SUITE(Config) when is_list(Config) -> C = <<" F1 = fun() -> 3.1415 end, FOne = fun() -> 1.0 end, Fcmp = fun(F1, F2) when (F1 - F2) / F2 < 0.0000001 -> ok end, MakeSubBin = fun(Bin0) -> Sz = size(Bin0), Bin1 = <<37,Bin0/binary,38,39>>, <<_:8,Bin:Sz/binary,_:8,_:8>> = Bin1, Bin end, MatchFloat = fun(Bin0, Fsz, I) -> Bin = MakeSubBin(Bin0), Bsz = size(Bin) * 8, Tsz = Bsz - Fsz - I, <<_:I,F:Fsz/float,_:Tsz>> = Bin, F end, TFloat = fun() -> F = F1(), G = FOne(), G = MatchFloat(<<63,128,0,0>>, 32, 0), G = MatchFloat(<<63,240,0,0,0,0,0,0>>, 64, 0), Fcmp(F, MatchFloat(<>, 32, 0)), Fcmp(F, MatchFloat(<>, 64, 0)), Fcmp(F, MatchFloat(<<1:1,F:32/float,127:7>>, 32, 1)), Fcmp(F, MatchFloat(<<1:1,F:64/float,127:7>>, 64, 1)), Fcmp(F, MatchFloat(<<1:13,F:32/float,127:3>>, 32, 13)), Fcmp(F, MatchFloat(<<1:13,F:64/float,127:3>>, 64, 13)) end, TFloat(), F2 = fun() -> 2.7133 end, MatchFloatLittle = fun(Bin0, Fsz, I) -> Bin = MakeSubBin(Bin0), Bsz = size(Bin) * 8, Tsz = Bsz - Fsz - I, <<_:I,F:Fsz/float-little,_:Tsz>> = Bin, F end, LittleFloat = fun() -> F = F2(), G = FOne(), G = MatchFloatLittle(<<0,0,0,0,0,0,240,63>>, 64, 0), G = MatchFloatLittle(<<0,0,128,63>>, 32, 0), Fcmp(F, MatchFloatLittle(<>, 32, 0)), Fcmp(F, MatchFloatLittle(<>, 64, 0)), Fcmp(F, MatchFloatLittle(<<1:1,F:32/float-little,127:7>>, 32, 1)), Fcmp(F, MatchFloatLittle(<<1:1,F:64/float-little,127:7>>, 64, 1)), Fcmp(F, MatchFloatLittle(<<1:13,F:32/float-little,127:3>>, 32, 13)), Fcmp(F, MatchFloatLittle(<<1:13,F:64/float-little,127:3>>, 64, 13)) end, LittleFloat(), Sean1 = fun(<>) when size(B) < 4 -> small; (<<1, _B/binary>>) -> large end, Sean = fun() -> small = Sean1(<<>>), small = Sean1(<<1>>), small = Sean1(<<1,2>>), small = Sean1(<<1,2,3>>), large = Sean1(<<1,2,3,4>>), small = Sean1(<<4>>), small = Sean1(<<4,5>>), small = Sean1(<<4,5,6>>), {'EXIT',{function_clause,_}} = (catch Sean1(<<4,5,6,7>>)) end, Sean(), NativeBig = fun() -> <<37.33:64/native-float>> = <<37.33:64/big-float>>, <<3974:16/native-integer>> = <<3974:16/big-integer>> end, NativeLittle = fun() -> <<37869.32343:64/native-float>> = <<37869.32343:64/little-float>>, <<7974:16/native-integer>> = <<7974:16/little-integer>> end, Native = fun() -> <<3.14:64/native-float>> = <<3.14:64/native-float>>, <<333:16/native>> = <<333:16/native>>, <<38658345:32/native>> = <<38658345:32/native>>, case <<1:16/native>> of <<0,1>> -> NativeBig(); <<1,0>> -> NativeLittle() end end, Native(), Split = fun(<>) -> {B,T} end, Split2 = fun(N, <>) -> {B,T} end, Split_2 = fun(<>) -> {B,T} end, Skip = fun(<>) -> T end, SizeVar = fun() -> {<<45>>,<<>>} = Split(<<1:16,45>>), {<<45>>,<<46,47>>} = Split(<<1:16,45,46,47>>), {<<45,46>>,<<47>>} = Split(<<2:16,45,46,47>>), {<<45,46,47>>,<<48>>} = Split_2(<<16:8,3:16,45,46,47,48>>), {<<45,46>>,<<47>>} = Split2(2, <<2:16,45,46,47>>), {'EXIT',{function_clause,_}} = (catch Split2(42, <<2:16,45,46,47>>)), <<\"cdef\">> = Skip(<<2:8,\"abcdef\">>) end, SizeVar(), Wcheck = fun(<>) when A==3-> ok1; (<<_,_:2/binary>>) -> ok2; (<<_>>) -> ok3; (Other) -> {error,Other} end, Wiger = fun() -> ok1 = Wcheck(<<3>>), ok2 = Wcheck(<<1,2,3>>), ok3 = Wcheck(<<4>>), {error,<<1,2,3,4>>} = Wcheck(<<1,2,3,4>>), {error,<<>>} = Wcheck(<<>>) end, Wiger(), ok. ">>, [ok] = scan(C), ok = evaluate(C, []). %% This one is not run during night builds since it takes several minutes. %% OTP-5327. Adopted from emulator/test/bs_match_int_SUITE.erl. bs_match_int_SUITE(Config) when is_list(Config) -> C = <<" FunClause = fun({'EXIT',{function_clause,_}}) -> ok end, Mkbin = fun(L) when list(L) -> list_to_binary(L) end, GetInt1 = fun(<>) -> I; (<>) -> I; (<>) -> I; (<>) -> I; (<>) -> I end, GetInt2 = fun(Bin0, I, F) when size(Bin0) < 4 -> Bin = <<0,Bin0/binary>>, I = GetInt1(Bin), F(Bin, I, F); (_, I, _F) -> I end, GetInt = fun(Bin) -> I = GetInt1(Bin), GetInt2(Bin, I, GetInt2) end, Cmp128 = fun(<>, I) -> equal; (_, _) -> not_equal end, Uint2 = fun([H|T], Acc, F) -> F(T, Acc bsl 8 bor H, F); ([], Acc, _F) -> Acc end, Uint = fun(L) -> Uint2(L, 0, Uint2) end, Integer = fun() -> 0 = GetInt(Mkbin([])), 0 = GetInt(Mkbin([0])), 42 = GetInt(Mkbin([42])), 255 = GetInt(Mkbin([255])), 256 = GetInt(Mkbin([1,0])), 257 = GetInt(Mkbin([1,1])), 258 = GetInt(Mkbin([1,2])), 258 = GetInt(Mkbin([1,2])), 65534 = GetInt(Mkbin([255,254])), 16776455 = GetInt(Mkbin([255,253,7])), 4245492555 = GetInt(Mkbin([253,13,19,75])), 4294967294 = GetInt(Mkbin([255,255,255,254])), 4294967295 = GetInt(Mkbin([255,255,255,255])), Eight = [200,1,19,128,222,42,97,111], Cmp128(Eight, Uint(Eight)), FunClause(catch GetInt(Mkbin(lists:seq(1,5)))) end, Integer(), Sint = fun(Bin) -> case Bin of <> -> I; <> -> I; Other -> {no_match,Other} end end, SignedInteger = fun() -> {no_match,_} = Sint(Mkbin([])), {no_match,_} = Sint(Mkbin([1,2,3])), 127 = Sint(Mkbin([127])), -1 = Sint(Mkbin([255])), -128 = Sint(Mkbin([128])), 42 = Sint(Mkbin([42,255])), 127 = Sint(Mkbin([127,255])) end, SignedInteger(), Dynamic5 = fun(Bin, S1, S2, A, B) -> case Bin of <> -> %% io:format(\"~p ~p ~p ~p~n\", [S1,S2,A,B]), ok; _Other -> erlang:error(badmatch, [Bin,S1,S2,A,B]) end end, Dynamic2 = fun(Bin, S1, F) when S1 >= 0 -> S2 = size(Bin) * 8 - S1, Dynamic5(Bin, S1, S2, (1 bsl S1) - 1, (1 bsl S2) - 1), F(Bin, S1-1, F); (_, _, _) -> ok end, Dynamic = fun(Bin, S1) -> Dynamic2(Bin, S1, Dynamic2) end, Dynamic(Mkbin([255]), 8), Dynamic(Mkbin([255,255]), 16), Dynamic(Mkbin([255,255,255]), 24), Dynamic(Mkbin([255,255,255,255]), 32), BigToLittle4 = fun([B0,B1,B2,B3,B4,B5,B6,B7|T], N, Acc, F) when N >= 8 -> F(T, N-8, [B0,B1,B2,B3,B4,B5,B6,B7|Acc], F); (List, N, Acc, _F) -> lists:sublist(List, 1, N) ++ Acc end, BigToLittle = fun(List, N) -> BigToLittle4(List, N, [], BigToLittle4) end, ReversedSublist = fun(_List, 0, Acc, _F) -> Acc; ([H|T], N, Acc, F) -> F(T, N-1, [H|Acc], F) end, TwoComplementAndReverse = fun([H|T], Carry, Acc, F) -> Sum = 1-H+Carry, F(T, Sum div 2, [Sum rem 2|Acc], F); ([], Carry, Acc, _F) -> [Carry|Acc] end, MakeInt = fun(_List, 0, Acc, _F) -> Acc; ([H|T], N, Acc, F) -> F(T, N-1, Acc bsl 1 bor H, F) end, MakeSignedInt = fun(_List, 0) -> 0; ([0|_]=List, N) -> MakeInt(List, N, 0, MakeInt); ([1|_]=List0, N) -> List1 = ReversedSublist(List0, N, [], ReversedSublist), List2 = TwoComplementAndReverse(List1, 1, [], TwoComplementAndReverse), -MakeInt(List2, length(List2), 0, MakeInt) end, BitsToList = fun([H|T], 0, F) -> F(T, 16#80, F); ([H|_]=List, Mask, F) -> [case H band Mask of 0 -> 0; _ -> 1 end | F(List, Mask bsr 1, F)]; ([], _, _F) -> [] end, MoreDynamic3 = fun(Action, Bin, List, Bef, Aft, F) when Bef =< Aft -> Action(Bin, List, Bef, Aft-Bef), F(Action, Bin, List, Bef, Aft-1, F); (_, _, _, _, _, _) -> ok end, MoreDynamic2 = fun(Action, Bin, [_|T]=List, Bef, F) -> MoreDynamic3(Action, Bin, List, Bef, size(Bin)*8, MoreDynamic3), F(Action, Bin, T, Bef+1, F); (_, _, [], _, _F) -> ok end, MoreDynamic1 = fun(Action, Bin) -> BitList = BitsToList(binary_to_list(Bin),16#80,BitsToList), MoreDynamic2(Action, Bin, BitList, 0, MoreDynamic2) end, MoreDynamic = fun() -> %% Unsigned big-endian numbers. Unsigned = fun(Bin, List, SkipBef, N) -> SkipAft = 8*size(Bin) - N - SkipBef, <<_:SkipBef,Int:N,_:SkipAft>> = Bin, Int = MakeInt(List, N, 0, MakeInt) end, MoreDynamic1(Unsigned, erlang:md5(Mkbin([42]))), %% Signed big-endian numbers. Signed = fun(Bin, List, SkipBef, N) -> SkipAft = 8*size(Bin) - N - SkipBef, <<_:SkipBef,Int:N/signed,_:SkipAft>> = Bin, case MakeSignedInt(List, N) of Int -> ok; Other -> io:format(\"Bin = ~p,\", [Bin]), io:format(\"SkipBef = ~p, N = ~p\", [SkipBef,N]), io:format(\"Expected ~p, got ~p\", [Int,Other]) end end, MoreDynamic1(Signed, erlang:md5(Mkbin([43]))), %% Unsigned little-endian numbers. UnsLittle = fun(Bin, List, SkipBef, N) -> SkipAft = 8*size(Bin) - N - SkipBef, <<_:SkipBef,Int:N/little,_:SkipAft>> = Bin, Int = MakeInt(BigToLittle(List, N), N, 0, MakeInt) end, MoreDynamic1(UnsLittle, erlang:md5(Mkbin([44]))), %% Signed little-endian numbers. SignLittle = fun(Bin, List, SkipBef, N) -> SkipAft = 8*size(Bin) - N - SkipBef, <<_:SkipBef,Int:N/signed-little,_:SkipAft>> = Bin, Little = BigToLittle(List, N), Int = MakeSignedInt(Little, N) end, MoreDynamic1(SignLittle, erlang:md5(Mkbin([45]))) end, MoreDynamic(), ok. ">>, [ok] = scan(C), ok = evaluate(C, []). %% OTP-5327. Adopted from emulator/test/bs_match_tail_SUITE.erl. bs_match_tail_SUITE(Config) when is_list(Config) -> C = <<" GetTailUsed = fun(<>) -> {A,T} end, GetTailUnused = fun(<>) -> A end, GetDynTailUsed = fun(Bin, Sz) -> <> = Bin, {A,T} end, GetDynTailUnused = fun(Bin, Sz) -> <> = Bin, A end, Mkbin = fun(L) when list(L) -> list_to_binary(L) end, TestZeroTail = fun(<>) -> A end, TestZeroTail2 = fun(<<_A:4,_B:4>>) -> ok end, ZeroTail = fun() -> 7 = (catch TestZeroTail(Mkbin([7]))), {'EXIT',{function_clause,_}} = (catch TestZeroTail(Mkbin([1,2]))), {'EXIT',{function_clause,_}} = (catch TestZeroTail2(Mkbin([1,2,3]))) end, ZeroTail(), AlGetTailUsed = fun(<>) -> {A,T} end, AlGetTailUnused = fun(<>) -> A end, Aligned = fun() -> Tail1 = Mkbin([]), {258,Tail1} = AlGetTailUsed(Mkbin([1,2])), Tail2 = Mkbin(lists:seq(1, 127)), {35091,Tail2} = AlGetTailUsed(Mkbin([137,19|Tail2])), 64896 = AlGetTailUnused(Mkbin([253,128])), 64895 = AlGetTailUnused(Mkbin([253,127|lists:seq(42, 255)])), Tail3 = Mkbin(lists:seq(0, 19)), {0,Tail1} = GetDynTailUsed(Tail1, 0), {0,Tail3} = GetDynTailUsed(Mkbin([Tail3]), 0), {73,Tail3} = GetDynTailUsed(Mkbin([73|Tail3]), 8), 0 = GetDynTailUnused(Mkbin([]), 0), 233 = GetDynTailUnused(Mkbin([233]), 8), 23 = GetDynTailUnused(Mkbin([23,22,2]), 8) end, Aligned(), UnAligned = fun() -> {'EXIT',{function_clause,_}} = (catch GetTailUsed(Mkbin([42]))), {'EXIT',{{badmatch,_},_}} = (catch GetDynTailUsed(Mkbin([137]), 3)), {'EXIT',{function_clause,_}} = (catch GetTailUnused(Mkbin([42,33]))), {'EXIT',{{badmatch,_},_}} = (catch GetDynTailUnused(Mkbin([44]), 7)) end, UnAligned(), ok. ">>, [ok] = scan(C), ok = evaluate(C, []). %% OTP-5327. Adopted from emulator/test/bs_match_bin_SUITE.erl. bs_match_bin_SUITE(Config) when is_list(Config) -> ByteSplitBinary = <<"ByteSplit = fun(L, B, Pos, Fun) when Pos >= 0 -> Sz1 = Pos, Sz2 = size(B) - Pos, <> = B, B1 = list_to_binary(lists:sublist(L, 1, Pos)), B2 = list_to_binary(lists:nthtail(Pos, L)), Fun(L, B, Pos-1, Fun); (L, B, _, _Fun) -> ok end, Mkbin = fun(L) when list(L) -> list_to_binary(L) end, L = lists:seq(0, 57), B = Mkbin(L), ByteSplit(L, B, size(B), ByteSplit), Id = fun(I) -> I end, MakeUnalignedSubBinary = fun(Bin0) -> Bin1 = <<0:3,Bin0/binary,31:5>>, Sz = size(Bin0), <<0:3,Bin:Sz/binary,31:5>> = Id(Bin1), Bin end, Unaligned = MakeUnalignedSubBinary(B), ByteSplit(L, Unaligned, size(Unaligned), ByteSplit), ok. ">>, [ok] = scan(ByteSplitBinary), ok = evaluate(ByteSplitBinary, []), BitSplitBinary = <<"Mkbin = fun(L) when list(L) -> list_to_binary(L) end, MakeInt = fun(List, 0, Acc, _F) -> Acc; ([H|T], N, Acc, F) -> F(T, N-1, Acc bsl 1 bor H, F) end, MakeBinFromList = fun(List, 0, _F) -> Mkbin([]); (List, N, F) -> list_to_binary([MakeInt(List, 8, 0, MakeInt), F(lists:nthtail(8, List), N-8, F)]) end, BitSplitBinary3 = fun(Action, Bin, List, Bef, Aft, F) when Bef =< Aft -> Action(Bin, List, Bef, (Aft-Bef) div 8 * 8), F(Action, Bin, List, Bef, Aft-8, F); (_, _, _, _, _, _) -> ok end, BitSplitBinary2 = fun(Action, Bin, [_|T]=List, Bef, F) -> BitSplitBinary3(Action, Bin, List, Bef, size(Bin)*8, BitSplitBinary3), F(Action, Bin, T, Bef+1, F); (Action, Bin, [], Bef, F) -> ok end, BitsToList = fun([H|T], 0, F) -> F(T, 16#80, F); ([H|_]=List, Mask, F) -> [case H band Mask of 0 -> 0; _ -> 1 end | F(List, Mask bsr 1, F)]; ([], _, _F) -> [] end, BitSplitBinary1 = fun(Action, Bin) -> BitList = BitsToList(binary_to_list(Bin), 16#80, BitsToList), BitSplitBinary2(Action, Bin, BitList, 0, BitSplitBinary2) end, Fun = fun(Bin, List, SkipBef, N) -> SkipAft = 8*size(Bin) - N - SkipBef, <> = Bin, OutBin = MakeBinFromList(List, N, MakeBinFromList) end, BitSplitBinary1(Fun, erlang:md5(<<1,2,3>>)), Id = fun(I) -> I end, MakeUnalignedSubBinary = fun(Bin0) -> Bin1 = <<0:3,Bin0/binary,31:5>>, Sz = size(Bin0), <<0:3,Bin:Sz/binary,31:5>> = Id(Bin1), Bin end, BitSplitBinary1(Fun, MakeUnalignedSubBinary(erlang:md5(<<1,2,3>>))), ok. ">>, [ok] = scan(BitSplitBinary), ok = evaluate(BitSplitBinary, []). -define(FAIL(Expr), "{'EXIT',{badarg,_}} = (catch " ??Expr ")"). -define(COF(Int0), "(fun(Int) -> true = <> =:= <<(float(Int)):32/float>>, true = <> =:= <<(float(Int)):64/float>> end)(Nonliteral(" ??Int0 ")), true = <<" ??Int0 ":32/float>> =:= <<(float("??Int0")):32/float>>, true = <<" ??Int0 ":64/float>> =:= <<(float("??Int0")):64/float>>"). -define(COF64(Int0), "(fun(Int) -> true = <> =:= <<(float(Int)):64/float>> end)(Nonliteral(" ??Int0 ")), true = <<" ??Int0 ":64/float>> =:= <<(float("??Int0")):64/float>>"). %% OTP-5327. Adopted from parts of emulator/test/bs_construct_SUITE.erl. bs_construct_SUITE(Config) when is_list(Config) -> C1 = <<" Testf_1 = fun(W, B) -> " ?FAIL(<<42:W>>) "," ?FAIL(<<3.14:W/float>>) "," ?FAIL(<>) " end, TestF = fun() -> " ?FAIL(<<3.14>>) "," ?FAIL(<<<<1,2>>>>) "," ?FAIL(<<2.71/binary>>) "," ?FAIL(<<24334/binary>>) "," ?FAIL(<<24334344294788947129487129487219847/binary>>) "," ?FAIL(<<<<1,2,3>>/float>>) ", %% Negative field widths. Testf_1(-8, <<1,2,3,4,5>>)," ?FAIL(<<42:(-16)>>) "," ?FAIL(<<3.14:(-8)/float>>) "," ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>) "," ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>) "," ?FAIL(<<<<23,56,0,2>>:(anka)>>) " end, TestF(), NotUsed1 = fun(I, BinString) -> <>, ok end, NotUsed2 = fun(I, Sz) -> <>, ok end, NotUsed3 = fun(I) -><>, ok end, NotUsed = fun() -> ok = NotUsed1(3, <<\"dum\">>), {'EXIT',{badarg,_}} = (catch NotUsed1(3, \"dum\")), " ?FAIL(NotUsed2(444, -2)) "," ?FAIL(NotUsed2(444, anka)) "," ?FAIL(NotUsed3(444)) " end, NotUsed(), InGuard3 = fun(Bin, A, B) when <> == Bin -> 1; (Bin, A, B) when <> == Bin -> 2; (Bin, A, B) when <> == Bin -> 3; (Bin, A, B) when {a,b,<>} == Bin -> cant_happen; (_, _, _) -> nope end, InGuard = fun() -> 1 = InGuard3(<<16#74ad:16>>, 16#e95, 5), 2 = InGuard3(<<16#3A,16#F7,\"hello\">>, 16#3AF7, <<\"hello\">>), 3 = InGuard3(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), nope = InGuard3(<<1>>, 42, b), nope = InGuard3(<<1>>, a, b), nope = InGuard3(<<1,2>>, 1, 1), nope = InGuard3(<<4,5>>, 1, 2.71), nope = InGuard3(<<4,5>>, 1, <<12,13>>) end, InGuard(), Nonliteral = fun(X) -> X end, CoerceToFloat = fun() -> " ?COF(0) "," ?COF(-1) "," ?COF(1) "," ?COF(42) "," ?COF(255) "," ?COF(-255) "," ?COF64(298748888888888888888888888883478264866528467367364766666666666666663) "," ?COF64(-367546729879999999999947826486652846736736476555566666663) " end, CoerceToFloat(), ok. ">>, [ok] = scan(C1), ok = evaluate(C1, []), %% There is another one, lib/compiler/test/bs_construct_SUITE.erl... C2 = <<" I = fun(X) -> X end, Fail = fun() -> I_minus_777 = I(-777), I_minus_2047 = I(-2047), %% One negative field size, but the sum of field sizes will be 1 byte. %% Make sure that we reject that properly. {'EXIT',{badarg,_}} = (catch <>), %% Same thing, but use literals. {'EXIT',{badarg,_}} = (catch <>), %% Bad alignment. I_one = I(1), <<1:1>> = <<2375:I_one>>, <<3:2>> = <<45:1,2375:I_one>>, <<14:4>> = <<45:1,2375:I_one,918:2>>, <<118:7>> = <<45:1,2375:I_one,918:5>>, %% Not numbers. {'EXIT',{badarg,_}} = (catch <<45:(I(not_a_number))>>), {'EXIT',{badarg,_}} = (catch <<13:8,45:(I(not_a_number))>>), %% Unaligned sizes. BadSz = I(7), <<2:4>> = <<34:4>>, <<34:7>> = <<34:BadSz>>, [] = [X || {X} <- [], X == <<3:BadSz>>], [] = [X || {X} <- [], X == <<3:4>>] end, Fail(), FloatBin1 = fun(F) -> {<<1,2,3>>,F+3.0} end, FloatBin = fun() -> %% Some more coverage. {<<1,2,3>>,7.0} = FloatBin1(4) end, FloatBin(), ok. ">>, [ok] = scan(C2), ok = evaluate(C2, []). evaluate(B, Vars) when is_binary(B) -> evaluate(binary_to_list(B), Vars); evaluate(Str, Vars) -> {ok,Tokens,_} = erl_scan:string(Str), {ok, Exprs} = erl_parse:parse_exprs(Tokens), case erl_eval:exprs(Exprs, Vars, none) of {value, Result, _} -> Result end. %% Bit syntax examples from the Reference Manual. OTP-5237. refman_bit_syntax(Config) when is_list(Config) -> %% Reference Manual "Bit Syntax Expressions" Bin1 = <<1,17,42>>, true = [1,17,42] =:= binary_to_list(Bin1), Bin2 = <<"abc">>, true = "abc" =:= binary_to_list(Bin2), Bin3 = <<1,17,42:16>>, true = [1,17,0,42] =:= binary_to_list(Bin3), <<_A,_B,C:16>> = <<1,17,42:16>>, true = C =:= 42, <> = <<1,17,42:16>>, true = D =:= 273, true = F =:= 42, <<_G,H/binary>> = <<1,17,42:16>>, true = H =:= <<17,0,42>>, [ok] = scan(<<"Bin1 = <<1,17,42>>, true = [1,17,42] =:= binary_to_list(Bin1), Bin2 = <<\"abc\">>, true = \"abc\" =:= binary_to_list(Bin2), Bin3 = <<1,17,42:16>>, true = [1,17,0,42] =:= binary_to_list(Bin3), <> = <<1,17,42:16>>, true = C =:= 42, <> = <<1,17,42:16>>, true = D =:= 273, true = F =:= 42, <> = <<1,17,42:16>>, true = H =:= <<17,0,42>>, ok.">>), %% Binary comprehensions. <<2,4,6>> = << << (X*2) >> || <> <= << 1,2,3 >> >>, ok. -define(IP_VERSION, 4). -define(IP_MIN_HDR_LEN, 5). %% Bit syntax examples from Programming Examples. OTP-5237. progex_bit_syntax(Config) when is_list(Config) -> Bin11 = <<1, 17, 42>>, true = [1, 17, 42] =:= binary_to_list(Bin11), Bin12 = <<"abc">>, true = [97, 98, 99] =:= binary_to_list(Bin12), A = 1, B = 17, C = 42, Bin2 = <>, true = [1, 17, 00, 42] =:= binary_to_list(Bin2), <> = Bin2, true = D =:= 273, true = E =:= 00, true = [42] =:= binary_to_list(F), Fun4 = fun(Dgram) -> DgramSize = byte_size(Dgram), case Dgram of <> when HLen>=5, 4*HLen= OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN), <> = RestDgram, {SrvcType, TotLen, Flgs, FragOff, ID, HdrChkSum, Proto, TTL, SrcIP, DestIP, Data, Opts}; _ -> not_ok end end, true = Fun4(<<>>) =:= not_ok, true = is_tuple(Fun4(list_to_binary([<>, list_to_binary(lists:seq(1,255))]))), X = 23432324, Y = 24324234, <<10:7>> = <>, Z = 234324324, XYZ = <>, true = [20] =:= binary_to_list(XYZ), Hello1 = <<"hello">>, Hello2 = <<$h,$e,$l,$l,$o>>, true = "hello" =:= binary_to_list(Hello1), true = "hello" =:= binary_to_list(Hello2), FunM1 = fun(<>) -> {X1,Y1} end, true = {<<"1234567">>,<<"8">>} =:= FunM1(<<"12345678">>), FunM2 = fun(<<_X1:7/binary-unit:7, _Y1:1/binary-unit:1>>) -> ok; (_) -> not_ok end, true = not_ok =:= FunM2(<<"1">>), BL = [{3,4,5},{6,7,8}], Lst = [0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8], B1 = triples_to_bin1(BL), true = Lst =:= binary_to_list(B1), B2 = triples_to_bin2(BL), true = Lst =:= binary_to_list(B2), [ok] = scan( <<"Bin11 = <<1, 17, 42>>, true = [1, 17, 42] =:= binary_to_list(Bin11), Bin12 = <<\"abc\">>, true = [97, 98, 99] =:= binary_to_list(Bin12), A = 1, B = 17, C = 42, Bin2 = <>, true = [1, 17, 00, 42] =:= binary_to_list(Bin2), <> = Bin2, true = D =:= 273, true = E =:= 00, true = [42] =:= binary_to_list(F), Fun4 = fun(Dgram) -> DgramSize = byte_size(Dgram), case Dgram of <<4:4, HLen:4, SrvcType:8, TotLen:16, ID:16, Flgs:3, FragOff:13, TTL:8, Proto:8, HdrChkSum:16, SrcIP:32, DestIP:32, RestDgram/binary>> when HLen>=5, 4*HLen= OptsLen = 4*(HLen - 5), <> = RestDgram, {SrvcType, TotLen, Flgs, FragOff, ID, HdrChkSum, Proto, TTL, SrcIP, DestIP, Data, Opts}; _ -> not_ok end end, true = Fun4(<<>>) =:= not_ok, true = is_tuple(Fun4(list_to_binary ([<<4:4,5:4>>,list_to_binary(lists:seq(1,255))]))), X = 23432324, Y = 24324234, <<10:7>> = <>, Z = 234324324, XYZ = <>, true = [20] =:= binary_to_list(XYZ), Hello1 = <<\"hello\">>, Hello2 = <<$h,$e,$l,$l,$o>>, true = \"hello\" =:= binary_to_list(Hello1), true = \"hello\" =:= binary_to_list(Hello2), FunM1 = fun(<>) -> {X1,Y1} end, true = {<<\"1234567\">>,<<\"8\">>} =:= FunM1(<<\"12345678\">>), FunM2 = fun(<<_X1:7/binary-unit:7, _Y1:1/binary-unit:1>>) -> ok; (_) -> not_ok end, true = not_ok =:= FunM2(<<\"1\">>), ok.">>), ok. triples_to_bin1(T) -> triples_to_bin1(T, <<>>). triples_to_bin1([{X,Y,Z} | T], Acc) -> triples_to_bin1(T, <>); % inefficient triples_to_bin1([], Acc) -> Acc. triples_to_bin2(T) -> triples_to_bin2(T, []). triples_to_bin2([{X,Y,Z} | T], Acc) -> triples_to_bin2(T, [<> | Acc]); triples_to_bin2([], Acc) -> list_to_binary(lists:reverse(Acc)). %% Record examples from Programming Examples. OTP-5237. progex_records(Config) when is_list(Config) -> Test1 = <<"-module(recs). -record(person, {name = \"\", phone = [], address}). -record(name, {first = \"Robert\", last = \"Ericsson\"}). -record(person2, {name = #name{}, phone}). -export([t/0]). t() -> _P1 = #person{phone=[0,8,2,3,4,3,1,2], name=\"Robert\"}, \"Robert\" = _P1#person.name, [0,8,2,3,4,3,1,2] = _P1#person.phone, undefined = _P1#person.address, _P2 = #person{name = \"Jakob\", _ = '_'}, \"Jakob\" = _P2#person.name, '_' = _P2#person.phone, '_' = _P2#person.address, P = #person{name = \"Joe\", phone = [0,8,2,3,4,3,1,2]}, \"Joe\" = P#person.name, [0,8,2,3,4,3,1,2] = P#person.phone, undefined = P#person.address, P1 = #person{name=\"Joe\", phone=[1,2,3], address=\"A street\"}, P2 = P1#person{name=\"Robert\"}, \"Robert\" = P2#person.name, [1,2,3] = P2#person.phone, \"A street\" = P2#person.address, a_person = foo(P1), {found, [1,2,3]} = find_phone([#person{name = a}, #person{name = b, phone = [3,2,1]}, #person{name = c, phone = [1,2,3]}], c), P3 = #person{name=\"Joe\", phone=[0,0,7], address=\"A street\"}, #person{name = Name} = P3, \"Joe\" = Name, \"Robert\" = demo(), ok. foo(P) when is_record(P, person) -> a_person; foo(_) -> not_a_person. find_phone([#person{name=Name, phone=Phone} | _], Name) -> {found, Phone}; find_phone([_| T], Name) -> find_phone(T, Name); find_phone([], _Name) -> not_found. demo() -> P = #person2{name= #name{first=\"Robert\",last=\"Virding\"}, phone=123}, _First = (P#person2.name)#name.first. ">>, ok = run_file(Config, recs, Test1), Test1_shell = <<"rd(person, {name = \"\", phone = [], address}), rd(name, {first = \"Robert\", last = \"Ericsson\"}), rd(person2, {name = #name{}, phone}), _P1 = #person{phone=[0,8,2,3,4,3,1,2], name=\"Robert\"}, \"Robert\" = _P1#person.name, [0,8,2,3,4,3,1,2] = _P1#person.phone, undefined = _P1#person.address, _P2 = #person{name = \"Jakob\", _ = '_'}, \"Jakob\" = _P2#person.name, '_' = _P2#person.phone, '_' = _P2#person.address, P = #person{name = \"Joe\", phone = [0,8,2,3,4,3,1,2]}, \"Joe\" = P#person.name, [0,8,2,3,4,3,1,2] = P#person.phone, undefined = P#person.address, P1 = #person{name=\"Joe\", phone=[1,2,3], address=\"A street\"}, P2 = P1#person{name=\"Robert\"}, \"Robert\" = P2#person.name, [1,2,3] = P2#person.phone, \"A street\" = P2#person.address, Foo = fun(P) when is_record(P, person) -> a_person; (_) -> not_a_person end, a_person = Foo(P1), Find = fun([#person{name=Name, phone=Phone} | _], Name, Fn) -> {found, Phone}; ([_| T], Name, Fn) -> Fn(T, Name, Fn); ([], _Name, _Fn) -> not_found end, {found, [1,2,3]} = Find([#person{name = a}, #person{name = b, phone = [3,2,1]}, #person{name = c, phone = [1,2,3]}], c, Find), P3 = #person{name=\"Joe\", phone=[0,0,7], address=\"A street\"}, #person{name = Name} = P3, \"Joe\" = Name, Demo = fun() -> P17 = #person2{name= #name{first=\"Robert\",last=\"Virding\"}, phone=123}, _First = (P17#person2.name)#name.first end, \"Robert\" = Demo(), ok. ">>, [ok] = scan(Test1_shell), Test2 = <<"-module(recs). -record(person, {name, age, phone = [], dict = []}). -export([t/0]). t() -> ok. make_hacker_without_phone(Name, Age) -> #person{name = Name, age = Age, dict = [{computer_knowledge, excellent}, {drinks, coke}]}. print(#person{name = Name, age = Age, phone = Phone, dict = Dict}) -> io:format(\"Name: ~s, Age: ~w, Phone: ~w ~n\" \"Dictionary: ~w.~n\", [Name, Age, Phone, Dict]). birthday(P) when record(P, person) -> P#person{age = P#person.age + 1}. register_two_hackers() -> Hacker1 = make_hacker_without_phone(\"Joe\", 29), OldHacker = birthday(Hacker1), %% The central_register_server should have %% an interface function for this. central_register_server ! {register_person, Hacker1}, central_register_server ! {register_person, OldHacker#person{name = \"Robert\", phone = [0,8,3,2,4,5,3,1]}}. ">>, ok = run_file(Config, recs, Test2), ok. %% List comprehension examples from Programming Examples. OTP-5237. progex_lc(Config) when is_list(Config) -> Test1 = <<"-module(lc). -export([t/0]). t() -> [a,4,b,5,6] = [X || X <- [1,2,a,3,4,b,5,6], X > 3], [4,5,6] = [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3], [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = [{X, Y} || X <- [1,2,3], Y <- [a,b]], [1,2,3,4,5,6,7,8] = sort([4,5,1,8,3,6,7,2]), [[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]] = perms([b,u,g]), [] = pyth(11), [{3,4,5},{4,3,5}] = pyth(12), [{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, {16,12,20}] = pyth(50), [] = pyth1(11), [{3,4,5},{4,3,5}] = pyth1(12), [{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, {16,12,20}] = pyth1(50), [1,2,3,4,5] = append([[1,2,3],[4,5]]), [2,3,4] = map(fun(X) -> X + 1 end, [1,2,3]), [2,4] = filter(fun(X) -> X > 1 end, [0,2,4]), [1,2,3,7] = select(b,[{a,1},{b,2},{c,3},{b,7}]), [2,7] = select2(b,[{a,1},{b,2},{c,3},{b,7}]), ok. sort([Pivot|T]) -> sort([ X || X <- T, X < Pivot]) ++ [Pivot] ++ sort([ X || X <- T, X >= Pivot]); sort([]) -> []. perms([]) -> [[]]; perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. pyth(N) -> [ {A,B,C} || A <- lists:seq(1,N), B <- lists:seq(1,N), C <- lists:seq(1,N), A+B+C =< N, A*A+B*B == C*C ]. pyth1(N) -> [{A,B,C} || A <- lists:seq(1,N), B <- lists:seq(1,N-A+1), C <- lists:seq(1,N-A-B+2), A+B+C =< N, A*A+B*B == C*C ]. append(L) -> [X || L1 <- L, X <- L1]. map(Fun, L) -> [Fun(X) || X <- L]. filter(Pred, L) -> [X || X <- L, Pred(X)]. select(X, L) -> [Y || {X, Y} <- L]. select2(X, L) -> [Y || {X1, Y} <- L, X == X1]. ">>, ok = run_file(Config, lc, Test1), Test1_shell = <<"[a,4,b,5,6] = [X || X <- [1,2,a,3,4,b,5,6], X > 3], [4,5,6] = [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3], [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = [{X, Y} || X <- [1,2,3], Y <- [a,b]], Sort = fun([Pivot|T], Fn) -> Fn([ X || X <- T, X < Pivot], Fn) ++ [Pivot] ++ Fn([ X || X <- T, X >= Pivot], Fn); ([], _Fn) -> [] end, [1,2,3,4,5,6,7,8] = Sort([4,5,1,8,3,6,7,2], Sort), Perms = fun([], _Fn) -> [[]]; (L, Fn) -> [[H|T] || H <- L, T <- Fn(L--[H], Fn)] end, [[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]] = Perms([b,u,g], Perms), Pyth = fun(N) -> [ {A,B,C} || A <- lists:seq(1,N), B <- lists:seq(1,N), C <- lists:seq(1,N), A+B+C =< N, A*A+B*B == C*C ] end, [] = Pyth(11), [{3,4,5},{4,3,5}] = Pyth(12), %%[{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, %% {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, %% {16,12,20}] = Pyth(50), Pyth1 = fun(N) -> [{A,B,C} || A <- lists:seq(1,N), B <- lists:seq(1,N-A+1), C <- lists:seq(1,N-A-B+2), A+B+C =< N, A*A+B*B == C*C ] end, [] = Pyth1(11), [{3,4,5},{4,3,5}] = Pyth1(12), [{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, {16,12,20}] = Pyth1(50), Append = fun(L) -> [X || L1 <- L, X <- L1] end, [1,2,3,4,5] = Append([[1,2,3],[4,5]]), Map = fun(Fun, L) -> [Fun(X) || X <- L] end, [2,3,4] = Map(fun(X) -> X + 1 end, [1,2,3]), Filter = fun(Pred, L) -> [X || X <- L, Pred(X)] end, [2,4] = Filter(fun(X) -> X > 1 end, [0,2,4]), Select = fun(X, L) -> [Y || {X, Y} <- L] end, [1,2,3,7] = Select(b,[{a,1},{b,2},{c,3},{b,7}]), Select2 = fun(X, L) -> [Y || {X1, Y} <- L, X == X1] end, [2,7] = Select2(b,[{a,1},{b,2},{c,3},{b,7}]), ok. ">>, [ok] = scan(Test1_shell), ok. %% Funs examples from Programming Examples. OTP-5237. progex_funs(Config) when is_list(Config) -> Test1 = <<"-module(funs). -export([t/0]). double([H|T]) -> [2*H|double(T)]; double([]) -> []. add_one([H|T]) -> [H+1|add_one(T)]; add_one([]) -> []. map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) -> []. double2(L) -> map(fun(X) -> 2*X end, L). add_one2(L) -> map(fun(X) -> 1 + X end, L). print_list(Stream, [H|T]) -> io:format(Stream, \"~p~n\", [H]), print_list(Stream, T); print_list(Stream, []) -> true. broadcast(Msg, [Pid|Pids]) -> Pid ! Msg, broadcast(Msg, Pids); broadcast(_, []) -> true. foreach(F, [H|T]) -> F(H), foreach(F, T); foreach(F, []) -> ok. print_list2(S, L) -> foreach(fun(H) -> io:format(S, \"~p~n\",[H]) end, L). broadcast2(M, L) -> foreach(fun(Pid) -> Pid ! M end, L). t1() -> map(fun(X) -> 2 * X end, [1,2,3,4,5]). t2() -> map(fun double/1, [1,2,3,4,5]). t3() -> map({?MODULE, double3}, [1,2,3,4,5]). double3(X) -> X * 2. f(F, Args) when function(F) -> apply(F, Args); f(N, _) when integer(N) -> N. print_list3(File, List) -> {ok, Stream} = file:open(File, write), foreach(fun(X) -> io:format(Stream,\"~p~n\",[X]) end, List), file:close(Stream). print_list4(File, List) -> {ok, Stream} = file:open(File, write), foreach(fun(File) -> io:format(Stream,\"~p~n\",[File]) end, List), file:close(Stream). any(Pred, [H|T]) -> case Pred(H) of true -> true; false -> any(Pred, T) end; any(Pred, []) -> false. all(Pred, [H|T]) -> case Pred(H) of true -> all(Pred, T); false -> false end; all(Pred, []) -> true. foldl(F, Accu, [Hd|Tail]) -> foldl(F, F(Hd, Accu), Tail); foldl(F, Accu, []) -> Accu. mapfoldl(F, Accu0, [Hd|Tail]) -> {R,Accu1} = F(Hd, Accu0), {Rs,Accu2} = mapfoldl(F, Accu1, Tail), {[R|Rs], Accu2}; mapfoldl(F, Accu, []) -> {[], Accu}. filter(F, [H|T]) -> case F(H) of true -> [H|filter(F, T)]; false -> filter(F, T) end; filter(F, []) -> []. diff(L1, L2) -> filter(fun(X) -> not lists:member(X, L2) end, L1). intersection(L1,L2) -> filter(fun(X) -> lists:member(X,L1) end, L2). takewhile(Pred, [H|T]) -> case Pred(H) of true -> [H|takewhile(Pred, T)]; false -> [] end; takewhile(Pred, []) -> []. dropwhile(Pred, [H|T]) -> case Pred(H) of true -> dropwhile(Pred, T); false -> [H|T] end; dropwhile(Pred, []) -> []. splitlist(Pred, L) -> splitlist(Pred, L, []). splitlist(Pred, [H|T], L) -> case Pred(H) of true -> splitlist(Pred, T, [H|L]); false -> {lists:reverse(L), [H|T]} end; splitlist(Pred, [], L) -> {lists:reverse(L), []}. first(Pred, [H|T]) -> case Pred(H) of true -> {true, H}; false -> first(Pred, T) end; first(Pred, []) -> false. ints_from(N) -> fun() -> [N|ints_from(N+1)] end. pconst(X) -> fun (T) -> case T of [X|T1] -> {ok, {const, X}, T1}; _ -> fail end end. pand(P1, P2) -> fun (T) -> case P1(T) of {ok, R1, T1} -> case P2(T1) of {ok, R2, T2} -> {ok, {'and', R1, R2}}; fail -> fail end; fail -> fail end end. por(P1, P2) -> fun (T) -> case P1(T) of {ok, R, T1} -> {ok, {'or',1,R}, T1}; fail -> case P2(T) of {ok, R1, T1} -> {ok, {'or',2,R1}, T1}; fail -> fail end end end. grammar() -> pand( por(pconst(a), pconst(b)), por(pconst(c), pconst(d))). parse(List) -> (grammar())(List). t() -> [2,4,6,8] = double([1,2,3,4]), [2,3,4,5] = add_one([1,2,3,4]), [2,4,6,8] = double2([1,2,3,4]), [2,3,4,5] = add_one2([1,2,3,4]), XX = ints_from(1), [1 | _] = XX(), 1 = hd(XX()), Y = tl(XX()), 2 = hd(Y()), P1 = pconst(a), {ok,{const,a},[b,c]} = P1([a,b,c]), fail = P1([x,y,z]), {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} = parse([a,c]), {ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} = parse([a,d]), {ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} = parse([b,c]), {ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} = parse([b,d]), fail = parse([a,b]), ok. ">>, ok = run_file(Config, funs, Test1), Test2_shell = <<"Double = fun(X) -> 2 * X end, [2,4,6,8,10] = lists:map(Double, [1,2,3,4,5]), Big = fun(X) -> if X > 10 -> true; true -> false end end, false = lists:any(Big, [1,2,3,4]), true = lists:any(Big, [1,2,3,12,5]), false = lists:all(Big, [1,2,3,4,12,6]), true = lists:all(Big, [12,13,14,15]), L = [\"I\",\"like\",\"Erlang\"], 11 = lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L), Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a; (X) -> X end, Upcase_word = fun(X) -> lists:map(Upcase, X) end, \"ERLANG\" = Upcase_word(\"Erlang\"), [\"I\",\"LIKE\",\"ERLANG\"] = lists:map(Upcase_word, L), {[\"I\",\"LIKE\",\"ERLANG\"],11} = lists:mapfoldl(fun(Word, Sum) -> {Upcase_word(Word), Sum + length(Word)} end, 0, L), [500,12,45] = lists:filter(Big, [500,12,2,45,6,7]), [200,500,45] = lists:takewhile(Big, [200,500,45,5,3,45,6]), [5,3,45,6] = lists:dropwhile(Big, [200,500,45,5,3,45,6]), {[200,500,45],[5,3,45,6]} = lists:splitwith(Big, [200,500,45,5,3,45,6]), %% {true,45} = lists:first(Big, [1,2,45,6,123]), %% false = lists:first(Big, [1,2,4,5]), Adder = fun(X) -> fun(Y) -> X + Y end end, Add6 = Adder(6), 16 = Add6(10), ok. ">>, [ok] = scan(Test2_shell), ok. %% OTP-5990. {erlang,is_record}. otp_5990(Config) when is_list(Config) -> [true] = scan(<<"rd('OrdSet', {orddata = {},ordtype = type}), " "S = #'OrdSet'{ordtype = {}}, " "if tuple(S#'OrdSet'.ordtype) -> true; true -> false end.">>), ok. %% OTP-6166. Order of record definitions. otp_6166(Config) when is_list(Config) -> Test1 = filename:join(proplists:get_value(priv_dir, Config), "test1.hrl"), Contents1 = <<"-module(test1). -record(r5, {f}). -record(r3, {f = #r5{}}). " "-record(r1, {f = #r3{}}). -record(r4, {f = #r1{}}). " "-record(r2, {f = #r4{}}).">>, ok = file:write_file(Test1, Contents1), Test2 = filename:join(proplists:get_value(priv_dir, Config), "test2.hrl"), Contents2 = <<"-module(test2). -record(r5, {f}). -record(r3, {f = #r5{}}). " "-record(r1, {f = #r3{}}). -record(r4, {f = #r1{}}). " "-record(r2, {f = #r4{}}). -record(r6, {f = #r5{}}). % r6 > r0 -record(r0, {f = #r5{}, g = #r5{}}). % r0 < r5">>, ok = file:write_file(Test2, Contents2), RR12 = "[r1,r2,r3,r4,r5] = rr(\"" ++ Test1 ++ "\"), [r0,r1,r2,r3,r4,r5,r6] = rr(\"" ++ Test2 ++ "\"), R0 = #r0{}, R6 = #r6{}, true = is_record(R0, r0), true = is_record(R6, r6), ok. ", [ok] = scan(RR12), file:delete(Test1), file:delete(Test2), ok. %% OTP-6554. Formatted exits and error messages. otp_6554(Config) when is_list(Config) -> %% Should check the stacktrace as well... "exception error: bad argument" = comm_err(<<"math:sqrt(a).">>), "exception error: bad argument" = comm_err(<<"fun(X, Y) -> X ++ Y end(a, b).">>), "exception error: bad argument" = comm_err(<<"math:sqrt(lists:seq(1,40)).">>), "exception error: bad argument" = comm_err(<<"math:sqrt(lists:seq(1,10)).">>), "exception error: bad argument" = comm_err(<<"a ++ b.">>), "exception error: bad argument" = comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined}, aa ++ I.">>), "exception error: bad argument" = comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined}, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ++ I.">>), "exception error: bad argument" = comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined}, I ++ I.">>), "exception error: bad argument" = comm_err(<<"fun(X) -> not X end(a).">>), "exception error: bad argument: a" = comm_err(<<"fun(A, B) -> A orelse B end(a, b).">>), "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"math:sqrt(2)/round(math:sqrt(0)).">>), "exception error: interpreted function with arity 1 called with no arguments" = comm_err(<<"fun(V) -> V end().">>), "exception error: interpreted function with arity 1 called with two arguments" = comm_err(<<"fun(V) -> V end(1,2).">>), "exception error: interpreted function with arity 0 called with one argument" = comm_err(<<"fun() -> v end(1).">>), "exception error: interpreted function with arity 0 called with 4 arguments" = comm_err(<<"fun() -> v end(1,2,3,4).">>), "exception error: math:sqrt/1 called with two arguments" = comm_err(<<"fun math:sqrt/1(1,2).">>), "exception error: bad function 1." ++ _ = comm_err(<<"(math:sqrt(2))().">>), "exception error: bad function [1," ++ _ = comm_err(<<"(lists:seq(1, 100))().">>), "exception error: no match of right hand side value 1" ++ _ = comm_err(<<"a = math:sqrt(2).">>), "exception error: no match of right hand side value" ++ _ = comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined,undefined, undefined,undefined,undefined,undefined}, a = I.">>), "exception error: no case clause matching 1" ++ _ = comm_err(<<"case math:sqrt(2) of a -> ok end.">>), "exception error: no case clause matching [1," ++ _ = comm_err(<<"V = lists:seq(1, 20), case V of a -> ok end.">>), "exception error: no function clause matching" = comm_err(<<"fun(P) when is_pid(P) -> true end(a).">>), case test_server:is_native(erl_eval) of true -> %% Native code has different exit reason. Don't bother %% testing them. ok; false -> "exception error: {function_clause," = comm_err(<<"erlang:error(function_clause, " "[unproper | list]).">>), %% Cheating: "exception error: no function clause matching " "shell:apply_fun(4)" ++ _ = comm_err(<<"erlang:error(function_clause, [4]).">>), "exception error: no function clause matching " "lists:reverse(" ++ _ = comm_err(<<"F=fun() -> hello end, lists:reverse(F).">>), "exception error: no function clause matching " "lists:reverse(34) (lists.erl, line " ++ _ = comm_err(<<"lists:reverse(34).">>) end, "exception error: function_clause" = comm_err(<<"erlang:error(function_clause, 4).">>), "exception error: no function clause matching" ++ _ = comm_err(<<"fun(a, b, c, d) -> foo end" " (lists:seq(1,17)," " lists:seq(1, 18)," " lists:seq(1, 40)," " lists:seq(1, 5)).">>), "exception error: no function clause matching" = comm_err(<<"fun(P, q) when is_pid(P) -> true end(a, b).">>), "exception error: no true branch found when evaluating an if expression" = comm_err(<<"if length([a,b]) > 17 -> a end.">>), "exception error: no such process or port" = comm_err(<<"Pid = spawn(fun() -> a end)," "timer:sleep(1)," "link(Pid).">>), "exception error: a system limit has been reached" = comm_err(<<"list_to_atom(lists:duplicate(300,$a)).">>), "exception error: bad receive timeout value" = comm_err(<<"receive after a -> foo end.">>), "exception error: no try clause matching 1" ++ _ = comm_err(<<"try math:sqrt(2) of bar -> yes after 3 end.">>), "exception error: no try clause matching [1" ++ _ = comm_err(<<"V = lists:seq(1, 20)," "try V of bar -> yes after 3 end.">>), "exception error: undefined function math:sqrt/2" = comm_err(<<"math:sqrt(2, 2).">>), "exception error: limit of number of arguments to interpreted function " "exceeded" = comm_err(<<"fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) ->" " a end().">>), "exception error: bad filter a" = comm_err(<<"[b || begin a end].">>), "exception error: bad generator a" = comm_err(<<"[X || X <- a].">>), "exception throw: undef" = comm_err(<<"throw(undef).">>), "exception exit: undef" = comm_err(<<"exit(undef).">>), "exception exit: foo" = comm_err(<<"catch spawn_link(fun() ->" " timer:sleep(300), exit(foo) " " end)," "timer:sleep(500).">>), [ok] = scan( <<"begin process_flag(trap_exit, true)," " Pid = spawn_link(fun() ->" " timer:sleep(300), exit(foo) " " end)," " timer:sleep(500)," " receive {'EXIT', Pid, foo} -> ok end end.">>), "exception exit: badarith" = comm_err(<<"catch spawn_link(fun() ->" " timer:sleep(300), 1/0 " " end)," "timer:sleep(500).">>), "exception exit: {nocatch,foo}" = comm_err(<<"catch spawn_link(fun() ->" " timer:sleep(300), throw(foo) " " end)," "timer:sleep(500).">>), [ok] = scan( <<"begin process_flag(trap_exit, true)," " Pid = spawn_link(fun() ->" " timer:sleep(300), throw(foo) " " end)," " timer:sleep(500)," " receive {'EXIT', Pid, {{nocatch,foo},_}} -> ok end " "end.">>), "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin catch_exception(true), 1/0 end.">>), "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin catch_exception(false), 1/0 end.">>), "exception error: no function clause matching call to catch_exception/1" = comm_err(<<"catch_exception(1).">>), %% A bug was corrected (expansion of 'try'): "2: command not found" = comm_err(<<"try 1 of 1 -> v(2) after 3 end.">>), %% Cover a few lines: "3: command not found" = comm_err(<<"receive foo -> foo after 0 -> v(3) end.">>), "3: command not found" = comm_err(<<"receive foo -> foo after 0 -> e(3) end.">>), "1 / 0: command not found" = comm_err(<<"v(1/0).">>), "1\n1.\n" = t(<<"1. e(1).">>), [ok] = scan(<<"h().">>), "exception exit: normal" = comm_err(<<"exit(normal).">>), [foo] = scan(<<"begin history(0), foo end.">>), application:unset_env(stdlib, shell_history_length), [true] = scan(<<"begin <<10:(1024*1024*10)>>," "<<10:(1024*1024*10)>>, garbage_collect() end.">>), "1: syntax error before: '.'" = comm_err("1-."), %% comm_err(<<"exit().">>), % would hang "exception error: no function clause matching call to history/1" = comm_err(<<"history(foo).">>), "exception error: no function clause matching call to results/1" = comm_err(<<"results(foo).">>), Test = filename:join(proplists:get_value(priv_dir, Config), "otp_6554.erl"), Contents = <<"-module(otp_6554). -export([local_allowed/3, non_local_allowed/3]). local_allowed(_,_,State) -> {true,State}. non_local_allowed(_,_,State) -> {true,State}. ">>, ok = compile_file(Config, Test, Contents, []), "exception exit: restricted shell starts now" = comm_err(<<"begin shell:start_restricted(otp_6554) end.">>), "-record(r,{}).\n1.\nok.\n" = t(<<"f(), f(B), h(), b(), history(20), results(20)," "rd(r, {}), rl(r), rf('_'), rl(), rf()," "rp(1), _ = rr({foo}), _ = rr({foo}, [])," "rr({foo}, [], []), ok.">>), "false.\n" = t(<<"catch_exception(true).">>), "exception exit: restricted shell stopped"= comm_err(<<"begin shell:stop_restricted() end.">>), "true.\n" = t(<<"catch_exception(false).">>), "20\n1\n1\n1: results(2)\n2: 1\n-> 1\n3: v(2)\n-> 1.\nok.\n" = t(<<"results(2). 1. v(2). h().">>), application:unset_env(stdlib, shell_saved_results), "1\nfoo\n17\nB = foo\nC = 17\nF = fun() ->\n foo" "\n end.\nok.\n" = t(<<"begin F = fun() -> foo end, 1 end. B = F(). C = 17. b().">>), "3: command not found" = comm_err(<<"#{v(3) => v}.">>), "3: command not found" = comm_err(<<"#{k => v(3)}.">>), "3: command not found" = comm_err(<<"#{v(3) := v}.">>), "3: command not found" = comm_err(<<"#{k := v(3)}.">>), "3: command not found" = comm_err(<<"(v(3))#{}.">>), %% Tests I'd like to do: (you should try them manually) %% "catch spawn_link(fun() -> timer:sleep(1000), exit(foo) end)." %% "** exception error: foo" should be output after 1 second %% "catch spawn_link(fun() -> timer:sleep(1000), 1/0 end)." %% "** exception error: bad argument..." should be output after 1 second %% "1/0", "exit(foo)", "throw(foo)". %% "h()." should show {'EXIT', {badarith,..}}, {'EXIT',{foo,...}}, %% and {'EXIT',{{nocatch,foo},...}}. ok. %% OTP-7184. Propagate exit signals from dying evaluator process. otp_7184(Config) when is_list(Config) -> register(otp_7184, self()), catch t(<<"P = self(), spawn_link(fun() -> process_flag(trap_exit,true), P ! up, receive X -> otp_7184 ! {otp_7184, X} end end), receive up -> ok end, erlang:raise(throw, thrown, []).">>), receive {otp_7184,{'EXIT',_,{{nocatch,thrown},[]}}} -> ok end, catch t(<<"P = self(), spawn_link(fun() -> process_flag(trap_exit,true), P ! up, receive X -> otp_7184 ! {otp_7184, X} end end), receive up -> ok end, erlang:raise(exit, fini, []).">>), receive {otp_7184,{'EXIT',_,{fini,[]}}} -> ok end, catch t(<<"P = self(), spawn_link(fun() -> process_flag(trap_exit,true), P ! up, receive X -> otp_7184 ! {otp_7184,X} end end), receive up -> ok end, erlang:raise(error, bad, []).">>), receive {otp_7184,{'EXIT',_,{bad,[]}}} -> ok end, unregister(otp_7184), %% v/1, a few missed cases "17\n<<0,0,0,64>>.\nok.\n" = t(<<"17. " "<<64:32>>. " "<<64>> = << << X >> || << X >> <= v(2), X > v(1) >>, ok.">>), "17\n<<0,17>>.\n" =t(<<"17. <<(v(1)):16>>.">>), ok. %% OTP-7232. qlc:info() bug. otp_7232(Config) when is_list(Config) -> Info = <<"qlc:info(qlc:sort(qlc:q([X || X <- [55296,56296]]), " "{order, fun(A,B)-> A>B end})).">>, "qlc:sort([55296, 56296],\n" " [{order,\n" " fun(A, B) ->\n" " A > B\n" " end}])" = evaluate(Info, []), ok. %% OTP-8393. Prompt string. otp_8393(Config) when is_list(Config) -> _ = shell:prompt_func(default), "Bad prompt function: '> '" = prompt_err(<<"shell:prompt_func('> ').">>), _ = shell:prompt_func(default), "exception error: an error occurred when evaluating an arithmetic expression"++_ = prompt_err(<<"shell:prompt_func({shell_SUITE,prompt4}).">>), _ = shell:prompt_func(default), "default.\n" = t(<<"shell:prompt_func({shell_SUITE,prompt2}).">>), _ = shell:prompt_func(default), "default\nl.\n" = t(<<"shell:prompt_func({shell_SUITE,prompt3}). l.">>), %% %% Although this tests that you can set a unicode prompt function %% it does not really test that it does work with the io-servers. %% That is instead tested in the io_proto_SUITE, which has %% the right infrastructure in place for such tests. /PaN %% _ = shell:prompt_func(default), "default\nl.\n" = t(<<"shell:prompt_func({shell_SUITE,prompt5}). l.">>), %% Restricted shell. Contents = <<"-module(test_restricted_shell). -export([local_allowed/3, non_local_allowed/3]). local_allowed(_,_,State) -> {false,State}. non_local_allowed({shell,stop_restricted},[],State) -> {true,State}; non_local_allowed({shell,prompt_func},[_L],State) -> {true,State}; non_local_allowed({shell_SUITE,prompt1},[_L],State) -> {true,State}; non_local_allowed(_,_,State) -> {false,State}. ">>, Test = filename:join(proplists:get_value(priv_dir, Config), "test_restricted_shell.erl"), ok = compile_file(Config, Test, Contents, []), _ = shell:prompt_func(default), "exception exit: restricted shell starts now" = comm_err(<<"begin shell:start_restricted(" "test_restricted_shell) end.">>), "default.\n"++_ = t(<<"shell:prompt_func({shell_SUITE,prompt1}).">>), "exception exit: restricted shell does not allow apple(" ++ _ = comm_err(<<"apple(1).">>), "{shell_SUITE,prompt1}.\n" = t(<<"shell:prompt_func(default).">>), "exception exit: restricted shell stopped"= comm_err(<<"begin shell:stop_restricted() end.">>), undefined = application:get_env(stdlib, restricted_shell), NR = shell:results(20), "default\n20.\n" = t(<<"shell:prompt_func({shell_SUITE,prompt3}). results(0).">>), _ = shell:prompt_func(default), 0 = shell:results(NR), ok. prompt1(_L) -> "prompt> ". prompt2(_L) -> {'EXIT', []}. prompt3(L) -> N = proplists:get_value(history, L), integer_to_list(N). prompt4(_L) -> erlang:apply(fun erlang:'/'/2, [1,0]). prompt5(_L) -> [1050,1072,1082,1074,1086,32,1077,32,85,110,105,99,111,100,101,32,63]. -ifdef(not_used). exit_term(B) -> "** exception exit:" ++ Reply = t(B), S0 = string:left(Reply, string:chr(Reply, $\n)-1), S = string:strip(S0, right, $*), {ok,Ts,_} = erl_scan:string(S), {ok,Term} = erl_parse:parse_term(Ts), Term. -endif. error_string(B) -> "** exception error:" ++ Reply = t(B), caught_string(Reply). exit_string(B) -> "** exception exit:" ++ Reply = t(B), caught_string(Reply). caught_string(Reply) -> S0 = string:left(Reply, string:chr(Reply, $\n)-1), S1 = string:strip(S0, right, $.), S2 = string:strip(S1, left, $*), S = string:strip(S2, both, $ ), string:strip(S, both, $"). comm_err(B) -> Reply = t(B), S0 = string:left(Reply, string:chr(Reply, $\n)-1), S1 = string:strip(S0, left, $*), S2 = string:strip(S1, both, $ ), S = string:strip(S2, both, $"), string:strip(S, right, $.). prompt_err(B) -> Reply = t(B), S00 = string:sub_string(Reply, string:chr(Reply, $\n)+1), S0 = string:left(S00, string:chr(S00, $\n)-1), S1 = string:strip(S0, left, $*), S2 = string:strip(S1, both, $ ), S = string:strip(S2, both, $"), string:strip(S, right, $.). %% OTP-10302. Unicode. Also OTP-14285, Unicode atoms. otp_10302(Config) when is_list(Config) -> {ok,Node} = start_node(shell_suite_helper_2, "-pa "++proplists:get_value(priv_dir,Config)++ " +pc unicode"), Test1 = <<"begin io:setopts([{encoding,utf8}]), [1024] = \"\\x{400}\", rd(rec, {a = \"\\x{400}\"}), ok = rl(rec) end.">>, "-record(rec,{a = \"\x{400}\"}).\nok.\n" = t({Node,Test1}), Test3 = <<"io:setopts([{encoding,utf8}]). rd(rec, {a = \"\\x{400}\"}). ok = rp(#rec{}).">>, "ok.\nrec\n#rec{a = \"\x{400}\"}.\nok.\n" = t({Node,Test3}), Test4 = <<"io:setopts([{encoding,utf8}]). A = [1024] = \"\\x{400}\". b(). h().">>, "ok.\n\"\x{400}\"\nA = \"\x{400}\".\nok.\n" "1: io:setopts([{encoding, utf8}])\n-> ok.\n" "2: A = [1024] = \"\x{400}\"\n-> \"\x{400}\"\n" "3: b()\n-> ok.\nok.\n" = t({Node,Test4}), Test5 = <<"begin io:setopts([{encoding,utf8}]), results(0), A = [1024] = \"\\x{400}\", b(), h() end.">>, "A = \"\x{400}\".\nok.\n" = t({Node,Test5}), %% One $" is "lost": true = "\x{400}\": command not found" =:= prompt_err({Node, <<"io:setopts([{encoding,utf8}]). v(\"\x{400}\")."/utf8>>, unicode}), "ok.\ndefault\n* Bad prompt function: \"\x{400}\".\n" = t({Node,<<"io:setopts([{encoding,utf8}]). " "shell:prompt_func(\"\x{400}\")."/utf8>>, unicode}), rpc:call(Node,shell, prompt_func, [default]), _ = shell:prompt_func(default), %% Test erl_error:format_exception() (cf. OTP-6554) Test6 = <<"begin A = <<\"\\xaa\">>, S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), {ok, Ts, _} = erl_scan:string(S, 1), {ok, Es} = erl_parse:parse_exprs(Ts), B = erl_eval:new_bindings(), erl_eval:exprs(Es, B) end.">>, "** exception error: an error occurred when evaluating" " an arithmetic expression\n in operator '/'/2\n" " called as <<\"\xaa\">> / <<\"\xaa\">>.\n" = t(Test6), Test7 = <<"io:setopts([{encoding,utf8}]). A = <<\"\\xaa\">>, S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), {ok, Ts, _} = erl_scan:string(S, 1), {ok, Es} = erl_parse:parse_exprs(Ts), B = erl_eval:new_bindings(), erl_eval:exprs(Es, B).">>, "ok.\n** exception error: an error occurred when evaluating" " an arithmetic expression\n in operator '/'/2\n" " called as <<\"ª\">> / <<\"ª\">>.\n" = t({Node,Test7}), Test8 = <<"begin A = [1089], S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), {ok, Ts, _} = erl_scan:string(S, 1), {ok, Es} = erl_parse:parse_exprs(Ts), B = erl_eval:new_bindings(), erl_eval:exprs(Es, B) end.">>, "** exception error: an error occurred when evaluating" " an arithmetic expression\n in operator '/'/2\n" " called as [1089] / [1089].\n" = t(Test8), Test9 = <<"io:setopts([{encoding,utf8}]). A = [1089], S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), {ok, Ts, _} = erl_scan:string(S, 1), {ok, Es} = erl_parse:parse_exprs(Ts), B = erl_eval:new_bindings(), erl_eval:exprs(Es, B).">>, "ok.\n** exception error: an error occurred when evaluating" " an arithmetic expression\n in operator '/'/2\n" " called as \"\x{441}\" / \"\x{441}\".\n" = t({Node,Test9}), Test10 = <<"A = {\"1\\xaa\", $\\xaa, << <<\"hi\">>/binary >>, <<\"1\xaa\">>}, fun(a) -> true end(A).">>, "** exception error: no function clause matching \n" " erl_eval:'-inside-an-interpreted-fun-'" "({\"1\xc2\xaa\",170,<<\"hi\">>,\n " " <<\"1\xc2\xaa\">>}) .\n" = t(Test10), Test11 = <<"io:setopts([{encoding,utf8}]). A = {\"1\\xaa\", $\\xaa, << <<\"hi\">>/binary >>, <<\"1\xaa\">>}, fun(a) -> true end(A).">>, "ok.\n** exception error: no function clause matching \n" " erl_eval:'-inside-an-interpreted-fun-'" "({\"1\xaa\",170,<<\"hi\">>,\n " " <<\"1\xaa\"/utf8>>}) .\n" = t({Node,Test11}), Test12 = <<"fun(a, b) -> false end(65, [1089]).">>, "** exception error: no function clause matching \n" " erl_eval:'-inside-an-interpreted-fun-'(65,[1089])" " .\n" = t(Test12), Test13 = <<"io:setopts([{encoding,utf8}]). fun(a, b) -> false end(65, [1089]).">>, "ok.\n** exception error: no function clause matching \n" " erl_eval:'-inside-an-interpreted-fun-'(65,\"\x{441}\")" " .\n" = t({Node,Test13}), %% Unicode atoms. Test14 = <<"'\\x{447}\\x{435}'().">>, "** exception error: undefined shell command '\\x{447}\\x{435}'/0.\n" = t(Test14), Test15 = <<"io:setopts([{encoding,utf8}]). '\\x{447}\\x{435}'().">>, "ok.\n** exception error: undefined shell command '\x{447}\x{435}'/0.\n" = t({Node,Test15}), Test16 = <<"shell_SUITE:'\\x{447}\\x{435}'().">>, "** exception error: undefined function " "shell_SUITE:'\\x{447}\\x{435}'/0.\n" = t(Test16), Test17 = <<"io:setopts([{encoding,utf8}]). shell_SUITE:'\\x{447}\\x{435}'().">>, "ok.\n** exception error: undefined function " "shell_SUITE:'\x{447}\x{435}'/0.\n" = t({Node,Test17}), test_server:stop_node(Node), ok. otp_13719(Config) when is_list(Config) -> Test = <<"-module(otp_13719). -record(bar, {}). -record(foo, {bar :: #bar{}}).">>, File = filename("otp_13719.erl", Config), Beam = filename("otp_13719.beam", Config), ok = compile_file(Config, File, Test, []), RR = "rr(\"" ++ Beam ++ "\"). #foo{}.", "[bar,foo]\n#foo{bar = undefined}.\n" = t(RR), file:delete(filename("test.beam", Config)), file:delete(File), ok. otp_14285(Config) -> {ok,Node} = start_node(shell_suite_helper_4, "-pa "++proplists:get_value(priv_dir,Config)++ " +pc unicode"), Test1 = <<"begin io:setopts([{encoding,utf8}]), [1024] = atom_to_list('\\x{400}'), rd('\\x{400}', {'\\x{400}' = '\\x{400}'}), ok = rl('\\x{400}') end.">>, "-record('\x{400}',{'\x{400}' = '\x{400}'}).\nok.\n" = t({Node,Test1}), test_server:stop_node(Node), ok. otp_14296(Config) when is_list(Config) -> fun() -> F = fun() -> a end, LocalFun = term_to_string(F), S = LocalFun ++ ".", "1: syntax error before: Fun" = comm_err(S) end(), fun() -> F = fun mod:func/1, ExternalFun = term_to_string(F), S = ExternalFun ++ ".", R = ExternalFun ++ ".\n", R = t(S) end(), fun() -> UnknownPid = "<100000.0.0>", S = UnknownPid ++ ".", "1: syntax error before: '<'" = comm_err(S) end(), fun() -> KnownPid = term_to_string(self()), S = KnownPid ++ ".", R = KnownPid ++ ".\n", R = t(S) end(), fun() -> Port = open_port({spawn, "ls"}, [{line,1}]), KnownPort = erlang:port_to_list(Port), S = KnownPort ++ ".", R = KnownPort ++ ".\n", R = t(S) end(), fun() -> UnknownPort = "#Port<100000.0>", S = UnknownPort ++ ".", "1: syntax error before: Port" = comm_err(S) end(), fun() -> UnknownRef = "#Ref<100000.0.0.0>", S = UnknownRef ++ ".", "1: syntax error before: Ref" = comm_err(S) end(), fun() -> KnownRef = term_to_string(make_ref()), S = KnownRef ++ ".", R = KnownRef ++ ".\n", R = t(S) end(), %% Test erl_eval:extended_parse_term/1 TF = fun(S) -> {ok, Ts, _} = erl_scan:string(S++".", 1, [text]), case erl_eval:extended_parse_term(Ts) of {ok, Term} -> Term; {error, _}=Error -> Error end end, Fun = fun m:f/1, Fun = TF(term_to_string(Fun)), Fun = TF("fun m:f/1"), Pid = self(), Pid = TF(term_to_string(Pid)), Ref = make_ref(), Ref = TF(term_to_string(Ref)), Term = {[10, a], {"foo", []}, #{x => <<"bar">>}}, Term = TF(lists:flatten(io_lib:format("~p", [Term]))), {$a, F1, "foo"} = TF("{$a, 1.0, \"foo\"}"), true = is_float(F1), 3 = TF("+3"), $a = TF("+$a"), true = is_float(TF("+1.0")), true = -3 =:= TF("-3"), true = -$a =:= TF("-$a"), true = is_float(TF("-1.0")), {error, {_, _, ["syntax error"++_|_]}} = TF("{1"), {error, {_,_,"bad term"}} = TF("fun() -> foo end"), {error, {_,_,"bad term"}} = TF("1, 2"), ok. term_to_string(T) -> lists:flatten(io_lib:format("~w", [T])). scan(B) -> F = fun(Ts) -> case erl_parse:parse_term(Ts) of {ok,Term} -> Term; _Error -> {ok,Form} = erl_parse:parse_form(Ts), Form end end, scan(t(B), F). scan(S0, F) -> case erl_scan:tokens([], S0, 1) of {done,{ok,Ts,_},S} -> [F(Ts) | scan(S, F)]; _Else -> [] end. t({Node,Bin,Enc}) when is_atom(Node),is_binary(Bin), is_atom(Enc) -> t0({Bin,Enc}, fun() -> start_new_shell(Node) end); t({Node,Bin}) when is_atom(Node),is_binary(Bin) -> t0({Bin,latin1}, fun() -> start_new_shell(Node) end); t(Bin) when is_binary(Bin) -> t0({Bin,latin1}, fun() -> start_new_shell() end); t({Bin,Enc}) when is_binary(Bin), is_atom(Enc) -> t0({Bin,Enc}, fun() -> start_new_shell() end); t(L) -> t(list_to_binary(L)). t0({Bin,Enc}, F) -> %% Spawn a process so that io_request messages do not interfer. P = self(), C = spawn(fun() -> t1(P, {Bin, Enc}, F) end), receive {C, R} -> R end. t1(Parent, {Bin,Enc}, F) -> io:format("*** Testing ~s~n", [binary_to_list(Bin)]), S = #state{bin = Bin, unic = Enc, reply = [], leader = group_leader()}, group_leader(self(), self()), _Shell = F(), try server_loop(S) catch exit:R -> Parent ! {self(), R}; throw:{?MODULE,LoopReply,latin1} -> L0 = binary_to_list(list_to_binary(LoopReply)), [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), Parent ! {self(), dotify(L1)}; throw:{?MODULE,LoopReply,_Uni} -> Tmp = unicode:characters_to_binary(LoopReply), L0 = unicode:characters_to_list(Tmp), [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), Parent ! {self(), dotify(L1)} after group_leader(S#state.leader, self()) end. dotify([$., $\n | L]) -> [$., $\n | dotify(L)]; dotify([$,, $\n | L]) -> [$,, $\n | dotify(L)]; dotify("ok\n" ++ L) -> "ok.\n" ++ dotify(L); dotify("\nok\n" ++ L) -> ".\nok.\n" ++ dotify(L); dotify([$\n]) -> [$., $\n]; dotify([C | L]) -> [C | dotify(L)]; dotify([]) -> []. start_new_shell() -> Shell = shell:start(), link(Shell), Shell. start_new_shell(Node) -> Shell = rpc:call(Node,shell,start,[]), link(Shell), Shell. %% This is a very minimal implementation of the IO protocol... server_loop(S) -> receive {io_request, From, ReplyAs, Request} when is_pid(From) -> server_loop(do_io_request(Request, From, S, ReplyAs)); NotExpected -> exit(NotExpected) end. do_io_request(Req, From, S, ReplyAs) -> case io_requests([Req], [], S) of {_Status,{eof,_},S1} -> io_reply(From, ReplyAs, {error,terminated}), throw({?MODULE,S1#state.reply,S1#state.unic}); {_Status,Reply,S1} -> io_reply(From, ReplyAs, Reply), S1 end. io_reply(From, ReplyAs, Reply) -> From ! {io_reply, ReplyAs, Reply}. io_requests([{requests, Rs1} | Rs], Cont, S) -> io_requests(Rs1, [Rs | Cont], S); io_requests([R | Rs], Cont, S) -> case io_request(R, S) of {ok, ok, S1} -> io_requests(Rs, Cont, S1); Reply -> Reply end; io_requests([], [Rs|Cont], S) -> io_requests(Rs, Cont, S); io_requests([], [], S) -> {ok,ok,S}. io_request({setopts, Opts}, S) -> #state{unic = OldEnc, bin = Bin} = S, NewEnc = case proplists:get_value(encoding, Opts) of undefined -> OldEnc; utf8 -> unicode; New -> New end, NewBin = case {OldEnc, NewEnc} of {E, E} -> Bin; {latin1, _} -> unicode:characters_to_binary(Bin, latin1, unicode); {_, latin1} -> unicode:characters_to_binary(Bin, unicode, latin1); {_, _} -> Bin end, {ok, ok, S#state{unic = NewEnc, bin = NewBin}}; io_request(getopts, S) -> {ok,[{encoding,S#state.unic}],S}; io_request({get_geometry,columns}, S) -> {ok,80,S}; io_request({get_geometry,rows}, S) -> {ok,24,S}; io_request({put_chars,latin1,Chars}, S) -> {ok,ok,S#state{reply = [S#state.reply | Chars]}}; io_request({put_chars,unicode,Chars0}, S) -> Chars = unicode:characters_to_list(Chars0), {ok,ok,S#state{reply = [S#state.reply | Chars]}}; io_request({put_chars,Enc,Mod,Func,Args}, S) -> case catch apply(Mod, Func, Args) of Chars when is_list(Chars) -> io_request({put_chars,Enc,Chars}, S) end; io_request({get_until,Enc,_Prompt,Mod,Func,ExtraArgs}, S) -> get_until(Mod, Func, ExtraArgs, S, Enc). get_until(Mod, Func, ExtraArgs, S, Enc) -> get_until_loop(Mod, Func, ExtraArgs, S, {more,[]}, Enc). get_until_loop(M, F, As, S, {more,Cont}, Enc) -> Bin = S#state.bin, case byte_size(Bin) of 0 -> get_until_loop(M, F, As, S, catch apply(M, F, [Cont,eof|As]), Enc); _ when S#state.unic =:= latin1 -> get_until_loop(M, F, As, S#state{bin = <<>>}, catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc); _ -> get_until_loop(M, F, As, S#state{bin = <<>>}, catch apply(M, F, [Cont,unicode:characters_to_list(Bin)|As]), Enc) end; get_until_loop(_M, _F, _As, S, {done,Res,Buf}, Enc) -> {ok,Res,S#state{bin = buf2bin(Buf, Enc)}}; get_until_loop(_M, F, _As, S, _Other, _Enc) -> {error,{error,F},S}. buf2bin(eof,_) -> <<>>; buf2bin(Buf,latin1) -> list_to_binary(Buf); buf2bin(Buf,utf8) -> unicode:characters_to_binary(Buf,unicode,unicode); buf2bin(Buf,unicode) -> unicode:characters_to_binary(Buf,unicode,unicode). run_file(Config, Module, Test) -> FileName = filename(lists:concat([Module, ".erl"]), Config), BeamFile = filename(lists:concat([Module, ".beam"]), Config), LoadBeamFile = filename(Module, Config), ok = file:write_file(FileName, Test), ok = compile_file(Config, FileName, Test, []), code:purge(Module), {module, Module} = code:load_abs(LoadBeamFile), ok = Module:t(), file:delete(FileName), file:delete(BeamFile), ok. compile_file(Config, File, Test, Opts0) -> Opts = [export_all,nowarn_export_all,return,{outdir,proplists:get_value(priv_dir, Config)}|Opts0], ok = file:write_file(File, Test), case compile:file(File, Opts) of {ok, _M, _Ws} -> ok; _ -> error end. filename(Name, Config) when is_atom(Name) -> filename(atom_to_list(Name), Config); filename(Name, Config) -> filename:join(proplists:get_value(priv_dir, Config), Name). start_node(Name, Xargs) -> N = test_server:start_node(Name, slave, [{args, " " ++ Xargs}]), global:sync(), N.