1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(compile_SUITE). 21 22%% Tests compile:file/1 and compile:file/2 with various options. 23 24-include_lib("common_test/include/ct.hrl"). 25-include_lib("stdlib/include/erl_compile.hrl"). 26 27-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 28 init_per_group/2,end_per_group/2, 29 app_test/1,appup_test/1, 30 debug_info/4, custom_debug_info/1, custom_compile_info/1, 31 file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1, 32 binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, 33 other_output/1, kernel_listing/1, encrypted_abstr/1, 34 strict_record/1, utf8_atoms/1, utf8_functions/1, extra_chunks/1, 35 cover/1, env/1, core_pp/1, tuple_calls/1, 36 core_roundtrip/1, asm/1, optimized_guards/1, 37 sys_pre_attributes/1, dialyzer/1, 38 warnings/1, pre_load_check/1, env_compiler_options/1, 39 bc_options/1, deterministic_include/1, deterministic_paths/1 40 ]). 41 42suite() -> [{ct_hooks,[ts_install_cth]}]. 43 44%% To cover the stripping of 'type' and 'spec' in beam_asm. 45-type all_return_type() :: [atom()]. 46-spec all() -> all_return_type(). 47 48all() -> 49 [app_test, appup_test, file_1, forms_2, module_mismatch, big_file, outdir, 50 binary, makedep, cond_and_ifdef, listings, listings_big, 51 other_output, kernel_listing, encrypted_abstr, tuple_calls, 52 strict_record, utf8_atoms, utf8_functions, extra_chunks, 53 cover, env, core_pp, core_roundtrip, asm, optimized_guards, 54 sys_pre_attributes, dialyzer, warnings, pre_load_check, 55 env_compiler_options, custom_debug_info, bc_options, 56 custom_compile_info, deterministic_include, deterministic_paths]. 57 58groups() -> 59 []. 60 61init_per_suite(Config) -> 62 test_lib:recompile(?MODULE), 63 Config. 64 65end_per_suite(_Config) -> 66 ok. 67 68init_per_group(_GroupName, Config) -> 69 Config. 70 71end_per_group(_GroupName, Config) -> 72 Config. 73 74 75 76%% Test that the Application file has no `basic' errors."; 77app_test(Config) when is_list(Config) -> 78 test_server:app_test(compiler). 79 80%% Test that the Application upgrade file has no `basic' errors."; 81appup_test(Config) when is_list(Config) -> 82 ok = test_server:appup_test(compiler). 83 84%% Tests that we can compile and run a simple Erlang program, 85%% using compile:file/1. 86 87file_1(Config) when is_list(Config) -> 88 process_flag(trap_exit, true), 89 90 {Simple, Target} = get_files(Config, simple, "file_1"), 91 {ok, Cwd} = file:get_cwd(), 92 ok = file:set_cwd(filename:dirname(Target)), 93 94 %% Native from BEAM without compilation info. 95 {ok,simple} = compile:file(Simple, [slim]), %Smoke test only. 96 {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. 97 98 %% Native from BEAM with compilation info. 99 {ok,simple} = compile:file(Simple), %Smoke test only. 100 {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. 101 102 {ok,simple} = compile:file(Simple, [native,report]), %Smoke test. 103 104 compile_and_verify(Simple, Target, []), 105 compile_and_verify(Simple, Target, [native]), 106 compile_and_verify(Simple, Target, [debug_info]), 107 {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage 108 109 {ok,simple} = compile:file(Simple, [{eprof,beam_z}]), %Coverage 110 111 112 %% Test option 'deterministic'. 113 {ok,simple} = compile:file(Simple, [deterministic]), 114 {module,simple} = c:l(simple), 115 [{version,_}] = simple:module_info(compile), 116 true = code:delete(simple), 117 false = code:purge(simple), 118 119 ok = file:set_cwd(Cwd), 120 true = exists(Target), 121 passed = run(Target, test, []), 122 123 %% Test option 'deterministic' as a compiler attribute. 124 Det = deterministic_module, 125 {DetPath, DetTarget} = get_files(Config, Det, "det_target"), 126 {ok,Det,DetCode} = compile:file(DetPath, [binary]), 127 {module,Det} = code:load_binary(Det, "", DetCode), 128 [{version,_}] = Det:module_info(compile), 129 true = code:delete(Det), 130 false = code:purge(Det), 131 132 %% Cleanup. 133 ok = file:delete(Target), 134 ok = file:del_dir(filename:dirname(Target)), 135 ok = file:del_dir(filename:dirname(DetTarget)), 136 137 %% There should not be any messages in the messages. 138 receive 139 Any -> 140 ct:fail({unexpected,Any}) 141 after 10 -> 142 ok 143 end, 144 145 ok. 146 147forms_2(Config) when is_list(Config) -> 148 Src = "/foo/bar", 149 AbsSrc = filename:absname(Src), 150 Anno = erl_anno:new(1), 151 SimpleCode = [{attribute,Anno,module,simple}], 152 {ok,simple,Bin1} = compile:forms(SimpleCode, [binary,{source,Src}]), 153 154 %% Load and test that the proper source is returned. 155 AbsSrc = forms_load_code(simple, Src, Bin1), 156 157 %% Work in a deleted directory. 158 PrivDir = proplists:get_value(priv_dir, Config), 159 WorkDir = filename:join(PrivDir, ?FUNCTION_NAME), 160 ok = file:make_dir(WorkDir), 161 ok = file:set_cwd(WorkDir), 162 case os:type() of 163 {unix,_} -> os:cmd("rm -rf " ++ WorkDir); 164 _ -> ok 165 end, 166 {ok,simple,Bin2} = compile:forms(SimpleCode), 167 undefined = forms_load_code(simple, "ignore", Bin2), 168 169 {ok,simple,Bin3} = compile:forms(SimpleCode, [{source,Src},report]), 170 case forms_load_code(simple, "ignore", Bin3) of 171 Src -> %Unix. 172 ok; 173 AbsSrc -> %Windows. 174 ok 175 end, 176 177 {ok,simple,Core} = compile:forms(SimpleCode, [to_core0,binary]), 178 forms_compile_and_load(Core, [from_core]), 179 forms_compile_and_load(Core, [from_core,native]), 180 181 {ok,simple,Asm} = compile:forms(SimpleCode, [to_asm,binary]), 182 forms_compile_and_load(Asm, [from_asm]), 183 forms_compile_and_load(Asm, [from_asm,native]), 184 185 {ok,simple,Beam} = compile:forms(SimpleCode, []), 186 forms_compile_and_load(Beam, [from_beam]), 187 forms_compile_and_load(Beam, [from_beam,native]), 188 189 %% Cover the error handling code. 190 error = compile:forms(bad_core, [from_core,report]), 191 error = compile:forms(bad_asm, [from_asm,report]), 192 error = compile:forms(<<"bad_beam">>, [from_beam,report]), 193 error = compile:forms(<<"bad_beam">>, [from_beam,native,report]), 194 195 ok. 196 197 198forms_load_code(Mod, Src, Bin) -> 199 {module,Mod} = code:load_binary(Mod, Src, Bin), 200 Info = Mod:module_info(compile), 201 SourceOption = proplists:get_value(source, Info), 202 203 %% Ensure that the options are not polluted with 'source'. 204 [] = proplists:get_value(options, Info), 205 206 %% Cleanup. 207 true = code:delete(simple), 208 false = code:purge(simple), 209 210 SourceOption. 211 212forms_compile_and_load(Code, Opts) -> 213 Mod = simple, 214 {ok,Mod,Bin} = compile:forms(Code, Opts), 215 {module,Mod} = code:load_binary(Mod, "ignore", Bin), 216 _ = Mod:module_info(), 217 true = code:delete(simple), 218 false = code:purge(simple), 219 ok. 220 221module_mismatch(Config) when is_list(Config) -> 222 DataDir = proplists:get_value(data_dir, Config), 223 File = filename:join(DataDir, "wrong_module_name.erl"), 224 {error,[{"wrong_module_name.beam", 225 [{none,compile,{module_name,arne,"wrong_module_name"}}]}], 226 []} = compile:file(File, [return]), 227 error = compile:file(File, [report]), 228 229 {ok,arne,[]} = compile:file(File, 230 [return,no_error_module_mismatch]), 231 232 ok. 233 234big_file(Config) when is_list(Config) -> 235 {Big,Target} = get_files(Config, big, "big_file"), 236 ok = file:set_cwd(filename:dirname(Target)), 237 compile_and_verify(Big, Target, []), 238 compile_and_verify(Big, Target, [debug_info]), 239 compile_and_verify(Big, Target, [no_postopt]), 240 241 %% Cleanup. 242 ok = file:delete(Target), 243 ok. 244 245%% Tests that the {outdir, Dir} option works. 246 247outdir(Config) when is_list(Config) -> 248 {Simple, Target} = get_files(Config, simple, "outdir"), 249 {ok, simple} = compile:file(Simple, [{outdir, filename:dirname(Target)}]), 250 true = exists(Target), 251 passed = run(Target, test, []), 252 ok = file:delete(Target), 253 ok = file:del_dir(filename:dirname(Target)), 254 ok. 255 256%% Tests that the binary option works. 257 258binary(Config) when is_list(Config) -> 259 {Simple, Target} = get_files(Config, simple, "binary"), 260 {ok, simple, Binary} = compile:file(Simple, [binary]), 261 code:load_binary(simple, Target, Binary), 262 passed = simple:test(), 263 true = code:delete(simple), 264 false = code:purge(simple), 265 ok = file:del_dir(filename:dirname(Target)), 266 ok. 267 268%% Tests that the dependencies-Makefile-related options work. 269 270makedep(Config) when is_list(Config) -> 271 {Simple,Target} = get_files(Config, simple, "makedep"), 272 DataDir = proplists:get_value(data_dir, Config), 273 SimpleRootname = filename:rootname(Simple), 274 IncludeDir = filename:join(filename:dirname(Simple), "include"), 275 IncludeOptions = [ 276 {d,need_foo}, 277 {d,foo_value,42}, 278 {d,include_generated}, 279 {i,IncludeDir} 280 ], 281 %% Basic rule. 282 BasicMf1Name = SimpleRootname ++ "-basic1.mk", 283 {ok,BasicMf1} = file:read_file(BasicMf1Name), 284 {ok,_,Mf1} = compile:file(Simple, [binary,makedep]), 285 BasicMf1 = makedep_canonicalize_result(Mf1, DataDir), 286 %% Basic rule with one existing header. 287 BasicMf2Name = SimpleRootname ++ "-basic2.mk", 288 {ok,BasicMf2} = file:read_file(BasicMf2Name), 289 {ok,_,Mf2} = compile:file(Simple, [binary,makedep|IncludeOptions]), 290 BasicMf2 = makedep_canonicalize_result(Mf2, DataDir), 291 %% Rule with one existing header and one missing header. 292 MissingMfName = SimpleRootname ++ "-missing.mk", 293 {ok,MissingMf} = file:read_file(MissingMfName), 294 {ok,_,Mf3} = compile:file(Simple, 295 [binary,makedep,makedep_add_missing|IncludeOptions]), 296 MissingMf = makedep_canonicalize_result(Mf3, DataDir), 297 %% Rule with modified target. 298 TargetMf1Name = SimpleRootname ++ "-target1.mk", 299 {ok,TargetMf1} = file:read_file(TargetMf1Name), 300 {ok,_,Mf4} = compile:file(Simple, 301 [binary,makedep,{makedep_target,"$target"}|IncludeOptions]), 302 TargetMf1 = makedep_modify_target( 303 makedep_canonicalize_result(Mf4, DataDir), "$$target"), 304 %% Rule with quoted modified target. 305 TargetMf2Name = SimpleRootname ++ "-target2.mk", 306 {ok,TargetMf2} = file:read_file(TargetMf2Name), 307 {ok,_,Mf5} = compile:file(Simple, 308 [binary,makedep,{makedep_target,"$target"},makedep_quote_target| 309 IncludeOptions]), 310 TargetMf2 = makedep_modify_target( 311 makedep_canonicalize_result(Mf5, DataDir), "$$target"), 312 %% Basic rule written to some file. 313 {ok,_} = compile:file(Simple, 314 [makedep,{makedep_output,Target}|IncludeOptions]), 315 {ok,Mf6} = file:read_file(Target), 316 BasicMf2 = makedep_canonicalize_result(Mf6, DataDir), 317 %% Rule with creating phony target. 318 PhonyMfName = SimpleRootname ++ "-phony.mk", 319 {ok,PhonyMf} = file:read_file(PhonyMfName), 320 {ok,_,Mf7} = compile:file(Simple, 321 [binary,makedep,makedep_phony|IncludeOptions]), 322 PhonyMf = makedep_canonicalize_result(Mf7, DataDir), 323 324 ok = file:delete(Target), 325 ok = file:del_dir(filename:dirname(Target)), 326 ok. 327 328makedep_canonicalize_result(Mf, DataDir) -> 329 Mf0 = binary_to_list(Mf), 330 %% Replace the Datadir by "$(srcdir)". 331 Mf1 = re:replace(Mf0, DataDir, "$(srcdir)/", 332 [global,multiline,{return,list}]), 333 %% Long lines are splitted, put back everything on one line. 334 Mf2 = re:replace(Mf1, "\\\\\n ", "", [global,multiline,{return,list}]), 335 list_to_binary(Mf2). 336 337makedep_modify_target(Mf, Target) -> 338 Mf0 = binary_to_list(Mf), 339 Mf1 = re:replace(Mf0, Target, "$target", [{return,list}]), 340 list_to_binary(Mf1). 341 342%% Tests that conditional compilation, defining values, including files work. 343 344cond_and_ifdef(Config) when is_list(Config) -> 345 {Simple, Target} = get_files(Config, simple, "cond_and_ifdef"), 346 IncludeDir = filename:join(filename:dirname(Simple), "include"), 347 Options = [{outdir, filename:dirname(Target)}, 348 {d, need_foo}, {d, foo_value, 42}, 349 {i, IncludeDir}, report], 350 {ok, simple} = compile:file(Simple, Options), 351 true = exists(Target), 352 {hiker, 42} = run(Target, foo, []), 353 ok = file:delete(Target), 354 ok = file:del_dir(filename:dirname(Target)), 355 ok. 356 357listings(Config) when is_list(Config) -> 358 DataDir = proplists:get_value(data_dir, Config), 359 PrivDir = proplists:get_value(priv_dir, Config), 360 ok = do_file_listings(DataDir, PrivDir, [ 361 "simple", 362 "small", 363 "small_maps" 364 ]), 365 ok. 366 367do_file_listings(_, _, []) -> ok; 368do_file_listings(DataDir, PrivDir, [File|Files]) -> 369 Simple = filename:join(DataDir, File), 370 TargetDir = filename:join(PrivDir, listings), 371 ok = file:make_dir(TargetDir), 372 373 %% Test all dedicated listing options. 374 do_listing(Simple, TargetDir, 'S'), 375 do_listing(Simple, TargetDir, 'E'), 376 do_listing(Simple, TargetDir, 'P'), 377 do_listing(Simple, TargetDir, dpp, ".pp"), 378 do_listing(Simple, TargetDir, dabstr, ".abstr"), 379 do_listing(Simple, TargetDir, dexp, ".expand"), 380 do_listing(Simple, TargetDir, dcore, ".core"), 381 do_listing(Simple, TargetDir, doldinline, ".oldinline"), 382 do_listing(Simple, TargetDir, dinline, ".inline"), 383 do_listing(Simple, TargetDir, dcore, ".core"), 384 do_listing(Simple, TargetDir, dcopt, ".copt"), 385 do_listing(Simple, TargetDir, dcbsm, ".core_bsm"), 386 do_listing(Simple, TargetDir, dsetel, ".dsetel"), 387 do_listing(Simple, TargetDir, dkern, ".kernel"), 388 do_listing(Simple, TargetDir, dcg, ".codegen"), 389 do_listing(Simple, TargetDir, dblk, ".block"), 390 do_listing(Simple, TargetDir, dexcept, ".except"), 391 do_listing(Simple, TargetDir, dbs, ".bs"), 392 do_listing(Simple, TargetDir, dtype, ".type"), 393 do_listing(Simple, TargetDir, ddead, ".dead"), 394 do_listing(Simple, TargetDir, djmp, ".jump"), 395 do_listing(Simple, TargetDir, dclean, ".clean"), 396 do_listing(Simple, TargetDir, dpeep, ".peep"), 397 do_listing(Simple, TargetDir, dopt, ".optimize"), 398 399 %% First clean up. 400 Listings = filename:join(PrivDir, listings), 401 lists:foreach(fun(F) -> ok = file:delete(F) end, 402 filelib:wildcard(filename:join(Listings, "*"))), 403 404 %% Test options that produce a listing file if 'binary' is not given. 405 do_listing(Simple, TargetDir, to_pp, ".P"), 406 do_listing(Simple, TargetDir, to_exp, ".E"), 407 do_listing(Simple, TargetDir, to_core0, ".core"), 408 ok = file:delete(filename:join(Listings, File ++ ".core")), 409 do_listing(Simple, TargetDir, to_core, ".core"), 410 do_listing(Simple, TargetDir, to_kernel, ".kernel"), 411 do_listing(Simple, TargetDir, to_dis, ".dis"), 412 413 %% Final clean up. 414 lists:foreach(fun(F) -> ok = file:delete(F) end, 415 filelib:wildcard(filename:join(Listings, "*"))), 416 ok = file:del_dir(Listings), 417 418 do_file_listings(DataDir,PrivDir,Files). 419 420listings_big(Config) when is_list(Config) -> 421 {Big,Target} = get_files(Config, big, listings_big), 422 TargetDir = filename:dirname(Target), 423 do_listing(Big, TargetDir, 'S'), 424 do_listing(Big, TargetDir, 'E'), 425 do_listing(Big, TargetDir, 'P'), 426 do_listing(Big, TargetDir, dkern, ".kernel"), 427 do_listing(Big, TargetDir, to_dis, ".dis"), 428 429 TargetNoext = filename:rootname(Target, code:objfile_extension()), 430 {ok,big} = compile:file(TargetNoext, [from_asm,{outdir,TargetDir}]), 431 432 %% Cleanup. 433 ok = file:delete(Target), 434 lists:foreach(fun(F) -> ok = file:delete(F) end, 435 filelib:wildcard(filename:join(TargetDir, "*"))), 436 ok = file:del_dir(TargetDir), 437 ok. 438 439other_output(Config) when is_list(Config) -> 440 {Simple,_Target} = get_files(Config, simple, "other_output"), 441 442 io:put_chars("to_pp"), 443 {ok,[],PP} = compile:file(Simple, [to_pp,binary,time]), 444 [] = [E || E <- PP, 445 begin 446 case element(1, E) of 447 attribute -> false; 448 function -> false; 449 eof -> false 450 end 451 end], 452 453 io:put_chars("to_exp (file)"), 454 {ok,[],Expand} = compile:file(Simple, [to_exp,binary,time]), 455 true = is_list(Expand), 456 {attribute,_,module,simple} = lists:keyfind(module, 3, Expand), 457 io:put_chars("to_exp (forms)"), 458 {ok,[],Expand} = compile:forms(PP, [to_exp,binary,time]), 459 460 io:put_chars("to_core (file)"), 461 {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), 462 c_module = element(1, Core), 463 {ok,_} = core_lint:module(Core), 464 io:put_chars("to_core (forms)"), 465 {ok,simple,Core} = compile:forms(PP, [to_core,binary,time]), 466 467 io:put_chars("to_kernel (file)"), 468 {ok,simple,Kernel} = compile:file(Simple, [to_kernel,binary,time]), 469 k_mdef = element(1, Kernel), 470 io:put_chars("to_kernel (forms)"), 471 {ok,simple,Kernel} = compile:forms(PP, [to_kernel,binary,time]), 472 473 io:put_chars("to_asm (file)"), 474 {ok,simple,Asm} = compile:file(Simple, [to_asm,binary,time]), 475 {simple,_,_,_,_} = Asm, 476 io:put_chars("to_asm (forms)"), 477 {ok,simple,Asm} = compile:forms(PP, [to_asm,binary,time]), 478 479 ok. 480 481%% Smoke test and cover of pretty-printing of Kernel code. 482kernel_listing(_Config) -> 483 TestBeams = get_unique_beam_files(), 484 Abstr = [begin {ok,{Mod,[{abstract_code, 485 {raw_abstract_v1,Abstr}}]}} = 486 beam_lib:chunks(Beam, [abstract_code]), 487 {Mod,Abstr} end || Beam <- TestBeams], 488 test_lib:p_run(fun(F) -> do_kernel_listing(F) end, Abstr). 489 490do_kernel_listing({M,A}) -> 491 try 492 {ok,M,Kern} = compile:forms(A, [to_kernel]), 493 IoList = v3_kernel_pp:format(Kern), 494 case unicode:characters_to_binary(IoList) of 495 Bin when is_binary(Bin) -> 496 ok 497 end 498 catch 499 throw:{error,Error} -> 500 io:format("*** compilation failure '~p' for module ~s\n", 501 [Error,M]), 502 error; 503 Class:Error:Stk -> 504 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 505 error 506 end. 507 508encrypted_abstr(Config) when is_list(Config) -> 509 {Simple,Target} = get_files(Config, simple, "encrypted_abstr"), 510 511 Res = case has_crypto() of 512 false -> 513 %% No crypto. 514 encrypted_abstr_no_crypto(Simple, Target), 515 {comment,"The crypto application is missing or broken"}; 516 true -> 517 %% Simulate not having crypto by removing 518 %% the crypto application from the path. 519 OldPath = code:get_path(), 520 try 521 NewPath = OldPath -- [filename:dirname(code:which(crypto))], 522 (catch crypto:stop()), 523 code:delete(crypto), 524 code:purge(crypto), 525 code:set_path(NewPath), 526 encrypted_abstr_no_crypto(Simple, Target) 527 after 528 code:set_path(OldPath) 529 end, 530 531 %% Now run the tests that require crypto. 532 encrypted_abstr_1(Simple, Target), 533 ok = file:delete(Target), 534 ok = file:del_dir(filename:dirname(Target)) 535 end, 536 537 %% Cleanup. 538 Res. 539 540encrypted_abstr_1(Simple, Target) -> 541 TargetDir = filename:dirname(Target), 542 Key = "ablurf123BX#$;3", 543 install_crypto_key(Key), 544 {ok,simple} = compile:file(Simple, 545 [debug_info,{debug_info_key,Key}, 546 {outdir,TargetDir}]), 547 verify_abstract(Target, erl_abstract_code), 548 549 {ok,simple} = compile:file(Simple, 550 [{debug_info_key,Key}, 551 {outdir,TargetDir}]), 552 verify_abstract(Target, erl_abstract_code), 553 554 {ok,simple} = compile:file(Simple, 555 [debug_info,{debug_info_key,{des3_cbc,Key}}, 556 {outdir,TargetDir}]), 557 verify_abstract(Target, erl_abstract_code), 558 559 {ok,simple} = compile:file(Simple, 560 [{debug_info,{?MODULE,ok}}, 561 {debug_info_key,Key}, 562 {outdir,TargetDir}]), 563 verify_abstract(Target, ?MODULE), 564 565 {ok,{simple,[{compile_info,CInfo}]}} = 566 beam_lib:chunks(Target, [compile_info]), 567 {_,Opts} = lists:keyfind(options, 1, CInfo), 568 {_,'********'} = lists:keyfind(debug_info_key, 1, Opts), 569 570 %% Try some illegal forms of crypto keys. 571 error = compile:file(Simple, 572 [debug_info,{debug_info_key,{blurf,"ss"}},report]), 573 error = compile:file(Simple, 574 [debug_info,{debug_info_key,{blurf,1,"ss"}},report]), 575 error = compile:file(Simple, 576 [debug_info,{debug_info_key,42},report]), 577 578 %% Place the crypto key in .erlang.crypt. 579 beam_lib:clear_crypto_key_fun(), 580 {ok,OldCwd} = file:get_cwd(), 581 ok = file:set_cwd(TargetDir), 582 583 error = compile:file(Simple, [encrypt_debug_info,report]), 584 585 NewKey = "better use another key here", 586 write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]), 587 {ok,simple} = compile:file(Simple, [encrypt_debug_info,report]), 588 verify_abstract("simple.beam", erl_abstract_code), 589 ok = file:delete(".erlang.crypt"), 590 beam_lib:clear_crypto_key_fun(), 591 {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = 592 beam_lib:chunks("simple.beam", [abstract_code]), 593 ok = file:set_cwd(OldCwd), 594 595 %% Test key compatibility by reading a BEAM file produced before 596 %% the update to the new crypto functions. 597 install_crypto_key("an old key"), 598 KeyCompat = filename:join(filename:dirname(Simple), 599 "key_compatibility"), 600 {ok,{key_compatibility,[Chunk]}} = beam_lib:chunks(KeyCompat, 601 [abstract_code]), 602 {abstract_code,{raw_abstract_v1,_}} = Chunk, 603 604 ok. 605 606 607write_crypt_file(Contents0) -> 608 Contents = list_to_binary([Contents0]), 609 io:format("~s\n", [binary_to_list(Contents)]), 610 ok = file:write_file(".erlang.crypt", Contents). 611 612encrypted_abstr_no_crypto(Simple, Target) -> 613 io:format("simpe: ~p~n", [Simple]), 614 TargetDir = filename:dirname(Target), 615 Key = "ablurf123BX#$;3", 616 error = compile:file(Simple, 617 [debug_info,{debug_info_key,Key}, 618 {outdir,TargetDir},report]), 619 ok. 620 621verify_abstract(Beam, Backend) -> 622 {ok,{simple,[Abst, Dbgi]}} = beam_lib:chunks(Beam, [abstract_code, debug_info]), 623 {abstract_code,{raw_abstract_v1,_}} = Abst, 624 {debug_info,{debug_info_v1,Backend,_}} = Dbgi. 625 626has_crypto() -> 627 try 628 crypto:start(), 629 crypto:stop(), 630 true 631 catch 632 error:_ -> false 633 end. 634 635install_crypto_key(Key) -> 636 F = fun (init) -> ok; 637 ({debug_info,des3_cbc,_,_}) -> Key; 638 (clear) -> ok 639 end, 640 ok = beam_lib:crypto_key_fun(F). 641 642%% Miscellanous tests, mainly to get better coverage. 643debug_info(erlang_v1, Module, ok, _Opts) -> 644 {ok, [Module]}; 645debug_info(erlang_v1, _Module, error, _Opts) -> 646 {error, unknown_format}. 647 648custom_debug_info(Config) when is_list(Config) -> 649 {Simple,_} = get_files(Config, simple, "file_1"), 650 651 {ok,simple,OkBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,ok}}]), %Coverage 652 {ok,{simple,[{abstract_code,{raw_abstract_v1,[simple]}}]}} = 653 beam_lib:chunks(OkBin, [abstract_code]), 654 {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,ok}}]}} = 655 beam_lib:chunks(OkBin, [debug_info]), 656 657 {ok,simple,ErrorBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,error}}]), %Coverage 658 {ok,{simple,[{abstract_code,no_abstract_code}]}} = 659 beam_lib:chunks(ErrorBin, [abstract_code]), 660 {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,error}}]}} = 661 beam_lib:chunks(ErrorBin, [debug_info]). 662 663custom_compile_info(Config) when is_list(Config) -> 664 Anno = erl_anno:new(1), 665 Forms = [{attribute,Anno,module,custom_compile_info}], 666 Opts = [binary,{compile_info,[{another,version}]}], 667 668 {ok,custom_compile_info,Bin} = compile:forms(Forms, Opts), 669 {ok,{custom_compile_info,[{compile_info,CompileInfo}]}} = 670 beam_lib:chunks(Bin, [compile_info]), 671 version = proplists:get_value(another, CompileInfo), 672 CompileOpts = proplists:get_value(options, CompileInfo), 673 undefined = proplists:get_value(compile_info, CompileOpts), 674 675 {ok,custom_compile_info,DetBin} = compile:forms(Forms, [deterministic|Opts]), 676 {ok,{custom_compile_info,[{compile_info,DetInfo}]}} = 677 beam_lib:chunks(DetBin, [compile_info]), 678 version = proplists:get_value(another, DetInfo). 679 680cover(Config) when is_list(Config) -> 681 io:format("~p\n", [compile:options()]), 682 ok. 683 684do_listing(Source, TargetDir, Type) -> 685 do_listing(Source, TargetDir, Type, "." ++ atom_to_list(Type)). 686 687do_listing(Source, TargetDir, Type, Ext) -> 688 io:format("Source: ~p TargetDir: ~p\n Type: ~p Ext: ~p\n", 689 [Source, TargetDir, Type, Ext]), 690 case compile:file(Source, [Type, time, {outdir, TargetDir}]) of 691 {ok, _} -> ok; 692 Other -> ct:fail({unexpected_result, Other}) 693 end, 694 SourceBase = filename:rootname(filename:basename(Source)), 695 696 Target = filename:join(TargetDir, SourceBase ++ Ext), 697 true = exists(Target). 698 699get_files(Config, Module, OutputName) -> 700 code:delete(Module), 701 code:purge(Module), 702 DataDir = proplists:get_value(data_dir, Config), 703 PrivDir = proplists:get_value(priv_dir, Config), 704 Src = filename:join(DataDir, atom_to_list(Module)), 705 TargetDir = filename:join(PrivDir, OutputName), 706 ok = file:make_dir(TargetDir), 707 File = atom_to_list(Module) ++ code:objfile_extension(), 708 Target = filename:join(TargetDir, File), 709 {Src, Target}. 710 711run(Target, Func, Args) -> 712 Module = list_to_atom(filename:rootname(filename:basename(Target))), 713 {module, Module} = code:load_abs(filename:rootname(Target)), 714 Result = (catch apply(Module, Func, Args)), 715 true = code:delete(Module), 716 false = code:purge(Module), 717 Result. 718 719exists(Name) -> 720 case file:read_file_info(Name) of 721 {ok, _} -> true; 722 {error, _} -> false 723 end. 724 725 726strict_record(Config) when is_list(Config) -> 727 Priv = proplists:get_value(priv_dir, Config), 728 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 729 Opts = [{outdir,Priv},report_errors], 730 M = record_access, 731 732 {ok,M} = c:c(M, [strict_record_tests|Opts]), 733 Turtle = test_strict(), 734 735 {ok,M} = c:c(M, [no_strict_record_tests|Opts]), 736 Turtle = test_sloppy(), 737 738 %% The option first given wins. 739 {ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]), 740 Turtle = test_sloppy(), 741 {ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]), 742 Turtle = test_strict(), 743 744 %% Default (possibly influenced by ERL_COMPILER_OPTIONS). 745 {ok,M} = c:c(M, [{outdir,Priv},report_errors]), 746 try 747 {1,2} = record_access:test(Turtle), 748 {comment,"Default: no_strict_record_tests"} 749 catch 750 error:{badrecord,tortoise} -> 751 {comment,"Default: strict_record_tests"} 752 end. 753 754test_strict() -> 755 Turtle = record_access:turtle(), 756 try 757 record_access:test(Turtle) 758 catch 759 error:{badrecord,tortoise} -> 760 ok 761 end, 762 Turtle. 763 764test_sloppy() -> 765 Turtle = record_access:turtle(), 766 {1,2} = record_access:test(Turtle), 767 Turtle. 768 769utf8_atoms(Config) when is_list(Config) -> 770 Anno = erl_anno:new(1), 771 Atom = binary_to_atom(<<"こんにちは"/utf8>>, utf8), 772 Forms = [{attribute,Anno,compile,[export_all]}, 773 {function,Anno,atom,0,[{clause,Anno,[],[],[{atom,Anno,Atom}]}]}], 774 775 Utf8AtomForms = [{attribute,Anno,module,utf8_atom}|Forms], 776 {ok,utf8_atom,Utf8AtomBin} = 777 compile:forms(Utf8AtomForms, [binary]), 778 {ok,{utf8_atom,[{atoms,_}]}} = 779 beam_lib:chunks(Utf8AtomBin, [atoms]), 780 code:load_binary(utf8_atom, "compile_SUITE", Utf8AtomBin), 781 Atom = utf8_atom:atom(), 782 783 NoUtf8AtomForms = [{attribute,Anno,module,no_utf8_atom}|Forms], 784 error = compile:forms(NoUtf8AtomForms, [binary, r19]). 785 786utf8_functions(Config) when is_list(Config) -> 787 Anno = erl_anno:new(1), 788 Atom = binary_to_atom(<<"こんにちは"/utf8>>, utf8), 789 Forms = [{attribute,Anno,compile,[export_all]}, 790 {function,Anno,Atom,0,[{clause,Anno,[],[],[{atom,Anno,world}]}]}], 791 792 Utf8FunctionForms = [{attribute,Anno,module,utf8_function}|Forms], 793 {ok,utf8_function,Utf8FunctionBin} = 794 compile:forms(Utf8FunctionForms, [binary]), 795 {ok,{utf8_function,[{atoms,_}]}} = 796 beam_lib:chunks(Utf8FunctionBin, [atoms]), 797 code:load_binary(utf8_function, "compile_SUITE", Utf8FunctionBin), 798 world = utf8_function:Atom(), 799 800 NoUtf8FunctionForms = [{attribute,Anno,module,no_utf8_function}|Forms], 801 error = compile:forms(NoUtf8FunctionForms, [binary, r19]). 802 803extra_chunks(Config) when is_list(Config) -> 804 Anno = erl_anno:new(1), 805 Forms = [{attribute,Anno,module,extra_chunks}], 806 807 {ok,extra_chunks,ExtraChunksBinary} = 808 compile:forms(Forms, [binary, {extra_chunks, [{<<"ExCh">>, <<"Contents">>}]}]), 809 {ok,{extra_chunks,[{"ExCh",<<"Contents">>}]}} = 810 beam_lib:chunks(ExtraChunksBinary, ["ExCh"]). 811 812tuple_calls(Config) when is_list(Config) -> 813 Anno = erl_anno:new(1), 814 Forms = [{attribute,Anno,export,[{size,1},{store,1}]}, 815 {function,Anno,size,1, 816 [{clause,Anno,[{var,[],mod}],[], 817 [{call,[],{remote,[],{var,[],mod},{atom,[],size}},[]}]}]}, 818 {function,Anno,store,1, 819 [{clause,Anno,[{var,[],mod}],[], 820 [{call,[],{remote,[],{var,[],mod},{atom,[],store}},[{atom,[],key},{atom,[],value}]}]}]}], 821 822 TupleCallsFalse = [{attribute,Anno,module,tuple_calls_false}|Forms], 823 {ok,_,TupleCallsFalseBinary} = compile:forms(TupleCallsFalse, [binary]), 824 code:load_binary(tuple_calls_false, "compile_SUITE.erl", TupleCallsFalseBinary), 825 {'EXIT',{badarg,_}} = (catch tuple_calls_false:store(dict())), 826 {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(dict())), 827 {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(empty_tuple())), 828 829 TupleCallsTrue = [{attribute,Anno,module,tuple_calls_true}|Forms], 830 {ok,_,TupleCallsTrueBinary} = compile:forms(TupleCallsTrue, [binary,tuple_calls]), 831 code:load_binary(tuple_calls_true, "compile_SUITE.erl", TupleCallsTrueBinary), 832 Dict = tuple_calls_true:store(dict()), 833 1 = tuple_calls_true:size(Dict), 834 {'EXIT',{badarg,_}} = (catch tuple_calls_true:size(empty_tuple())), 835 836 ok. 837 838dict() -> 839 dict:new(). 840empty_tuple() -> 841 {}. 842 843env(Config) when is_list(Config) -> 844 {Simple,Target} = get_files(Config, simple, env), 845 {ok,Cwd} = file:get_cwd(), 846 ok = file:set_cwd(filename:dirname(Target)), 847 848 true = os:putenv("ERL_COMPILER_OPTIONS", "binary"), 849 try 850 env_1(Simple, Target) 851 after 852 true = os:putenv("ERL_COMPILER_OPTIONS", "ignore_me"), 853 file:set_cwd(Cwd), 854 file:delete(Target), 855 file:del_dir(filename:dirname(Target)) 856 end, 857 ok. 858 859env_1(Simple, Target) -> 860 %% file 861 {ok,simple,<<_/binary>>} = compile:file(Simple), 862 {ok,simple} = compile:noenv_file(Simple, [debug_info]), 863 true = exists(Target), 864 {ok,{simple,[{abstract_code,Abstr0}]}} = 865 beam_lib:chunks(Target, [abstract_code]), 866 {raw_abstract_v1,Forms} = Abstr0, 867 868 %% forms 869 true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"), 870 {ok,simple} = compile:forms(Forms), 871 {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, []), 872 873 %% output_generated 874 false = compile:output_generated([]), 875 true = compile:noenv_output_generated([]), 876 877 ok = file:delete(Target), 878 879 ok. 880 881%% Test pretty-printing in Core Erlang format and then try to 882%% compile the generated Core Erlang files. 883 884core_pp(Config) when is_list(Config) -> 885 PrivDir = proplists:get_value(priv_dir, Config), 886 Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), 887 ok = file:make_dir(Outdir), 888 889 TestBeams = get_unique_beam_files(), 890 Abstr = [begin {ok,{Mod,[{abstract_code, 891 {raw_abstract_v1,Abstr}}]}} = 892 beam_lib:chunks(Beam, [abstract_code]), 893 {Mod,Abstr} end || Beam <- TestBeams], 894 test_lib:p_run(fun(F) -> do_core_pp(F, Outdir) end, Abstr). 895 896do_core_pp({M,A}, Outdir) -> 897 try 898 do_core_pp_1(M, A, Outdir) 899 catch 900 throw:{error,Error} -> 901 io:format("*** compilation failure '~p' for module ~s\n", 902 [Error,M]), 903 error; 904 Class:Error:Stk -> 905 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 906 error 907 end. 908 909do_core_pp_1(M, A, Outdir) -> 910 {ok,M,Core0} = compile:forms(A, [to_core]), 911 CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), 912 CorePP = core_pp:format(Core0), 913 ok = file:write_file(CoreFile, unicode:characters_to_binary(CorePP)), 914 915 %% Parse the .core file and return the result as Core Erlang Terms. 916 Core = case compile:file(CoreFile, [report_errors,from_core,no_copt,to_core,binary]) of 917 {ok,M,Core1} -> Core1; 918 Other -> throw({error,Other}) 919 end, 920 ok = file:delete(CoreFile), 921 922 %% Compile as usual (including optimizations). 923 compile_forms(M, Core, [clint,from_core,binary]), 924 925 %% Don't optimize to test that we are not dependent 926 %% on the Core Erlang optmimization passes. 927 %% (Example of a previous bug: The core_parse pass 928 %% would not turn map literals into #c_literal{} 929 %% records; if sys_core_fold was run it would fix 930 %% that; if sys_core_fold was not run v3_kernel would 931 %% crash.) 932 compile_forms(M, Core, [clint,from_core,no_copt,binary]), 933 934 ok. 935 936compile_forms(Mod, Forms, Opts) -> 937 case compile:forms(Forms, [report_errors|Opts]) of 938 {ok,Mod,_} -> ok; 939 Other -> throw({error,Other}) 940 end. 941 942%% Pretty-print core and read it back. Should be identical. 943 944core_roundtrip(Config) -> 945 PrivDir = proplists:get_value(priv_dir, Config), 946 Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), 947 ok = file:make_dir(Outdir), 948 949 TestBeams = get_unique_beam_files(), 950 test_lib:p_run(fun(F) -> do_core_roundtrip(F, Outdir) end, TestBeams). 951 952do_core_roundtrip(Beam, Outdir) -> 953 try 954 {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} = 955 beam_lib:chunks(Beam, [abstract_code]), 956 do_core_roundtrip_1(Mod, Abstr, Outdir) 957 catch 958 throw:{error,Error} -> 959 io:format("*** compilation failure '~p' for file ~s\n", 960 [Error,Beam]), 961 error; 962 Class:Error:Stk -> 963 io:format("~p: ~p ~p\n~p\n", [Beam,Class,Error,Stk]), 964 error 965 end. 966 967do_core_roundtrip_1(Mod, Abstr, Outdir) -> 968 {ok,Mod,Core0} = compile:forms(Abstr, [to_core0]), 969 do_core_roundtrip_2(Mod, Core0, Outdir), 970 971 %% Primarily, test that annotations are accepted for all 972 %% constructs. Secondarily, smoke test cerl_trees:label/1. 973 {Core1,_} = cerl_trees:label(Core0), 974 do_core_roundtrip_2(Mod, Core1, Outdir), 975 976 %% Run the inliner to force generation of variables 977 %% with numeric names. 978 {ok,Mod,Core2} = compile:forms(Abstr, [inline,to_core]), 979 do_core_roundtrip_2(Mod, Core2, Outdir). 980 981do_core_roundtrip_2(M, Core0, Outdir) -> 982 CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), 983 CorePP = core_pp:format_all(Core0), 984 ok = file:write_file(CoreFile, unicode:characters_to_binary(CorePP)), 985 986 %% Parse the .core file and return the result as Core Erlang Terms. 987 Core2 = case compile:file(CoreFile, [report_errors,from_core, 988 no_copt,to_core,binary]) of 989 {ok,M,Core1} -> Core1; 990 Other -> throw({error,Other}) 991 end, 992 Core = undo_var_translation(Core2), 993 ok = file:delete(CoreFile), 994 995 case cmp_core(Core0, Core, M) of 996 true -> ok; 997 false -> error 998 end. 999 1000undo_var_translation(Tree) -> 1001 F = fun(Node) -> 1002 case cerl:is_c_var(Node) of 1003 true -> 1004 Name0 = cerl:var_name(Node), 1005 try atom_to_list(Name0) of 1006 "_X"++Name -> 1007 cerl:update_c_var(Node, list_to_atom(Name)); 1008 "_"++Name -> 1009 cerl:update_c_var(Node, list_to_atom(Name)); 1010 _ -> 1011 Node 1012 catch 1013 error:badarg -> 1014 Node 1015 1016 end; 1017 false -> 1018 Node 1019 end 1020 end, 1021 cerl_trees:map(F, Tree). 1022 1023cmp_core(E, E, _Mod) -> 1024 true; 1025cmp_core(M1, M2, Mod) -> 1026 cmp_core_fs(cerl:module_defs(M1), cerl:module_defs(M2), Mod). 1027 1028cmp_core_fs([F1|T1], [F2|T2], Mod) -> 1029 cmp_core_f(F1, F2, Mod) andalso cmp_core_fs(T1, T2, Mod); 1030cmp_core_fs([], [], _Mod) -> 1031 true; 1032cmp_core_fs(_, _, _Mod) -> 1033 false. 1034 1035cmp_core_f(E, E, _Mod) -> 1036 true; 1037cmp_core_f({Name,F1}, {Name,F2}, Mod) -> 1038 case diff(F1, F2) of 1039 F1 -> 1040 true; 1041 Diff -> 1042 io:format("~p ~p:\n~p\n", [Mod,Name,Diff]), 1043 false 1044 end. 1045 1046diff(E, E) -> 1047 E; 1048diff([H1|T1], [H2|T2]) -> 1049 [diff(H1, H2)|diff(T1, T2)]; 1050diff(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> 1051 case cerl:is_c_var(T1) andalso cerl:is_c_var(T2) of 1052 true -> 1053 diff_var(T1, T2); 1054 false -> 1055 case cerl:is_c_map(T1) andalso cerl:is_c_map(T2) of 1056 true -> 1057 diff_map(T1, T2); 1058 false -> 1059 diff_tuple(T1, T2) 1060 end 1061 end; 1062diff(E1, E2) -> 1063 {'DIFF',E1,E2}. 1064 1065diff_var(V1, V2) -> 1066 case {cerl:var_name(V1),cerl:var_name(V2)} of 1067 {Same,Same} -> 1068 V1; 1069 {Name1,Name2} -> 1070 %% The inliner uses integers as variable names. Such integers 1071 %% are read back as atoms. 1072 case is_integer(Name1) andalso 1073 list_to_atom(integer_to_list(Name1)) =:= Name2 of 1074 true -> 1075 V1; 1076 _ -> 1077 cerl:update_c_var(V1, {'DIFF',Name1,Name2}) 1078 end 1079 end. 1080 1081%% Annotations for maps are not preserved exactly, but that is not 1082%% a real problem. Workaround by not comparing all annotations when 1083%% comparing maps. 1084 1085diff_map(M, M) -> 1086 M; 1087diff_map(M1, M2) -> 1088 case cerl:get_ann(M1) =:= cerl:get_ann(M2) of 1089 false -> 1090 diff_tuple(M1, M2); 1091 true -> 1092 case remove_compiler_gen(M1) =:= remove_compiler_gen(M2) of 1093 true -> 1094 M1; 1095 false -> 1096 diff_tuple(M1, M2) 1097 end 1098 end. 1099 1100diff_tuple(T1, T2) -> 1101 L = diff(tuple_to_list(T1), tuple_to_list(T2)), 1102 list_to_tuple(L). 1103 1104remove_compiler_gen(M) -> 1105 Arg0 = cerl:map_arg(M), 1106 Arg = cerl:set_ann(Arg0, []), 1107 Es0 = cerl:map_es(M), 1108 Es = [remove_compiler_gen_1(Pair) || Pair <- Es0], 1109 cerl:update_c_map(M, Arg, Es). 1110 1111remove_compiler_gen_1(Pair) -> 1112 Op0 = cerl:map_pair_op(Pair), 1113 Op = cerl:set_ann(Op0, []), 1114 K = map_var(cerl:map_pair_key(Pair)), 1115 V = map_var(cerl:map_pair_val(Pair)), 1116 cerl:update_c_map_pair(Pair, Op, K, V). 1117 1118map_var(Var) -> 1119 case cerl:is_c_var(Var) of 1120 true -> 1121 case cerl:var_name(Var) of 1122 Name when is_atom(Name) -> 1123 L = atom_to_list(Name), 1124 try list_to_integer(L) of 1125 Int -> 1126 cerl:update_c_var(Var, Int) 1127 catch 1128 error:_ -> 1129 Var 1130 end; 1131 _ -> 1132 Var 1133 end; 1134 false -> 1135 Var 1136 end. 1137 1138%% Compile to Beam assembly language (.S) and then try to 1139%% run .S through the compiler again. 1140 1141asm(Config) when is_list(Config) -> 1142 PrivDir = proplists:get_value(priv_dir, Config), 1143 Outdir = filename:join(PrivDir, "asm"), 1144 ok = file:make_dir(Outdir), 1145 1146 TestBeams = get_unique_beam_files(), 1147 Res = test_lib:p_run(fun(F) -> do_asm(F, Outdir) end, TestBeams), 1148 Res. 1149 1150 1151do_asm(Beam, Outdir) -> 1152 {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} = 1153 beam_lib:chunks(Beam, [abstract_code]), 1154 try 1155 {ok,M,Asm} = compile:forms(A, ['S']), 1156 AsmFile = filename:join(Outdir, atom_to_list(M)++".S"), 1157 {ok,Fd} = file:open(AsmFile, [write,{encoding,utf8}]), 1158 beam_listing:module(Fd, Asm), 1159 ok = file:close(Fd), 1160 case compile:file(AsmFile, [from_asm,binary,report]) of 1161 {ok,M,_} -> 1162 ok = file:delete(AsmFile); 1163 Other -> 1164 io:format("*** failure '~p' for ~s\n", 1165 [Other,AsmFile]), 1166 error 1167 end 1168 catch Class:Error:Stk -> 1169 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 1170 error 1171 end. 1172 1173%% Make sure that guards are fully optimized. Guards should 1174%% should use 'test' instructions, not 'bif' instructions. 1175 1176optimized_guards(_Config) -> 1177 TestBeams = get_unique_beam_files(), 1178 test_lib:p_run(fun(F) -> do_opt_guards(F) end, TestBeams). 1179 1180do_opt_guards(Beam) -> 1181 {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} = 1182 beam_lib:chunks(Beam, [abstract_code]), 1183 try 1184 {ok,M,Asm} = compile:forms(A, ['S']), 1185 do_opt_guards_mod(Asm) 1186 catch Class:Error:Stk -> 1187 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 1188 error 1189 end. 1190 1191do_opt_guards_mod({Mod,_Exp,_Attr,Asm,_NumLabels}) -> 1192 case do_opt_guards_fs(Mod, Asm) of 1193 [] -> 1194 ok; 1195 [_|_]=Bifs -> 1196 io:format("ERRORS FOR ~p:\n~p\n", [Mod,Bifs]), 1197 error 1198 end. 1199 1200do_opt_guards_fs(Mod, [{function,Name,Arity,_,Is}|Fs]) -> 1201 Bifs0 = do_opt_guards_fun(Is), 1202 1203 %% The compiler does not attempt to optimize 'xor'. 1204 %% Therefore, ignore all functions that use 'xor' in 1205 %% a guard. 1206 Bifs = case lists:any(fun({bif,'xor',_,_,_}) -> true; 1207 (_) -> false 1208 end, Bifs0) of 1209 true -> []; 1210 false -> Bifs0 1211 end, 1212 1213 %% Filter out the allowed exceptions. 1214 FA = {Name,Arity}, 1215 case {Bifs,is_exception(Mod, FA)} of 1216 {[_|_],true} -> 1217 io:format("~p:~p/~p IGNORED:\n~p\n", 1218 [Mod,Name,Arity,Bifs]), 1219 do_opt_guards_fs(Mod, Fs); 1220 {[_|_],false} -> 1221 [{FA,Bifs}|do_opt_guards_fs(Mod, Fs)]; 1222 {[],false} -> 1223 do_opt_guards_fs(Mod, Fs); 1224 {[],true} -> 1225 io:format("Redundant exception for ~p:~p/~p\n", 1226 [Mod,Name,Arity]), 1227 error(redundant) 1228 end; 1229do_opt_guards_fs(_, []) -> []. 1230 1231do_opt_guards_fun([{bif,Name,{f,F},As,_}=I|Is]) when F =/= 0 -> 1232 Arity = length(As), 1233 case erl_internal:comp_op(Name, Arity) orelse 1234 erl_internal:bool_op(Name, Arity) orelse 1235 erl_internal:new_type_test(Name, Arity) of 1236 true -> 1237 [I|do_opt_guards_fun(Is)]; 1238 false -> 1239 do_opt_guards_fun(Is) 1240 end; 1241do_opt_guards_fun([_|Is]) -> 1242 do_opt_guards_fun(Is); 1243do_opt_guards_fun([]) -> []. 1244 1245is_exception(bs_match_SUITE, {matching_and_andalso_2,2}) -> true; 1246is_exception(bs_match_SUITE, {matching_and_andalso_3,2}) -> true; 1247is_exception(guard_SUITE, {'-complex_not/1-fun-4-',1}) -> true; 1248is_exception(guard_SUITE, {'-complex_not/1-fun-5-',1}) -> true; 1249is_exception(guard_SUITE, {basic_andalso_orelse,1}) -> true; 1250is_exception(guard_SUITE, {bad_guards,1}) -> true; 1251is_exception(guard_SUITE, {bad_guards_2,2}) -> true; 1252is_exception(guard_SUITE, {bad_guards_3,2}) -> true; 1253is_exception(guard_SUITE, {cqlc,4}) -> true; 1254is_exception(guard_SUITE, {csemi7,3}) -> true; 1255is_exception(guard_SUITE, {misc,1}) -> true; 1256is_exception(guard_SUITE, {nested_not_2b,4}) -> true; 1257is_exception(guard_SUITE, {tricky_1,2}) -> true; 1258is_exception(map_SUITE, {map_guard_update,2}) -> true; 1259is_exception(map_SUITE, {map_guard_update_variables,3}) -> true; 1260is_exception(_, _) -> false. 1261 1262sys_pre_attributes(Config) -> 1263 DataDir = proplists:get_value(data_dir, Config), 1264 File = filename:join(DataDir, "attributes.erl"), 1265 Mod = attributes, 1266 CommonOpts = [binary,report,verbose, 1267 {parse_transform,sys_pre_attributes}], 1268 PreOpts = [{attribute,delete,deleted}], 1269 PostOpts = [{attribute,insert,inserted,"value"}], 1270 PrePostOpts = [{attribute,replace,replaced,42}, 1271 {attribute,replace,replace_nonexisting,new}], 1272 {ok,Mod,Code} = compile:file(File, PrePostOpts ++ PreOpts ++ 1273 PostOpts ++ CommonOpts), 1274 code:load_binary(Mod, File, Code), 1275 Attr = Mod:module_info(attributes), 1276 io:format("~p", [Attr]), 1277 {inserted,"value"} = lists:keyfind(inserted, 1, Attr), 1278 {replaced,[42]} = lists:keyfind(replaced, 1, Attr), 1279 {replace_nonexisting,[new]} = lists:keyfind(replace_nonexisting, 1, Attr), 1280 false = lists:keymember(deleted, 1, Attr), 1281 1282 %% Cover more code. 1283 {ok,Mod,_} = compile:file(File, PostOpts ++ CommonOpts), 1284 {ok,Mod,_} = compile:file(File, CommonOpts -- [verbose]), 1285 {ok,Mod,_} = compile:file(File, PreOpts ++ CommonOpts), 1286 {ok,Mod,_} = compile:file(File, 1287 [{attribute,replace,replaced,42}|CommonOpts]), 1288 {ok,Mod,_} = compile:file(File, PrePostOpts ++ PreOpts ++ 1289 PostOpts ++ CommonOpts -- 1290 [report,verbose]), 1291 ok. 1292 1293%% Test the dialyzer option to cover more code. 1294dialyzer(Config) -> 1295 Priv = proplists:get_value(priv_dir, Config), 1296 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 1297 Opts = [{outdir,Priv},report_errors], 1298 M = dialyzer_test, 1299 {ok,M} = c:c(M, [dialyzer|Opts]), 1300 [{a,b,c}] = M:M(), 1301 1302 %% Cover huge line numbers without the 'dialyzer' option. 1303 {ok,M} = c:c(M, Opts), 1304 [{a,b,c}] = M:M(), 1305 ok. 1306 1307 1308%% Test that warnings contain filenames and line numbers. 1309warnings(_Config) -> 1310 Files = get_unique_files(".erl"), 1311 test_lib:p_run(fun do_warnings/1, Files). 1312 1313do_warnings(F) -> 1314 {ok,_,_,Ws} = compile:file(F, [binary,bin_opt_info,return]), 1315 do_warnings_1(Ws, F). 1316 1317do_warnings_1([{"no_file",Ws}|_], F) -> 1318 io:format("~s:\nMissing file for warnings: ~p\n", 1319 [F,Ws]), 1320 error; 1321do_warnings_1([{Name,Ws}|T], F) -> 1322 case filename:extension(Name) of 1323 ".erl" -> 1324 do_warnings_2(Ws, T, F); 1325 _ -> 1326 io:format("~s:\nNo .erl extension\n", [F]), 1327 error 1328 end; 1329do_warnings_1([], _) -> ok. 1330 1331do_warnings_2([{Int,_,_}=W|T], Next, F) -> 1332 if 1333 is_integer(Int) -> 1334 do_warnings_2(T, Next, F); 1335 true -> 1336 io:format("~s:\nMissing line number: ~p\n", 1337 [F,W]), 1338 error 1339 end; 1340do_warnings_2([], Next, F) -> 1341 do_warnings_1(Next, F). 1342 1343 1344%% Test that the compile:pre_load/0 function (used by 'erlc') 1345%% pre-loads the modules that are used by a typical compilation. 1346 1347pre_load_check(Config) -> 1348 case {test_server:is_cover(),code:module_info(native)} of 1349 {true,_} -> 1350 {skip,"Cover is running"}; 1351 {false,true} -> 1352 %% Tracing won't work. 1353 {skip,"'code' is native-compiled"}; 1354 {false,false} -> 1355 try 1356 do_pre_load_check(Config) 1357 after 1358 dbg:stop_clear() 1359 end 1360 end. 1361 1362do_pre_load_check(Config) -> 1363 DataDir = ?config(data_dir, Config), 1364 Simple = filename:join(DataDir, "simple.erl"), 1365 Big = filename:join(DataDir, "big.erl"), 1366 {ok,_} = dbg:tracer(process, {fun pre_load_trace/2,[]}), 1367 dbg:p(self(), call), 1368 dbg:p(new, call), 1369 {ok,_} = dbg:tpl({?MODULE,get_trace_data,0}, []), 1370 {ok,_} = dbg:tp({code,ensure_modules_loaded,1}, []), 1371 1372 %% Compile a simple module using the erl_compile interface 1373 %% to find out the modules that are pre-loaded by 1374 %% compile:pre_load/0. 1375 Opts = #options{specific=[binary]}, 1376 {ok,simple,_} = compile:compile(Simple, "", Opts), 1377 [{code,ensure_modules_loaded,[PreLoaded0]}] = get_trace_data(), 1378 PreLoaded1 = ordsets:from_list(PreLoaded0), 1379 1380 %% Since 'compile' is the function doing the pre-loaded, 1381 %% it is useless to include it in the list. 1382 case ordsets:is_element(compile, PreLoaded1) of 1383 true -> 1384 io:put_chars("The 'compile' module should not be included " 1385 "in the list of modules to be pre-loaded."), 1386 ?t:fail(compile); 1387 false -> 1388 [] 1389 end, 1390 PreLoaded = ordsets:add_element(compile, PreLoaded1), 1391 1392 %% Now unload all pre-loaded modules and all modules in 1393 %% compiler application. Then compile a module to find 1394 %% which modules that get loaded. 1395 CompilerMods = compiler_modules(), 1396 Unload = ordsets:union(ordsets:from_list(CompilerMods), PreLoaded), 1397 _ = [begin 1398 code:delete(M), 1399 code:purge(M) 1400 end || M <- Unload], 1401 1402 {ok,_} = dbg:ctp({code,ensure_modules_loaded,1}), 1403 {ok,_} = dbg:tp({code,ensure_loaded,1}, []), 1404 {ok,big,_} = compile:file(Big, [binary]), 1405 WasLoaded0 = get_trace_data(), 1406 WasLoaded1 = [M || {code,ensure_loaded,[M]} <- WasLoaded0], 1407 WasLoaded = ordsets:from_list(WasLoaded1), 1408 1409 %% Check for modules that should have been pre-loaded. 1410 case ordsets:subtract(WasLoaded, PreLoaded) of 1411 [] -> 1412 ok; 1413 [_|_]=NotPreLoaded -> 1414 io:format("The following modules were used " 1415 "but not pre-loaded:\n~p\n", 1416 [NotPreLoaded]), 1417 ?t:fail({not_preload,NotPreLoaded}) 1418 end, 1419 1420 %% Check for modules that should not be pre-loaded. 1421 case ordsets:subtract(PreLoaded, WasLoaded) of 1422 [] -> 1423 ok; 1424 [_|_]=NotUsed -> 1425 io:format("The following modules were pre-loaded" 1426 " but not used:\n~p\n", 1427 [NotUsed]), 1428 ?t:fail({not_used,NotUsed}) 1429 end, 1430 1431 ok. 1432 1433get_trace_data() -> 1434 %% Apparantely, doing a receive at the beginning of 1435 %% a traced function can cause extra trace messages. 1436 %% To avoid that, don't do the receive in this function. 1437 do_get_trace_data(). 1438 1439do_get_trace_data() -> 1440 receive 1441 {trace_data,Data} -> Data 1442 end. 1443 1444pre_load_trace({trace,Pid,call,{?MODULE,get_trace_data,[]}}, Acc) -> 1445 Pid ! {trace_data,Acc}, 1446 []; 1447pre_load_trace({trace,_,call,MFA}, Acc) -> 1448 [MFA|Acc]. 1449 1450compiler_modules() -> 1451 Wc = filename:join([code:lib_dir(compiler),"ebin","*.beam"]), 1452 Ms = filelib:wildcard(Wc), 1453 FN = filename, 1454 [list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms]. 1455 1456%% Test that ERL_COMPILER_OPTIONS are correctly retrieved 1457%% by env_compiler_options/0 1458 1459env_compiler_options(_Config) -> 1460 Cases = [ 1461 {"bin_opt_info", [bin_opt_info]}, 1462 {"'S'", ['S']}, 1463 {"{source, \"test.erl\"}", [{source, "test.erl"}]}, 1464 {"[{d,macro_one,1},{d,macro_two}]", [{d, macro_one, 1}, {d, macro_two}]}, 1465 {"[warn_export_all, warn_export_vars]", [warn_export_all, warn_export_vars]} 1466 ], 1467 F = fun({Env, Expected}) -> 1468 true = os:putenv("ERL_COMPILER_OPTIONS", Env), 1469 Expected = compile:env_compiler_options() 1470 end, 1471 lists:foreach(F, Cases). 1472 1473%% Test options for compatibility with previous major versions of OTP. 1474 1475bc_options(Config) -> 1476 DataDir = proplists:get_value(data_dir, Config), 1477 1478 101 = highest_opcode(DataDir, small_float, [no_get_hd_tl,no_line_info]), 1479 1480 103 = highest_opcode(DataDir, big, 1481 [no_get_hd_tl,no_record_opt, 1482 no_line_info,no_stack_trimming]), 1483 1484 125 = highest_opcode(DataDir, small_float, 1485 [no_get_hd_tl,no_line_info,no_float_opt]), 1486 1487 132 = highest_opcode(DataDir, small, 1488 [no_get_hd_tl,no_record_opt,no_float_opt,no_line_info]), 1489 1490 136 = highest_opcode(DataDir, big, [no_get_hd_tl,no_record_opt,no_line_info]), 1491 1492 153 = highest_opcode(DataDir, big, [no_get_hd_tl,no_record_opt]), 1493 153 = highest_opcode(DataDir, big, [r16]), 1494 153 = highest_opcode(DataDir, big, [r17]), 1495 153 = highest_opcode(DataDir, big, [r18]), 1496 153 = highest_opcode(DataDir, big, [r19]), 1497 153 = highest_opcode(DataDir, small_float, [r16]), 1498 153 = highest_opcode(DataDir, small_float, []), 1499 1500 158 = highest_opcode(DataDir, small_maps, [r17]), 1501 158 = highest_opcode(DataDir, small_maps, [r18]), 1502 158 = highest_opcode(DataDir, small_maps, [r19]), 1503 158 = highest_opcode(DataDir, small_maps, [r20]), 1504 158 = highest_opcode(DataDir, small_maps, []), 1505 1506 163 = highest_opcode(DataDir, big, []), 1507 1508 ok. 1509 1510highest_opcode(DataDir, Mod, Opt) -> 1511 Src = filename:join(DataDir, atom_to_list(Mod)++".erl"), 1512 {ok,Mod,Beam} = compile:file(Src, [binary|Opt]), 1513 {ok,{Mod,[{"Code",Code}]}} = beam_lib:chunks(Beam, ["Code"]), 1514 <<16:32,0:32,HighestOpcode:32,_/binary>> = Code, 1515 HighestOpcode. 1516 1517deterministic_include(Config) when is_list(Config) -> 1518 DataDir = proplists:get_value(data_dir, Config), 1519 Simple = filename:join(DataDir, "simple"), 1520 1521 %% Files without +deterministic should differ if their include paths do, 1522 %% as their debug info will be different. 1523 {ok,_,NonDetA} = compile:file(Simple, [binary, {i,"gurka"}]), 1524 {ok,_,NonDetB} = compile:file(Simple, [binary, {i,"gaffel"}]), 1525 true = NonDetA =/= NonDetB, 1526 1527 %% ... but files with +deterministic shouldn't. 1528 {ok,_,DetC} = compile:file(Simple, [binary, deterministic, {i,"gurka"}]), 1529 {ok,_,DetD} = compile:file(Simple, [binary, deterministic, {i,"gaffel"}]), 1530 true = DetC =:= DetD, 1531 1532 ok. 1533 1534deterministic_paths(Config) when is_list(Config) -> 1535 DataDir = proplists:get_value(data_dir, Config), 1536 1537 %% Files without +deterministic should differ if they were compiled from a 1538 %% different directory. 1539 true = deterministic_paths_1(DataDir, "simple", []), 1540 1541 %% ... but files with +deterministic shouldn't. 1542 false = deterministic_paths_1(DataDir, "simple", [deterministic]), 1543 1544 ok. 1545 1546deterministic_paths_1(DataDir, Name, Opts) -> 1547 Simple = filename:join(DataDir, "simple"), 1548 {ok, Cwd} = file:get_cwd(), 1549 try 1550 {ok,_,A} = compile:file(Simple, [binary | Opts]), 1551 ok = file:set_cwd(DataDir), 1552 {ok,_,B} = compile:file(Name, [binary | Opts]), 1553 A =/= B 1554 after 1555 file:set_cwd(Cwd) 1556 end. 1557 1558%%% 1559%%% Utilities. 1560%%% 1561 1562compile_and_verify(Name, Target, Opts) -> 1563 Mod = list_to_atom(filename:basename(Name, ".erl")), 1564 {ok,Mod} = compile:file(Name, Opts), 1565 {ok,{Mod,[{compile_info,CInfo}]}} = 1566 beam_lib:chunks(Target, [compile_info]), 1567 {options,BeamOpts} = lists:keyfind(options, 1, CInfo), 1568 Opts = BeamOpts. 1569 1570get_unique_beam_files() -> 1571 get_unique_files(".beam"). 1572 1573get_unique_files(Ext) -> 1574 Wc = filename:join(filename:dirname(code:which(?MODULE)), "*"++Ext), 1575 [F || F <- filelib:wildcard(Wc), 1576 not is_cloned(F, Ext), not is_lfe_module(F, Ext)]. 1577 1578is_cloned(File, Ext) -> 1579 Mod = list_to_atom(filename:basename(File, Ext)), 1580 test_lib:is_cloned_mod(Mod). 1581 1582is_lfe_module(File, Ext) -> 1583 case filename:basename(File, Ext) of 1584 "lfe_" ++ _ -> true; 1585 _ -> false 1586 end. 1587