1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2003-2011. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: -module(ms_transform_SUITE). 20: -author('pan@erix.ericsson.se'). 21: 22: -include_lib("test_server/include/test_server.hrl"). 23: 24: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 25: init_per_group/2,end_per_group/2]). 26: -export([basic_ets/1]). 27: -export([basic_dbg/1]). 28: -export([from_shell/1]). 29: -export([records/1]). 30: -export([record_index/1]). 31: -export([multipass/1]). 32: -export([top_match/1]). 33: -export([old_guards/1]). 34: -export([autoimported/1]). 35: -export([semicolon/1]). 36: -export([bitsyntax/1]). 37: -export([record_defaults/1]). 38: -export([andalso_orelse/1]). 39: -export([float_1_function/1]). 40: -export([action_function/1]). 41: -export([warnings/1]). 42: -export([no_warnings/1]). 43: -export([init_per_testcase/2, end_per_testcase/2]). 44: 45: init_per_testcase(_Func, Config) -> 46: Dog=test_server:timetrap(test_server:seconds(360)), 47: [{watchdog, Dog}|Config]. 48: 49: end_per_testcase(_Func, Config) -> 50: Dog=?config(watchdog, Config), 51: test_server:timetrap_cancel(Dog). 52: 53: suite() -> [{ct_hooks,[ts_install_cth]}]. 54: 55: all() -> 56: [from_shell, basic_ets, basic_dbg, records, 57: record_index, multipass, bitsyntax, record_defaults, 58: andalso_orelse, float_1_function, action_function, 59: warnings, no_warnings, top_match, old_guards, autoimported, 60: semicolon]. 61: 62: groups() -> 63: []. 64: 65: init_per_suite(Config) -> 66: Config. 67: 68: end_per_suite(_Config) -> 69: ok. 70: 71: init_per_group(_GroupName, Config) -> 72: Config. 73: 74: end_per_group(_GroupName, Config) -> 75: Config. 76: 77: 78: %% This may be subject to change 79: -define(WARN_NUMBER_SHADOW,50). 80: warnings(suite) -> 81: []; 82: warnings(doc) -> 83: ["Check that shadowed variables in fun head generate warning"]; 84: warnings(Config) when is_list(Config) -> 85: ?line setup(Config), 86: Prog = <<"A=5, " 87: "ets:fun2ms(fun({A,B}) " 88: " when is_integer(A) and (A+5 > B) -> " 89: " A andalso B " 90: " end)">>, 91: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] = 92: compile_ww(Prog), 93: Prog2 = <<"C=5, " 94: "ets:fun2ms(fun({A,B} = C) " 95: " when is_integer(A) and (A+5 > B) -> " 96: " {A andalso B,C} " 97: " end)">>, 98: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = 99: compile_ww(Prog2), 100: Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>, 101: Prog3 = <<"A=3,C=5, " 102: "ets:fun2ms(fun(#a{a = A, b = B} = C) " 103: " when is_integer(A) and (A+5 > B) -> " 104: " {A andalso B,C} " 105: " end)">>, 106: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, 107: {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = 108: compile_ww(Rec3,Prog3), 109: Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>, 110: Prog4 = <<"A=3,C=5, " 111: "F = fun(B) -> B*3 end," 112: "erlang:display(F(A))," 113: "ets:fun2ms(fun(#a{a = A, b = B} = C) " 114: " when is_integer(A) and (A+5 > B) -> " 115: " {A andalso B,C} " 116: " end)">>, 117: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, 118: {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = 119: compile_ww(Rec4,Prog4), 120: Rec5 = <<"-record(a,{a,b,c,d=foppa}).">>, 121: Prog5 = <<"A=3,C=5, " 122: "F = fun(B) -> B*3 end," 123: "erlang:display(F(A))," 124: "B = ets:fun2ms(fun(#a{a = A, b = B} = C) " 125: " when is_integer(A) and (A+5 > B) -> " 126: " {A andalso B,C} " 127: " end)">>, 128: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, 129: {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = 130: compile_ww(Rec5,Prog5), 131: Prog6 = <<" X=bar, " 132: " A = case X of" 133: " foo ->" 134: " foo;" 135: " Y ->" 136: " ets:fun2ms(fun(Y) ->" % This is a warning 137: " 3*Y" 138: " end)" 139: " end," 140: " ets:fun2ms(fun(Y) ->" % Y out of "scope" here, so no warning 141: " {3*Y,A}" 142: " end)">>, 143: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] = 144: compile_ww(Prog6), 145: Prog7 = <<" X=bar, " 146: " A = case X of" 147: " foo ->" 148: " Y = foo;" 149: " Y ->" 150: " bar" 151: " end," 152: " ets:fun2ms(fun(Y) ->" % Y exported from case and safe, so warn 153: " {3*Y,A}" 154: " end)">>, 155: ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] = 156: compile_ww(Prog7), 157: ok. 158: 159: no_warnings(suite) -> 160: []; 161: no_warnings(doc) -> 162: ["Check that variables bound in other function clauses don't generate " 163: "warning"]; 164: no_warnings(Config) when is_list(Config) -> 165: ?line setup(Config), 166: Prog = <<"tmp(X) when X > 100 ->\n", 167: " Y=X,\n" 168: " Y;\n" 169: "tmp(X) ->\n" 170: " ets:fun2ms(fun(Y) ->\n" 171: " {X, 3*Y}\n" 172: " end)">>, 173: ?line [] = compile_no_ww(Prog), 174: 175: Prog2 = <<"tmp(X) when X > 100 ->\n", 176: " Y=X,\n" 177: " Y;\n" 178: "tmp(X) when X < 200 ->\n" 179: " ok;\n" 180: "tmp(X) ->\n" 181: " ets:fun2ms(fun(Y) ->\n" 182: " {X, 3*Y}\n" 183: " end)">>, 184: ?line [] = compile_no_ww(Prog2), 185: ok. 186: 187: andalso_orelse(suite) -> 188: []; 189: andalso_orelse(doc) -> 190: ["Tests that andalso and orelse are allowed in guards."]; 191: andalso_orelse(Config) when is_list(Config) -> 192: ?line setup(Config), 193: ?line [{{'$1','$2'}, 194: [{'and',{is_integer,'$1'},{'>',{'+','$1',5},'$2'}}], 195: [{'andalso','$1','$2'}]}] = 196: compile_and_run(<<"ets:fun2ms(fun({A,B}) " 197: " when is_integer(A) and (A+5 > B) -> " 198: " A andalso B " 199: " end)">>), 200: ?line [{{'$1','$2'}, 201: [{'or',{is_atom,'$1'},{'>',{'+','$1',5},'$2'}}], 202: [{'orelse','$1','$2'}]}] = 203: compile_and_run(<<"ets:fun2ms(fun({A,B}) " 204: " when is_atom(A) or (A+5 > B) -> " 205: " A orelse B " 206: " end)">>), 207: ?line [{{'$1','$2'}, 208: [{'andalso',{is_integer,'$1'},{'>',{'+','$1',5},'$2'}}], 209: ['$1']}] = 210: compile_and_run( 211: <<"ets:fun2ms(fun({A,B}) when is_integer(A) andalso (A+5 > B) ->" 212: " A " 213: " end)">>), 214: ?line [{{'$1','$2'}, 215: [{'orelse',{is_atom,'$1'},{'>',{'+','$1',5},'$2'}}], 216: ['$1']}] = 217: compile_and_run( 218: <<"ets:fun2ms(fun({A,B}) when is_atom(A) orelse (A+5 > B) -> " 219: " A " 220: " end)">>), 221: ok. 222: 223: 224: bitsyntax(suite) -> 225: []; 226: bitsyntax(doc) -> 227: ["Tests that bitsyntax works and does not work where appropriate"]; 228: bitsyntax(Config) when is_list(Config) -> 229: ?line setup(Config), 230: ?line [{'_',[], 231: [<<0,27,0,27>>]}] = 232: compile_and_run(<<"A = 27, " 233: "ets:fun2ms(fun(_) -> <<A:16,27:16>> end)">>), 234: ?line [{{<<15,47>>, 235: '$1', 236: '$2'}, 237: [{'=:=','$1', 238: <<0,27>>}, 239: {'=:=','$2', 240: <<27,28,19>>}], 241: [<<188,0,13>>]}] = 242: compile_and_run(<<"A = 27, " 243: "ets:fun2ms(" 244: " fun({<<15,47>>,B,C}) " 245: " when B =:= <<A:16>>, C =:= <<27,28,19>> -> " 246: " <<A:4,12:4,13:16>> " 247: " end)">>), 248: ?line expect_failure( 249: <<>>, 250: <<"ets:fun2ms(fun({<<15,47>>,B,C}) " 251: " when B =:= <<16>>, C =:= <<27,28,19>> -> " 252: " <<B:4,12:4,13:16>> " 253: " end)">>), 254: ?line expect_failure( 255: <<>>, 256: <<"ets:fun2ms(fun({<<A:15,47>>,B,C}) " 257: " when B =:= <<16>>, C =:= <<27,28,19>> -> " 258: " <<B:4,12:4,13:16>> " 259: " end)">>), 260: ok. 261: 262: record_defaults(suite) -> 263: []; 264: record_defaults(doc) -> 265: ["Tests that record defaults works"]; 266: record_defaults(Config) when is_list(Config) -> 267: ?line setup(Config), 268: ?line [{{<<27>>,{a,5,'$1',hej,hej}}, 269: [], 270: [{{a,hej,{'*','$1',2},flurp,flurp}}]}] = 271: compile_and_run(<<"-record(a,{a,b,c,d=foppa}).">>, 272: <<"ets:fun2ms(fun({<<27>>,#a{a=5, b=B,_=hej}}) -> " 273: "#a{a=hej,b=B*2,_=flurp} " 274: "end)">>), 275: ok. 276: 277: basic_ets(suite) -> 278: []; 279: basic_ets(doc) -> 280: ["Tests basic ets:fun2ms"]; 281: basic_ets(Config) when is_list(Config) -> 282: ?line setup(Config), 283: ?line [{{a,b},[],[true]}] = compile_and_run( 284: <<"ets:fun2ms(fun({a,b}) -> true end)">>), 285: ?line [{{'$1',foo},[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]}, 286: {{'$1','$1'},[{is_tuple,'$1'}],[{{{element,1,'$1'},'$*'}}]}] = 287: compile_and_run(<<"ets:fun2ms(fun({X,foo}) when is_list(X) -> ", 288: "{hd(X),object()};", 289: "({X,X}) when is_tuple(X) ->", 290: "{element(1,X),bindings()}", 291: "end)">>), 292: ?line [{{'$1','$2'},[],[{{'$2','$1'}}]}] = 293: compile_and_run(<<"ets:fun2ms(fun({A,B}) -> {B,A} end)">>), 294: ?line [{{'$1','$2'},[],[['$2','$1']]}] = 295: compile_and_run(<<"ets:fun2ms(fun({A,B}) -> [B,A] end)">>), 296: ok. 297: 298: basic_dbg(suite) -> 299: []; 300: basic_dbg(doc) -> 301: ["Tests basic ets:fun2ms"]; 302: basic_dbg(Config) when is_list(Config) -> 303: ?line setup(Config), 304: ?line [{[a,b],[],[{message,banan},{return_trace}]}] = 305: compile_and_run(<<"dbg:fun2ms(fun([a,b]) -> message(banan), ", 306: "return_trace() end)">>), 307: ?line [{['$1','$2'],[],[{{'$2','$1'}}]}] = 308: compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> {B,A} end)">>), 309: ?line [{['$1','$2'],[],[['$2','$1']]}] = 310: compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> [B,A] end)">>), 311: ?line [{['$1','$2'],[],['$*']}] = 312: compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> bindings() end)">>), 313: ?line [{['$1','$2'],[],['$_']}] = 314: compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> object() end)">>), 315: ok. 316: 317: from_shell(suite) -> 318: []; 319: from_shell(doc) -> 320: ["Test calling of ets/dbg:fun2ms from the shell"]; 321: from_shell(Config) when is_list(Config) -> 322: ?line setup(Config), 323: ?line Fun = do_eval("fun({a,b}) -> true end"), 324: ?line [{{a,b},[],[true]}] = apply(ets,fun2ms,[Fun]), 325: ?line [{{a,b},[],[true]}] = do_eval("ets:fun2ms(fun({a,b}) -> true end)"), 326: ?line Fun2 = do_eval("fun([a,b]) -> message(banan), return_trace() end"), 327: ?line [{[a,b],[],[{message,banan},{return_trace}]}] 328: = apply(dbg,fun2ms,[Fun2]), 329: ?line [{[a,b],[],[{message,banan},{return_trace}]}] = 330: do_eval( 331: "dbg:fun2ms(fun([a,b]) -> message(banan), return_trace() end)"), 332: ok. 333: 334: records(suite) -> 335: []; 336: records(doc) -> 337: ["Tests expansion of records in fun2ms"]; 338: records(Config) when is_list(Config) -> 339: ?line setup(Config), 340: ?line RD = <<"-record(t, {" 341: "t1 = []," 342: "t2 = foo," 343: "t3," 344: "t4" 345: "}).">>, 346: ?line [{{t,'$1','$2',foo,'_'},[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]}, 347: {{t,'_','_','_','_'},[{'==',{element,2,'$_'},nisse}],[{{'$*'}}]}] = 348: compile_and_run(RD,<< 349: "ets:fun2ms(fun(#t{t1 = X, t2 = Y, t3 = foo}) when is_list(X) -> 350: {hd(X),object()}; 351: (#t{}) when (object())#t.t1 == nisse -> 352: {bindings()} 353: end)">>), 354: ?line [{{t,'$1','$2','_',foo}, 355: [{'==',{element,4,'$_'},7},{is_list,'$1'}], 356: [{{{hd,'$1'},'$_'}}]}, 357: {'$1',[{is_record,'$1',t,5}], 358: [{{{element,2,'$1'}, 359: {{t,'$1',foo,undefined,undefined}}, 360: {{t,{element,2,'$1'},{element,3,'$1'},{element,4,'$1'},boooo}}}}]}] = 361: compile_and_run(RD,<< 362: "ets:fun2ms(fun(#t{t1 = X, t2 = Y, t4 = foo}) when 363: (object())#t.t3==7,is_list(X) -> 364: {hd(X),object()}; 365: (A) when is_record(A,t) -> 366: {A#t.t1 367: ,#t{t1=A} 368: ,A#t{t4=boooo} 369: } 370: end)" 371: >>), 372: ?line [{[{t,'$1','$2',foo,'_'}],[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]}, 373: {[{t,'_','_','_','_'}],[{'==',{element,2,{hd,'$_'}},nisse}],[{{'$*'}}]}]= 374: compile_and_run(RD,<< 375: "dbg:fun2ms(fun([#t{t1 = X, t2 = Y, t3 = foo}]) when is_list(X) -> 376: {hd(X),object()}; 377: ([#t{}]) when (hd(object()))#t.t1 == nisse -> 378: {bindings()} 379: end)" 380: >>), 381: ok. 382: 383: 384: record_index(suite) -> 385: []; 386: record_index(doc) -> 387: ["Tests expansion of records in fun2ms, part 2"]; 388: record_index(Config) when is_list(Config) -> 389: ?line setup(Config), 390: ?line RD = <<"-record(a,{a,b}).">>, 391: ?line [{{2},[],[true]}] = compile_and_run(RD, 392: <<"ets:fun2ms(fun({#a.a}) -> true end)">>), 393: ?line [{{2},[],[2]}] = compile_and_run(RD, 394: <<"ets:fun2ms(fun({#a.a}) -> #a.a end)">>), 395: ?line [{{2,'$1'},[{'>','$1',2}],[2]}] = compile_and_run(RD, 396: <<"ets:fun2ms(fun({#a.a,A}) when A > #a.a -> #a.a end)">>), 397: ok. 398: 399: top_match(suite) -> 400: []; 401: top_match(doc) -> 402: ["Tests matching on top level in head to give alias for object()"]; 403: top_match(Config) when is_list(Config) -> 404: ?line setup(Config), 405: ?line RD = <<"-record(a,{a,b}).">>, 406: ?line [{{a,3,'_'},[],['$_']}] = 407: compile_and_run(RD, 408: <<"ets:fun2ms(fun(A = #a{a=3}) -> A end)">>), 409: ?line [{{a,3,'_'},[],['$_']}] = 410: compile_and_run(RD, 411: <<"ets:fun2ms(fun(#a{a=3} = A) -> A end)">>), 412: ?line [{[a,b],[],['$_']}] = 413: compile_and_run(RD, 414: <<"dbg:fun2ms(fun(A = [a,b]) -> A end)">>), 415: ?line [{[a,b],[],['$_']}] = 416: compile_and_run(RD, 417: <<"dbg:fun2ms(fun([a,b] = A) -> A end)">>), 418: ?line expect_failure(RD, 419: <<"ets:fun2ms(fun({a,A = {_,b}}) -> A end)">>), 420: ?line expect_failure(RD, 421: <<"dbg:fun2ms(fun([a,A = {_,b}]) -> A end)">>), 422: ?line expect_failure(RD, 423: <<"ets:fun2ms(fun(A#a{a = 2}) -> A end)">>), 424: ok. 425: 426: multipass(suite) -> 427: []; 428: multipass(doc) -> 429: ["Tests that multi-defined fields in records give errors."]; 430: multipass(Config) when is_list(Config) -> 431: ?line setup(Config), 432: ?line RD = <<"-record(a,{a,b}).">>, 433: ?line expect_failure(RD,<<"ets:fun2ms(fun(A) -> #a{a=2,a=3} end)">>), 434: ?line expect_failure(RD,<<"ets:fun2ms(fun(A) -> A#a{a=2,a=3} end)">>), 435: ?line expect_failure(RD,<<"ets:fun2ms(fun(A) when A =:= #a{a=2,a=3} ->", 436: " true end)">>), 437: ?line expect_failure(RD,<<"ets:fun2ms(fun({A,B})when A =:= B#a{a=2,a=3}->", 438: "true end)">>), 439: ?line expect_failure(RD,<<"ets:fun2ms(fun(#a{a=3,a=3}) -> true end)">>), 440: ?line compile_and_run(RD,<<"ets:fun2ms(fun(A) -> #a{a=2,b=3} end)">>), 441: ?line compile_and_run(RD,<<"ets:fun2ms(fun(A) -> A#a{a=2,b=3} end)">>), 442: ?line compile_and_run(RD,<<"ets:fun2ms(fun(A) when A =:= #a{a=2,b=3} ->", 443: " true end)">>), 444: ?line compile_and_run(RD,<<"ets:fun2ms(fun({A,B})when A=:= B#a{a=2,b=3}->", 445: "true end)">>), 446: ?line compile_and_run(RD,<<"ets:fun2ms(fun(#a{a=3,b=3}) -> true end)">>), 447: ok. 448: 449: 450: old_guards(suite) -> 451: []; 452: old_guards(doc) -> 453: ["Tests that old type tests in guards are translated"]; 454: old_guards(Config) when is_list(Config) -> 455: ?line setup(Config), 456: Tests = [ 457: {atom,is_atom}, 458: {float,is_float}, 459: {integer,is_integer}, 460: {list,is_list}, 461: {number,is_number}, 462: {pid,is_pid}, 463: {port,is_port}, 464: {reference,is_reference}, 465: {tuple,is_tuple}, 466: {binary,is_binary}, 467: {function,is_function}], 468: ?line lists:foreach( 469: fun({Old,New}) -> 470: Bin = list_to_binary([<<"ets:fun2ms(fun(X) when ">>, 471: atom_to_list(Old), 472: <<"(X) -> true end)">>]), 473: case compile_and_run(Bin) of 474: [{'$1',[{New,'$1'}],[true]}] -> 475: ok; 476: _ -> 477: exit({bad_result_for, binary_to_list(Bin)}) 478: end 479: end, 480: Tests), 481: ?line RD = <<"-record(a,{a,b}).">>, 482: ?line [{'$1',[{is_record,'$1',a,3}],[true]}] = 483: compile_and_run(RD, 484: <<"ets:fun2ms(fun(X) when record(X,a) -> true end)">>), 485: ?line expect_failure 486: (RD, 487: <<"ets:fun2ms(fun(X) when integer(X) and constant(X) -> " 488: "true end)">>), 489: ?line [{'$1',[{is_integer,'$1'}, 490: {is_float,'$1'}, 491: {is_atom,'$1'}, 492: {is_list,'$1'}, 493: {is_number,'$1'}, 494: {is_pid,'$1'}, 495: {is_port,'$1'}, 496: {is_reference,'$1'}, 497: {is_tuple,'$1'}, 498: {is_binary,'$1'}, 499: {is_record,'$1',a,3}], 500: [true]}] = 501: compile_and_run(RD, << 502: "ets:fun2ms(fun(X) when integer(X)," 503: "float(X), atom(X)," 504: "list(X), number(X), pid(X)," 505: "port(X), reference(X), tuple(X)," 506: "binary(X), record(X,a) -> true end)" 507: >>), 508: ok. 509: 510: autoimported(suite) -> 511: []; 512: autoimported(doc) -> 513: ["Tests use of autoimported bif's used like erlang:'+'(A,B) in guards" 514: " and body."]; 515: autoimported(Config) when is_list(Config) -> 516: ?line setup(Config), 517: Allowed = [ 518: {abs,1}, 519: {element,2}, 520: {hd,1}, 521: {length,1}, 522: {node,0}, 523: {node,1}, 524: {round,1}, 525: {size,1}, 526: {tl,1}, 527: {trunc,1}, 528: {self,0}, 529: %{float,1}, see float_1_function/1 530: {is_atom,1}, 531: {is_float,1}, 532: {is_integer,1}, 533: {is_list,1}, 534: {is_number,1}, 535: {is_pid,1}, 536: {is_port,1}, 537: {is_reference,1}, 538: {is_tuple,1}, 539: {is_binary,1}, 540: {is_function,1}, 541: {is_record,2,magic}, 542: {'and',2,infix}, 543: {'or',2,infix}, 544: {'xor',2,infix}, 545: {'not',1}, 546: %{'andalso',2,infix}, 547: %{'orelse',2,infix}, 548: {'+',1}, 549: {'+',2,infix}, 550: {'-',1}, 551: {'-',2,infix}, 552: {'*',2,infix}, 553: {'/',2,infix}, 554: {'div',2,infix}, 555: {'rem',2,infix}, 556: {'band',2,infix}, 557: {'bor',2,infix}, 558: {'bxor',2,infix}, 559: {'bnot',1}, 560: {'bsl',2,infix}, 561: {'bsr',2,infix}, 562: {'>',2,infix}, 563: {'>=',2,infix}, 564: {'<',2,infix}, 565: {'=<',2,infix}, 566: {'==',2,infix}, 567: {'=:=',2,infix}, 568: {'/=',2,infix}, 569: {'=/=',2,infix}], 570: ?line RD = <<"-record(a,{a,b}).">>, 571: ?line lists:foreach( 572: fun({A,0}) -> 573: L = atom_to_list(A), 574: Bin1 = list_to_binary( 575: [ 576: <<"ets:fun2ms(fun(X) when ">>, 577: L,<<"() -> ">>, 578: L,<<"() end)">> 579: ]), 580: Bin2 = list_to_binary( 581: [ 582: <<"ets:fun2ms(fun(X) when erlang:'">>, 583: L,<<"'() -> erlang:'">>, 584: L,<<"'() end)">> 585: ]), 586: Res1 = compile_and_run(Bin1), 587: Res2 = compile_and_run(Bin2), 588: case Res1 =:= Res2 of 589: true -> 590: ok; 591: false -> 592: exit({not_equal,{Res1,Res2,A}}) 593: end; 594: ({A,1}) -> 595: L = atom_to_list(A), 596: Bin1 = list_to_binary( 597: [ 598: <<"ets:fun2ms(fun(X) when ">>, 599: L,<<"(X) -> ">>, 600: L,<<"(X) end)">> 601: ]), 602: Bin2 = list_to_binary( 603: [ 604: <<"ets:fun2ms(fun(X) when erlang:'">>, 605: L,<<"'(X) -> erlang:'">>, 606: L,<<"'(X) end)">> 607: ]), 608: Res1 = compile_and_run(Bin1), 609: Res2 = compile_and_run(Bin2), 610: case Res1 =:= Res2 of 611: true -> 612: ok; 613: false -> 614: exit({not_equal,{Res1,Res2,A}}) 615: end; 616: ({A,2}) -> 617: L = atom_to_list(A), 618: Bin1 = list_to_binary( 619: [ 620: <<"ets:fun2ms(fun({X,Y}) when ">>, 621: L,<<"(X,Y) -> ">>, 622: L,<<"(X,Y) end)">> 623: ]), 624: Bin2 = list_to_binary( 625: [ 626: <<"ets:fun2ms(fun({X,Y}) when erlang:'">>, 627: L,<<"'(X,Y) -> erlang:'">>, 628: L,<<"'(X,Y) end)">> 629: ]), 630: Res1 = compile_and_run(Bin1), 631: Res2 = compile_and_run(Bin2), 632: case Res1 =:= Res2 of 633: true -> 634: ok; 635: false -> 636: exit({not_equal,{Res1,Res2,A}}) 637: end; 638: ({A,2,infix}) -> 639: L = atom_to_list(A), 640: Bin1 = list_to_binary( 641: [ 642: <<"ets:fun2ms(fun({X,Y}) when X ">>, 643: L,<<" Y -> X ">>, 644: L,<<" Y end)">> 645: ]), 646: Bin2 = list_to_binary( 647: [ 648: <<"ets:fun2ms(fun({X,Y}) when erlang:'">>, 649: L,<<"'(X,Y) -> erlang:'">>, 650: L,<<"'(X,Y) end)">> 651: ]), 652: Res1 = compile_and_run(Bin1), 653: Res2 = compile_and_run(Bin2), 654: case Res1 =:= Res2 of 655: true -> 656: ok; 657: false -> 658: exit({not_equal,{Res1,Res2,A}}) 659: end; 660: ({A,2,magic}) -> %is_record 661: L = atom_to_list(A), 662: Bin1 = list_to_binary( 663: [ 664: <<"ets:fun2ms(fun(X) when ">>, 665: L,<<"(X,a) -> ">>, 666: L,<<"(X,a) end)">> 667: ]), 668: Bin2 = list_to_binary( 669: [ 670: <<"ets:fun2ms(fun(X) when erlang:'">>, 671: L,<<"'(X,a) -> erlang:'">>, 672: L,<<"'(X,a) end)">> 673: ]), 674: Res1 = compile_and_run(RD,Bin1), 675: Res2 = compile_and_run(RD,Bin2), 676: case Res1 =:= Res2 of 677: true -> 678: ok; 679: false -> 680: exit({not_equal,{Res1,Res2,A}}) 681: end 682: end, 683: Allowed), 684: ok. 685: 686: semicolon(suite) -> 687: []; 688: semicolon(doc) -> 689: ["Tests semicolon in guards of match_specs."]; 690: semicolon(Config) when is_list(Config) -> 691: ?line setup(Config), 692: ?line Res01 = compile_and_run 693: (<<"ets:fun2ms(fun(X) when is_integer(X); " 694: "is_float(X) -> true end)">>), 695: ?line Res02 = compile_and_run 696: (<<"ets:fun2ms(fun(X) when is_integer(X) -> true; " 697: "(X) when is_float(X) -> true end)">>), 698: ?line Res01 = Res02, 699: ?line Res11 = compile_and_run 700: (<<"ets:fun2ms(fun(X) when is_integer(X); " 701: "is_float(X); atom(X) -> true end)">>), 702: ?line Res12 = compile_and_run 703: (<<"ets:fun2ms(fun(X) when is_integer(X) -> true; " 704: "(X) when is_float(X) -> true; " 705: "(X) when is_atom(X) -> true end)">>), 706: ?line Res11 = Res12, 707: ok. 708: 709: 710: float_1_function(suite) -> 711: []; 712: float_1_function(doc) -> 713: ["OTP-5297. The function float/1."]; 714: float_1_function(Config) when is_list(Config) -> 715: ?line setup(Config), 716: RunMS = fun(L, MS) -> 717: ets:match_spec_run(L, ets:match_spec_compile(MS)) 718: end, 719: ?line MS1 = compile_and_run 720: (<<"ets:fun2ms(fun(X) -> float(X) end)">>), 721: ?line [F1] = RunMS([3], MS1), 722: ?line true = is_float(F1) and (F1 == 3), 723: 724: ?line MS1b = compile_and_run 725: (<<"dbg:fun2ms(fun(X) -> float(X) end)">>), 726: ?line [F2] = RunMS([3], MS1b), 727: ?line true = is_float(F2) and (F2 == 3), 728: 729: ?line MS2 = compile_and_run 730: (<<"ets:fun2ms(fun(X) when is_pid(X) or float(X) -> true end)">>), 731: ?line [] = RunMS([3.0], MS2), 732: 733: ?line MS3 = compile_and_run 734: (<<"dbg:fun2ms(fun(X) when is_pid(X); float(X) -> true end)">>), 735: ?line [true] = RunMS([3.0], MS3), 736: 737: ?line MS4 = compile_and_run 738: (<<"ets:fun2ms(fun(X) when erlang:float(X) > 1 -> big;" 739: " (_) -> small end)">>), 740: ?line [small,big] = RunMS([1.0, 3.0], MS4), 741: 742: ?line MS5 = compile_and_run 743: (<<"ets:fun2ms(fun(X) when float(X) > 1 -> big;" 744: " (_) -> small end)">>), 745: ?line [small,big] = RunMS([1.0, 3.0], MS5), 746: 747: %% This is the test from autoimported/1. 748: ?line [{'$1',[{is_float,'$1'}],[{float,'$1'}]}] = 749: compile_and_run 750: (<<"ets:fun2ms(fun(X) when float(X) -> float(X) end)">>), 751: ?line [{'$1',[{float,'$1'}],[{float,'$1'}]}] = 752: compile_and_run 753: (<<"ets:fun2ms(fun(X) when erlang:'float'(X) -> " 754: "erlang:'float'(X) end)">>), 755: ok. 756: 757: 758: action_function(suite) -> 759: []; 760: action_function(doc) -> 761: ["Test all 'action functions'."]; 762: action_function(Config) when is_list(Config) -> 763: ?line setup(Config), 764: ?line [{['$1','$2'],[], 765: [{set_seq_token,label,0}, 766: {get_seq_token}, 767: {message,'$1'}, 768: {return_trace}, 769: {exception_trace}]}] = 770: compile_and_run 771: (<<"dbg:fun2ms(fun([X,Y]) -> " 772: "set_seq_token(label, 0), " 773: "get_seq_token(), " 774: "message(X), " 775: "return_trace(), " 776: "exception_trace() end)">>), 777: ?line [{['$1','$2'],[], 778: [{process_dump}, 779: {enable_trace,send}, 780: {enable_trace,'$2',send}, 781: {disable_trace,procs}, 782: {disable_trace,'$2',procs}]}] = 783: compile_and_run 784: (<<"dbg:fun2ms(fun([X,Y]) -> " 785: "process_dump(), " 786: "enable_trace(send), " 787: "enable_trace(Y, send), " 788: "disable_trace(procs), " 789: "disable_trace(Y, procs) end)">>), 790: ?line [{['$1','$2'], 791: [], 792: [{display,'$1'}, 793: {caller}, 794: {set_tcw,{const,16}}, 795: {silent,true}, 796: {trace,[send],[procs]}, 797: {trace,'$2',[procs],[send]}]}] = 798: compile_and_run 799: (<<"A = 16, dbg:fun2ms(fun([X,Y]) -> " 800: "display(X), " 801: "caller(), " 802: "set_tcw(A), " 803: "silent(true), " 804: "trace([send], [procs]), " 805: "trace(Y, [procs], [send]) end)">>), 806: ok. 807: 808: 809: 810: 811: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 812: % Helpers 813: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 814: 815: setup(Config) -> 816: put(mts_config,Config), 817: put(mts_tf_counter,0). 818: 819: temp_name() -> 820: Conf = get(mts_config), 821: C = get(mts_tf_counter), 822: put(mts_tf_counter,C+1), 823: filename:join([?config(priv_dir,Conf), 824: "tempfile"++integer_to_list(C)++".tmp"]). 825: 826: 827: expect_failure(Recs,Code) -> 828: case (catch compile_and_run(Recs,Code)) of 829: {'EXIT',_Foo} -> 830: %erlang:display(_Foo), 831: ok; 832: Other -> 833: exit({expected,failure,got,Other}) 834: end. 835: 836: compile_and_run(Expr) -> 837: compile_and_run(<<>>,Expr). 838: compile_and_run(Records,Expr) -> 839: Prog = << 840: "-module(tmp).\n", 841: "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", 842: "-export([tmp/0]).\n", 843: Records/binary,"\n", 844: "tmp() ->\n", 845: Expr/binary,".\n">>, 846: FN=temp_name(), 847: file:write_file(FN,Prog), 848: {ok,Forms} = epp:parse_file(FN,"",""), 849: {ok,tmp,Bin} = compile:forms(Forms), 850: code:load_binary(tmp,FN,Bin), 851: tmp:tmp(). 852: 853: compile_ww(Expr) -> 854: compile_ww(<<>>,Expr). 855: compile_ww(Records,Expr) -> 856: Prog = << 857: "-module(tmp).\n", 858: "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", 859: "-export([tmp/0]).\n", 860: Records/binary,"\n", 861: "tmp() ->\n", 862: Expr/binary,".\n">>, 863: FN=temp_name(), 864: file:write_file(FN,Prog), 865: {ok,Forms} = epp:parse_file(FN,"",""), 866: {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings, 867: nowarn_unused_vars, 868: nowarn_unused_record]), 869: Wlist. 870: 871: compile_no_ww(Expr) -> 872: Prog = << 873: "-module(tmp).\n", 874: "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", 875: "-export([tmp/1]).\n\n", 876: Expr/binary,".\n">>, 877: FN=temp_name(), 878: file:write_file(FN,Prog), 879: {ok,Forms} = epp:parse_file(FN,"",""), 880: {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings, 881: nowarn_unused_vars, 882: nowarn_unused_record]), 883: Wlist. 884: 885: do_eval(String) -> 886: {done,{ok,T,_},[]} = erl_scan:tokens( 887: [], 888: String++".\n",1), 889: {ok,Tree} = erl_parse:parse_exprs(T), 890: {value,Res,[]} = erl_eval:exprs(Tree,[]), 891: Res.