%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2003-2017. 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(ms_transform_SUITE). -author('pan@erix.ericsson.se'). -include_lib("common_test/include/ct.hrl"). -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([basic_ets/1]). -export([basic_dbg/1]). -export([from_shell/1]). -export([records/1]). -export([record_index/1]). -export([multipass/1]). -export([top_match/1]). -export([old_guards/1]). -export([autoimported/1]). -export([semicolon/1]). -export([bitsyntax/1]). -export([record_defaults/1]). -export([andalso_orelse/1]). -export([float_1_function/1]). -export([action_function/1]). -export([warnings/1]). -export([no_warnings/1]). -export([eep37/1]). -export([otp_14454/1]). init_per_testcase(_Func, Config) -> Config. end_per_testcase(_Func, _Config) -> ok. suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,6}}]. all() -> [from_shell, basic_ets, basic_dbg, records, record_index, multipass, bitsyntax, record_defaults, andalso_orelse, float_1_function, action_function, warnings, no_warnings, top_match, old_guards, autoimported, semicolon, eep37, otp_14454]. groups() -> []. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. %% This may be subject to change -define(WARN_NUMBER_SHADOW,50). %% Check that shadowed variables in fun head generate warning. warnings(Config) when is_list(Config) -> setup(Config), Prog = <<"A=5, " "ets:fun2ms(fun({A,B}) " " when is_integer(A) and (A+5 > B) -> " " A andalso B " " end)">>, [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] = compile_ww(Prog), Prog2 = <<"C = 5, ets:fun2ms(fun ({A,B} = C) when is_integer(A) and (A+5 > B) -> {A andalso B,C} end)">>, [{_,[{3,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = compile_ww(Prog2), Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>, Prog3 = <<"A = 3, C = 5, ets:fun2ms(fun (C = #a{a = A, b = B}) when is_integer(A) and (A+5 > B) -> {A andalso B,C} end)">>, [{_,[{3,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}, {4,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] = compile_ww(Rec3,Prog3), Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>, Prog4 = <<"A=3,C=5, " "F = fun(B) -> B*3 end," "erlang:display(F(A))," "ets:fun2ms(fun(#a{a = A, b = B} = C) " " when is_integer(A) and (A+5 > B) -> " " {A andalso B,C} " " end)">>, [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = compile_ww(Rec4,Prog4), Rec5 = <<"-record(a,{a,b,c,d=foppa}).">>, Prog5 = <<"A=3,C=5, " "F = fun(B) -> B*3 end," "erlang:display(F(A))," "B = ets:fun2ms(fun(#a{a = A, b = B} = C) " " when is_integer(A) and (A+5 > B) -> " " {A andalso B,C} " " end)">>, [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = compile_ww(Rec5,Prog5), Prog6 = <<" X=bar, " " A = case X of" " foo ->" " foo;" " Y ->" " ets:fun2ms(fun(Y) ->" % This is a warning " 3*Y" " end)" " end," " ets:fun2ms(fun(Y) ->" % Y out of "scope" here, so no warning " {3*Y,A}" " end)">>, [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] = compile_ww(Prog6), Prog7 = <<" X=bar, " " A = case X of" " foo ->" " Y = foo;" " Y ->" " bar" " end," " ets:fun2ms(fun(Y) ->" % Y exported from case and safe, so warn " {3*Y,A}" " end)">>, [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] = compile_ww(Prog7), ok. %% Check that variables bound in other function clauses don't generate %% warning. no_warnings(Config) when is_list(Config) -> setup(Config), Prog = <<"tmp(X) when X > 100 ->\n", " Y=X,\n" " Y;\n" "tmp(X) ->\n" " ets:fun2ms(fun(Y) ->\n" " {X, 3*Y}\n" " end)">>, [] = compile_no_ww(Prog), Prog2 = <<"tmp(X) when X > 100 ->\n", " Y=X,\n" " Y;\n" "tmp(X) when X < 200 ->\n" " ok;\n" "tmp(X) ->\n" " ets:fun2ms(fun(Y) ->\n" " {X, 3*Y}\n" " end)">>, [] = compile_no_ww(Prog2), ok. %% Test that andalso and orelse are allowed in guards. andalso_orelse(Config) when is_list(Config) -> setup(Config), [{{'$1','$2'}, [{'and',{is_integer,'$1'},{'>',{'+','$1',5},'$2'}}], [{'andalso','$1','$2'}]}] = compile_and_run(<<"ets:fun2ms(fun({A,B}) " " when is_integer(A) and (A+5 > B) -> " " A andalso B " " end)">>), [{{'$1','$2'}, [{'or',{is_atom,'$1'},{'>',{'+','$1',5},'$2'}}], [{'orelse','$1','$2'}]}] = compile_and_run(<<"ets:fun2ms(fun({A,B}) " " when is_atom(A) or (A+5 > B) -> " " A orelse B " " end)">>), [{{'$1','$2'}, [{'andalso',{is_integer,'$1'},{'>',{'+','$1',5},'$2'}}], ['$1']}] = compile_and_run( <<"ets:fun2ms(fun({A,B}) when is_integer(A) andalso (A+5 > B) ->" " A " " end)">>), [{{'$1','$2'}, [{'orelse',{is_atom,'$1'},{'>',{'+','$1',5},'$2'}}], ['$1']}] = compile_and_run( <<"ets:fun2ms(fun({A,B}) when is_atom(A) orelse (A+5 > B) -> " " A " " end)">>), ok. %% Test that bitsyntax works and does not work where appropriate. bitsyntax(Config) when is_list(Config) -> setup(Config), [{'_',[], [<<0,27,0,27>>]}] = compile_and_run(<<"A = 27, " "ets:fun2ms(fun(_) -> <> end)">>), [{{<<15,47>>, '$1', '$2'}, [{'=:=','$1', <<0,27>>}, {'=:=','$2', <<27,28,19>>}], [<<188,0,13>>]}] = compile_and_run(<<"A = 27, " "ets:fun2ms(" " fun({<<15,47>>,B,C}) " " when B =:= <>, C =:= <<27,28,19>> -> " " <> " " end)">>), expect_failure( <<>>, <<"ets:fun2ms(fun({<<15,47>>,B,C}) " " when B =:= <<16>>, C =:= <<27,28,19>> -> " " <> " " end)">>), expect_failure( <<>>, <<"ets:fun2ms(fun({<>,B,C}) " " when B =:= <<16>>, C =:= <<27,28,19>> -> " " <> " " end)">>), ok. %% Test that record defaults works. record_defaults(Config) when is_list(Config) -> setup(Config), [{{<<27>>,{a,5,'$1',hej,hej}}, [], [{{a,hej,{'*','$1',2},flurp,flurp}}]}] = compile_and_run(<<"-record(a,{a,b,c,d=foppa}).">>, <<"ets:fun2ms(fun({<<27>>,#a{a=5, b=B,_=hej}}) -> " "#a{a=hej,b=B*2,_=flurp} " "end)">>), ok. %% Test basic ets:fun2ms. basic_ets(Config) when is_list(Config) -> setup(Config), [{{a,b},[],[true]}] = compile_and_run( <<"ets:fun2ms(fun({a,b}) -> true end)">>), [{{'$1',foo},[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]}, {{'$1','$1'},[{is_tuple,'$1'}],[{{{element,1,'$1'},'$*'}}]}] = compile_and_run(<<"ets:fun2ms(fun({X,foo}) when is_list(X) -> ", "{hd(X),object()};", "({X,X}) when is_tuple(X) ->", "{element(1,X),bindings()}", "end)">>), [{{'$1','$2'},[],[{{'$2','$1'}}]}] = compile_and_run(<<"ets:fun2ms(fun({A,B}) -> {B,A} end)">>), [{{'$1','$2'},[],[['$2','$1']]}] = compile_and_run(<<"ets:fun2ms(fun({A,B}) -> [B,A] end)">>), ok. %% Tests basic ets:fun2ms. basic_dbg(Config) when is_list(Config) -> setup(Config), [{[a,b],[],[{message,banan},{return_trace}]}] = compile_and_run(<<"dbg:fun2ms(fun([a,b]) -> message(banan), ", "return_trace() end)">>), [{['$1','$2'],[],[{{'$2','$1'}}]}] = compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> {B,A} end)">>), [{['$1','$2'],[],[['$2','$1']]}] = compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> [B,A] end)">>), [{['$1','$2'],[],['$*']}] = compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> bindings() end)">>), [{['$1','$2'],[],['$_']}] = compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> object() end)">>), [{[],[],[{return_trace}]}] = compile_and_run(<<"dbg:fun2ms(fun([]) -> return_trace() end)">>), ok. %% Test calling of ets/dbg:fun2ms from the shell. from_shell(Config) when is_list(Config) -> setup(Config), Fun = do_eval("fun({a,b}) -> true end"), [{{a,b},[],[true]}] = apply(ets,fun2ms,[Fun]), [{{a,b},[],[true]}] = do_eval("ets:fun2ms(fun({a,b}) -> true end)"), Fun2 = do_eval("fun([a,b]) -> message(banan), return_trace() end"), [{[a,b],[],[{message,banan},{return_trace}]}] = apply(dbg,fun2ms,[Fun2]), [{[a,b],[],[{message,banan},{return_trace}]}] = do_eval( "dbg:fun2ms(fun([a,b]) -> message(banan), return_trace() end)"), ok. %% Tests expansion of records in fun2ms. records(Config) when is_list(Config) -> setup(Config), RD = <<"-record(t, {" "t1 = [] :: list()," "t2 = foo :: atom()," "t3," "t4" "}).">>, [{{t,'$1','$2',foo,'_'},[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]}, {{t,'_','_','_','_'},[{'==',{element,2,'$_'},nisse}],[{{'$*'}}]}] = compile_and_run(RD,<< "ets:fun2ms(fun(#t{t1 = X, t2 = Y, t3 = foo}) when is_list(X) -> {hd(X),object()}; (#t{}) when (object())#t.t1 == nisse -> {bindings()} end)">>), [{{t,'$1','$2','_',foo}, [{'==',{element,4,'$_'},7},{is_list,'$1'}], [{{{hd,'$1'},'$_'}}]}, {'$1',[{is_record,'$1',t,5}], [{{{element,2,'$1'}, {{t,'$1',foo,undefined,undefined}}, {{t,{element,2,'$1'},{element,3,'$1'},{element,4,'$1'},boooo}}}}]}] = compile_and_run(RD,<< "ets:fun2ms(fun(#t{t1 = X, t2 = Y, t4 = foo}) when (object())#t.t3==7,is_list(X) -> {hd(X),object()}; (A) when is_record(A,t) -> {A#t.t1 ,#t{t1=A} ,A#t{t4=boooo} } end)" >>), [{[{t,'$1','$2',foo,'_'}],[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]}, {[{t,'_','_','_','_'}],[{'==',{element,2,{hd,'$_'}},nisse}],[{{'$*'}}]}]= compile_and_run(RD,<< "dbg:fun2ms(fun([#t{t1 = X, t2 = Y, t3 = foo}]) when is_list(X) -> {hd(X),object()}; ([#t{}]) when (hd(object()))#t.t1 == nisse -> {bindings()} end)" >>), ok. %% Test expansion of records in fun2ms, part 2. record_index(Config) when is_list(Config) -> setup(Config), RD = <<"-record(a,{a,b}).">>, [{{2},[],[true]}] = compile_and_run(RD, <<"ets:fun2ms(fun({#a.a}) -> true end)">>), [{{2},[],[2]}] = compile_and_run(RD, <<"ets:fun2ms(fun({#a.a}) -> #a.a end)">>), [{{2,'$1'},[{'>','$1',2}],[2]}] = compile_and_run(RD, <<"ets:fun2ms(fun({#a.a,A}) when A > #a.a -> #a.a end)">>), ok. %% Tests matching on top level in head to give alias for object(). top_match(Config) when is_list(Config) -> setup(Config), RD = <<"-record(a,{a,b}).">>, [{{a,3,'_'},[],['$_']}] = compile_and_run(RD, <<"ets:fun2ms(fun(A = #a{a=3}) -> A end)">>), [{{a,3,'_'},[],['$_']}] = compile_and_run(RD, <<"ets:fun2ms(fun(#a{a=3} = A) -> A end)">>), [{[a,b],[],['$_']}] = compile_and_run(RD, <<"dbg:fun2ms(fun(A = [a,b]) -> A end)">>), [{[a,b],[],['$_']}] = compile_and_run(RD, <<"dbg:fun2ms(fun([a,b] = A) -> A end)">>), expect_failure(RD, <<"ets:fun2ms(fun({a,A = {_,b}}) -> A end)">>), expect_failure(RD, <<"dbg:fun2ms(fun([a,A = {_,b}]) -> A end)">>), expect_failure(RD, <<"ets:fun2ms(fun(A#a{a = 2}) -> A end)">>), ok. %% Tests that multi-defined fields in records give errors. multipass(Config) when is_list(Config) -> setup(Config), RD = <<"-record(a,{a,b}).">>, expect_failure(RD,<<"ets:fun2ms(fun(A) -> #a{a=2,a=3} end)">>), expect_failure(RD,<<"ets:fun2ms(fun(A) -> A#a{a=2,a=3} end)">>), expect_failure(RD,<<"ets:fun2ms(fun(A) when A =:= #a{a=2,a=3} ->", " true end)">>), expect_failure(RD,<<"ets:fun2ms(fun({A,B})when A =:= B#a{a=2,a=3}->", "true end)">>), expect_failure(RD,<<"ets:fun2ms(fun(#a{a=3,a=3}) -> true end)">>), compile_and_run(RD,<<"ets:fun2ms(fun(A) -> #a{a=2,b=3} end)">>), compile_and_run(RD,<<"ets:fun2ms(fun(A) -> A#a{a=2,b=3} end)">>), compile_and_run(RD,<<"ets:fun2ms(fun(A) when A =:= #a{a=2,b=3} ->", " true end)">>), compile_and_run(RD,<<"ets:fun2ms(fun({A,B})when A=:= B#a{a=2,b=3}->", "true end)">>), compile_and_run(RD,<<"ets:fun2ms(fun(#a{a=3,b=3}) -> true end)">>), ok. %% Test that old type tests in guards are translated. old_guards(Config) when is_list(Config) -> setup(Config), Tests = [ {atom,is_atom}, {float,is_float}, {integer,is_integer}, {list,is_list}, {number,is_number}, {pid,is_pid}, {port,is_port}, {reference,is_reference}, {tuple,is_tuple}, {binary,is_binary}, {function,is_function}], lists:foreach( fun({Old,New}) -> Bin = list_to_binary([<<"ets:fun2ms(fun(X) when ">>, atom_to_list(Old), <<"(X) -> true end)">>]), case compile_and_run(Bin) of [{'$1',[{New,'$1'}],[true]}] -> ok; _ -> exit({bad_result_for, binary_to_list(Bin)}) end end, Tests), RD = <<"-record(a,{a,b}).">>, [{'$1',[{is_record,'$1',a,3}],[true]}] = compile_and_run(RD, <<"ets:fun2ms(fun(X) when record(X,a) -> true end)">>), expect_failure (RD, <<"ets:fun2ms(fun(X) when integer(X) and constant(X) -> " "true end)">>), [{'$1',[{is_integer,'$1'}, {is_float,'$1'}, {is_atom,'$1'}, {is_list,'$1'}, {is_number,'$1'}, {is_pid,'$1'}, {is_port,'$1'}, {is_reference,'$1'}, {is_tuple,'$1'}, {is_binary,'$1'}, {is_record,'$1',a,3}], [true]}] = compile_and_run(RD, << "ets:fun2ms(fun(X) when integer(X)," "float(X), atom(X)," "list(X), number(X), pid(X)," "port(X), reference(X), tuple(X)," "binary(X), record(X,a) -> true end)" >>), ok. %% Test use of autoimported BIFs used like erlang:'+'(A,B) in guards %% and body. autoimported(Config) when is_list(Config) -> setup(Config), Allowed = [ {abs,1}, {element,2}, {hd,1}, {length,1}, {node,0}, {node,1}, {round,1}, {size,1}, {tl,1}, {trunc,1}, {self,0}, %%{float,1}, see float_1_function/1 {is_atom,1}, {is_float,1}, {is_integer,1}, {is_list,1}, {is_number,1}, {is_pid,1}, {is_port,1}, {is_reference,1}, {is_tuple,1}, {is_binary,1}, {is_function,1}, {is_record,2,magic}, {'and',2,infix}, {'or',2,infix}, {'xor',2,infix}, {'not',1}, %%{'andalso',2,infix}, %%{'orelse',2,infix}, {'+',1}, {'+',2,infix}, {'-',1}, {'-',2,infix}, {'*',2,infix}, {'/',2,infix}, {'div',2,infix}, {'rem',2,infix}, {'band',2,infix}, {'bor',2,infix}, {'bxor',2,infix}, {'bnot',1}, {'bsl',2,infix}, {'bsr',2,infix}, {'>',2,infix}, {'>=',2,infix}, {'<',2,infix}, {'=<',2,infix}, {'==',2,infix}, {'=:=',2,infix}, {'/=',2,infix}, {'=/=',2,infix}], RD = <<"-record(a,{a,b}).">>, lists:foreach( fun({A,0}) -> L = atom_to_list(A), Bin1 = list_to_binary( [ <<"ets:fun2ms(fun(X) when ">>, L,<<"() -> ">>, L,<<"() end)">> ]), Bin2 = list_to_binary( [ <<"ets:fun2ms(fun(X) when erlang:'">>, L,<<"'() -> erlang:'">>, L,<<"'() end)">> ]), Res1 = compile_and_run(Bin1), Res2 = compile_and_run(Bin2), case Res1 =:= Res2 of true -> ok; false -> exit({not_equal,{Res1,Res2,A}}) end; ({A,1}) -> L = atom_to_list(A), Bin1 = list_to_binary( [ <<"ets:fun2ms(fun(X) when ">>, L,<<"(X) -> ">>, L,<<"(X) end)">> ]), Bin2 = list_to_binary( [ <<"ets:fun2ms(fun(X) when erlang:'">>, L,<<"'(X) -> erlang:'">>, L,<<"'(X) end)">> ]), Res1 = compile_and_run(Bin1), Res2 = compile_and_run(Bin2), case Res1 =:= Res2 of true -> ok; false -> exit({not_equal,{Res1,Res2,A}}) end; ({A,2}) -> L = atom_to_list(A), Bin1 = list_to_binary( [ <<"ets:fun2ms(fun({X,Y}) when ">>, L,<<"(X,Y) -> ">>, L,<<"(X,Y) end)">> ]), Bin2 = list_to_binary( [ <<"ets:fun2ms(fun({X,Y}) when erlang:'">>, L,<<"'(X,Y) -> erlang:'">>, L,<<"'(X,Y) end)">> ]), Res1 = compile_and_run(Bin1), Res2 = compile_and_run(Bin2), case Res1 =:= Res2 of true -> ok; false -> exit({not_equal,{Res1,Res2,A}}) end; ({A,2,infix}) -> L = atom_to_list(A), Bin1 = list_to_binary( [ <<"ets:fun2ms(fun({X,Y}) when X ">>, L,<<" Y -> X ">>, L,<<" Y end)">> ]), Bin2 = list_to_binary( [ <<"ets:fun2ms(fun({X,Y}) when erlang:'">>, L,<<"'(X,Y) -> erlang:'">>, L,<<"'(X,Y) end)">> ]), Res1 = compile_and_run(Bin1), Res2 = compile_and_run(Bin2), case Res1 =:= Res2 of true -> ok; false -> exit({not_equal,{Res1,Res2,A}}) end; ({A,2,magic}) -> %is_record L = atom_to_list(A), Bin1 = list_to_binary( [ <<"ets:fun2ms(fun(X) when ">>, L,<<"(X,a) -> ">>, L,<<"(X,a) end)">> ]), Bin2 = list_to_binary( [ <<"ets:fun2ms(fun(X) when erlang:'">>, L,<<"'(X,a) -> erlang:'">>, L,<<"'(X,a) end)">> ]), Res1 = compile_and_run(RD,Bin1), Res2 = compile_and_run(RD,Bin2), case Res1 =:= Res2 of true -> ok; false -> exit({not_equal,{Res1,Res2,A}}) end end, Allowed), ok. %% Test semicolon in guards of match_specs. semicolon(Config) when is_list(Config) -> setup(Config), Res01 = compile_and_run (<<"ets:fun2ms(fun(X) when is_integer(X); " "is_float(X) -> true end)">>), Res02 = compile_and_run (<<"ets:fun2ms(fun(X) when is_integer(X) -> true; " "(X) when is_float(X) -> true end)">>), Res01 = Res02, Res11 = compile_and_run (<<"ets:fun2ms(fun(X) when is_integer(X); " "is_float(X); atom(X) -> true end)">>), Res12 = compile_and_run (<<"ets:fun2ms(fun(X) when is_integer(X) -> true; " "(X) when is_float(X) -> true; " "(X) when is_atom(X) -> true end)">>), Res11 = Res12, ok. %% OTP-5297. The function float/1. float_1_function(Config) when is_list(Config) -> setup(Config), RunMS = fun(L, MS) -> ets:match_spec_run(L, ets:match_spec_compile(MS)) end, MS1 = compile_and_run (<<"ets:fun2ms(fun(X) -> float(X) end)">>), [F1] = RunMS([3], MS1), true = is_float(F1) and (F1 == 3), MS1b = compile_and_run (<<"dbg:fun2ms(fun(X) -> float(X) end)">>), [F2] = RunMS([3], MS1b), true = is_float(F2) and (F2 == 3), MS2 = compile_and_run (<<"ets:fun2ms(fun(X) when is_pid(X) or float(X) -> true end)">>), [] = RunMS([3.0], MS2), MS3 = compile_and_run (<<"dbg:fun2ms(fun(X) when is_pid(X); float(X) -> true end)">>), [true] = RunMS([3.0], MS3), MS4 = compile_and_run (<<"ets:fun2ms(fun(X) when erlang:float(X) > 1 -> big;" " (_) -> small end)">>), [small,big] = RunMS([1.0, 3.0], MS4), MS5 = compile_and_run (<<"ets:fun2ms(fun(X) when float(X) > 1 -> big;" " (_) -> small end)">>), [small,big] = RunMS([1.0, 3.0], MS5), %% This is the test from autoimported/1. [{'$1',[{is_float,'$1'}],[{float,'$1'}]}] = compile_and_run (<<"ets:fun2ms(fun(X) when float(X) -> float(X) end)">>), [{'$1',[{float,'$1'}],[{float,'$1'}]}] = compile_and_run (<<"ets:fun2ms(fun(X) when erlang:'float'(X) -> " "erlang:'float'(X) end)">>), ok. %% Test all 'action functions'. action_function(Config) when is_list(Config) -> setup(Config), [{['$1','$2'],[], [{set_seq_token,label,0}, {get_seq_token}, {message,'$1'}, {return_trace}, {exception_trace}]}] = compile_and_run (<<"dbg:fun2ms(fun([X,Y]) -> " "set_seq_token(label, 0), " "get_seq_token(), " "message(X), " "return_trace(), " "exception_trace() end)">>), [{['$1','$2'],[], [{process_dump}, {enable_trace,send}, {enable_trace,'$2',send}, {disable_trace,procs}, {disable_trace,'$2',procs}]}] = compile_and_run (<<"dbg:fun2ms(fun([X,Y]) -> " "process_dump(), " "enable_trace(send), " "enable_trace(Y, send), " "disable_trace(procs), " "disable_trace(Y, procs) end)">>), [{['$1','$2'], [], [{display,'$1'}, {caller}, {set_tcw,{const,16}}, {silent,true}, {trace,[send],[procs]}, {trace,'$2',[procs],[send]}]}] = compile_and_run (<<"A = 16, dbg:fun2ms(fun([X,Y]) -> " "display(X), " "caller(), " "set_tcw(A), " "silent(true), " "trace([send], [procs]), " "trace(Y, [procs], [send]) end)">>), ok. eep37(Config) when is_list(Config) -> setup(Config), [{'$1',[],['$1']}] = compile_and_run(<<"F = fun _Ms() ->\n" " ets:fun2ms(fun (X) -> X end)\n" " end,\n" "F()">>). otp_14454(Config) when is_list(Config) -> setup(Config), [{'$1',[],[{'band','$1',136}]}] = compile_and_run( <<"ets:fun2ms(fun(A) -> A band ( -(-17) bsl 3) end)">>), [{'$1',[],[{'band','$1',136}]}] = compile_and_run( <<"ets:fun2ms(fun(A) -> A band ( erlang:'bsl'(-(-17), 3)) end)">>), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% setup(Config) -> put(mts_config,Config), put(mts_tf_counter,0). temp_name() -> Conf = get(mts_config), C = get(mts_tf_counter), put(mts_tf_counter,C+1), filename:join([proplists:get_value(priv_dir,Conf), "tempfile"++integer_to_list(C)++".tmp"]). expect_failure(Recs,Code) -> case (catch compile_and_run(Recs,Code)) of {'EXIT',_Foo} -> ok; Other -> exit({expected,failure,got,Other}) end. compile_and_run(Expr) -> compile_and_run(<<>>,Expr). compile_and_run(Records,Expr) -> Prog = << "-module(tmp).\n", "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", "-export([tmp/0]).\n", Records/binary,"\n", "tmp() ->\n", Expr/binary,".\n">>, FN=temp_name(), file:write_file(FN,Prog), {ok,Forms} = epp:parse_file(FN,"",""), {ok,tmp,Bin} = compile:forms(Forms), code:load_binary(tmp,FN,Bin), tmp:tmp(). compile_ww(Expr) -> compile_ww(<<>>,Expr). compile_ww(Records,Expr) -> Prog = << "-module(tmp).\n", "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", "-export([tmp/0]).\n", Records/binary,"\n", "-file(?FILE, 0). ", "tmp() ->\n", Expr/binary,".\n">>, FN=temp_name(), file:write_file(FN,Prog), {ok,Forms} = epp:parse_file(FN,"",""), {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings, nowarn_unused_vars, nowarn_unused_record]), Wlist. compile_no_ww(Expr) -> Prog = << "-module(tmp).\n", "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", "-export([tmp/1]).\n\n", Expr/binary,".\n">>, FN=temp_name(), file:write_file(FN,Prog), {ok,Forms} = epp:parse_file(FN,"",""), {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings, nowarn_unused_vars, nowarn_unused_record]), Wlist. do_eval(String) -> {done,{ok,T,_},[]} = erl_scan:tokens( [], String++".\n",1), {ok,Tree} = erl_parse:parse_exprs(T), {value,Res,[]} = erl_eval:exprs(Tree,[]), Res.