1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-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 21%% 22-module(int_eval_SUITE). 23 24%% Purpose: Deeper test of the evaluator. 25 26-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 27 init_per_group/2,end_per_group/2, 28 init_per_testcase/2, end_per_testcase/2, 29 bifs_outside_erlang/1, spawning/1, applying/1, 30 catch_and_throw/1, external_call/1, test_module_info/1, 31 apply_interpreted_fun/1, apply_uninterpreted_fun/1, 32 interpreted_exit/1, otp_8310/1, stacktrace/1, maps/1, 33 call_inside_binary/1]). 34 35%% Helpers. 36-export([applier/3]). 37 38-define(IM, my_int_eval_module). 39 40-include_lib("common_test/include/ct.hrl"). 41 42suite() -> [{ct_hooks,[ts_install_cth]}, 43 {timetrap,{minutes,1}}]. 44 45all() -> 46 [bifs_outside_erlang, spawning, applying, 47 catch_and_throw, external_call, test_module_info, 48 apply_interpreted_fun, apply_uninterpreted_fun, 49 interpreted_exit, otp_8310, stacktrace, maps, 50 call_inside_binary]. 51 52groups() -> 53 []. 54 55init_per_suite(Config) -> 56 Config. 57 58end_per_suite(_Config) -> 59 ok. 60 61init_per_group(_GroupName, Config) -> 62 Config. 63 64end_per_group(_GroupName, Config) -> 65 Config. 66 67 68init_per_testcase(_Case, Config) -> 69 DataDir = proplists:get_value(data_dir, Config), 70 {module,?IM} = int:i(filename:join(DataDir, ?IM)), 71 ok = io:format("Interpreted modules: ~p",[int:interpreted()]), 72 Config. 73 74end_per_testcase(_Case, _Config) -> 75 ok = io:format("Interpreted modules: ~p", [int:interpreted()]), 76 ok. 77 78%% Test that BIFs outside the erlang module are correctly evaluated. 79bifs_outside_erlang(Config) when is_list(Config) -> 80 Fun = fun() -> 81 Id = ?IM:ets_new(), 82 Self = self(), 83 ok = io:format("Self: ~p", [Self]), 84 Info = ets:info(Id), 85 Self = proplists:get_value(owner, Info), 86 ?IM:ets_delete(Id), 87 ok 88 end, 89 ok = spawn_eval(Fun), 90 ok. 91 92%% Try evalutate spawn_link/3. 93spawning(Config) when is_list(Config) -> 94 ok = spawn_eval(fun() -> ?IM:spawn_test() end). 95 96%% Try various sorts of applies. 97applying(Config) when is_list(Config) -> 98 Fun = fun({number,X}, {number,Y}) -> X+Y end, 99 ok = spawn_eval(fun() -> ?IM:apply_test(Fun) end). 100 101%% Test catch and throw/1. 102catch_and_throw(Config) when is_list(Config) -> 103 {a,ball} = spawn_eval(fun() -> ok = ?IM:catch_a_ball(), 104 catch ?IM:throw_a_ball() end), 105 106 %% Throw and catch without any extra outer catch. 107 108 process_flag(trap_exit, true), 109 Pid1 = spawn_link(fun() -> exit(?IM:catch_a_ball()) end), 110 receive 111 {'EXIT',Pid1,ok} -> ok; 112 {'EXIT',Pid1,Bad1} -> ct:fail({bad_message,Bad1}) 113 after 5000 -> 114 ct:fail(timeout) 115 end, 116 117 118 %% Throw without catch. 119 120 Pid2 = spawn_link(fun() -> ?IM:throw_a_ball() end), 121 receive 122 {'EXIT',Pid2,{{nocatch,{a,ball}},[_|_]}} -> ok; 123 {'EXIT',Pid2,Bad2} -> ct:fail({bad_message,Bad2}) 124 after 5000 -> 125 ct:fail(timeout) 126 end, 127 128 ok = ?IM:more_catch(fun(_) -> ?IM:exit_me() end), 129 ok = ?IM:more_catch(fun(_) -> exit({unint, exit}) end), 130 {a, ball} = ?IM:more_catch(fun(_) -> ?IM:throw_a_ball() end), 131 {b, ball} = ?IM:more_catch(fun(_) -> throw({b,ball}) end), 132 133 ExitInt = {'EXIT',{int,exit}}, 134 ExitU = {'EXIT',{unint,exit}}, 135 136 ExitInt = (catch ?IM:more_nocatch(fun(_) -> ?IM:exit_me() end)), 137 ExitU = (catch ?IM:more_nocatch(fun(_) -> exit({unint, exit}) end)), 138 {a, ball} = (catch {error, ?IM:more_nocatch(fun(_) -> ?IM:throw_a_ball() end)}), 139 {b, ball} = (catch {error, ?IM:more_nocatch(fun(_) -> throw({b,ball}) end)}), 140 ok. 141 142%% Test external calls. 143external_call(Config) when is_list(Config) -> 144 ok = spawn_eval(fun() -> ?IM:external_call_test({some,stupid,data}) end). 145 146%% Test the module_info/0,1 functions. 147test_module_info(Config) when is_list(Config) -> 148 ModInfo = ?IM:module_info(), 149 {value,{exports,Exp}} = lists:keysearch(exports, 1, ModInfo), 150 {value,{attributes,Attr}} = lists:keysearch(attributes, 1, ModInfo), 151 Exp = ?IM:module_info(exports), 152 Attr = ?IM:module_info(attributes), 153 {value,{stupid_attribute,[{a,b}]}} = 154 lists:keysearch(stupid_attribute, 1, Attr), 155 156 %% Check exports using a list comprehension in the module itself. 157 158 ok = ?IM:check_exports(Exp), 159 160 %% Call module_info/0,1 from the module itself. 161 162 ok = ?IM:check_module_info(ModInfo, Exp), 163 164 ok. 165 166%% Apply a fun defined in interpreted code. 167apply_interpreted_fun(Config) when is_list(Config) -> 168 169 %% Called from uninterpreted code 170 F1 = spawn_eval(fun() -> ?IM:give_me_a_fun_0() end), 171 perfectly_alright = spawn_eval(fun() -> F1() end), 172 ATerm = {a,term}, 173 F2 = spawn_eval(fun() -> ?IM:give_me_a_fun_0(ATerm) end), 174 {ok,ATerm} = spawn_eval(fun() -> F2() end), 175 176 %% Called from uninterpreted code, badarity 177 {'EXIT',{{badarity,{F1,[snape]}},[{?MODULE,_,_,_}|_]}} = 178 spawn_eval(fun() -> F1(snape) end), 179 180 %% Called from uninterpreted code, error in fun 181 F3 = spawn_eval(fun() -> ?IM:give_me_a_bad_fun() end), 182 {'EXIT',{snape,[{?IM,_FunName,_,_}|_]}} = 183 spawn_eval(fun() -> F3(snape) end), 184 185 %% Called from within interpreted code 186 perfectly_alright = spawn_eval(fun() -> ?IM:do_apply(F1) end), 187 188 %% Called from within interpreted code, badarity 189 {'EXIT',{{badarity,{F1,[snape]}},[{?IM,do_apply,_,_}|_]}} = 190 spawn_eval(fun() -> ?IM:do_apply(F1, snape) end), 191 192 %% Called from within interpreted code, error in fun 193 {'EXIT',{snape,[{?IM,_FunName,_,_}|_]}} = 194 spawn_eval(fun() -> ?IM:do_apply(F3, snape) end), 195 196 %% Try some more complex funs. 197 F4 = ?IM:give_me_a_fun_1(14, 42), 198 {false,yes,yeah,false} = 199 F4({{1,nope},{14,yes},{42,yeah},{100,forget_it}}), 200 [this_is_ok,me_too] = 201 F4([{-24,no_way},{15,this_is_ok},{1333,forget_me},{37,me_too}]), 202 203 %% OTP-5837 204 %% Try fun with guard containing variable bound in environment 205 [yes,no,no,no] = ?IM:otp_5837(1), 206 207 ok. 208 209%% Apply a fun defined outside interpreted code. 210apply_uninterpreted_fun(Config) when is_list(Config) -> 211 212 F1 = fun(snape) -> 213 erlang:error(snape); 214 (_Arg) -> 215 perfectly_alright 216 end, 217 218 %% Ok 219 perfectly_alright = 220 spawn_eval(fun() -> ?IM:do_apply(F1, any_arg) end), 221 222 %% Badarity (evaluated in dbg_debugged, which calls erlang:apply/2) 223 {'EXIT',{{badarity,{F1,[]}},[{erlang,apply,_,_}|_]}} = 224 spawn_eval(fun() -> ?IM:do_apply(F1) end), 225 226 %% Error in fun 227 {'EXIT',{snape,[{?MODULE,_FunName,_,_}|_]}} = 228 spawn_eval(fun() -> ?IM:do_apply(F1, snape) end), 229 230 ok. 231 232%% 233%% Try executing an interpreted exit/1 call. 234%% 235 236interpreted_exit(Config) when is_list(Config) -> 237 process_flag(trap_exit, true), 238 Reason = make_ref(), 239 Pid = spawn_link(fun() -> ?IM:please_call_exit(Reason) end), 240 receive 241 {'EXIT',Pid,Reason} -> 242 ok; 243 {'EXIT',Pid,BadReason} -> 244 ct:fail({bad_message,BadReason}) 245 after 10000 -> 246 ct:fail(timeout) 247 end, 248 ok. 249 250%% OTP-8310. Bugfixes lc/bc and andalso/orelse. 251otp_8310(Config) when is_list(Config) -> 252 ok = ?IM:otp_8310(), 253 ok. 254 255applier(M, F, A) -> 256 Res = apply(M, F, A), 257 io:format("~p:~p(~p) => ~p\n", [M,F,A,Res]), 258 Res. 259 260stacktrace(Config) when is_list(Config) -> 261 {done,Stk} = do_eval(Config, stacktrace), 262 13 = length(Stk), 263 OldStackTraceFlag = int:stack_trace(), 264 int:stack_trace(no_tail), 265 try 266 Res = spawn_eval(fun() -> stacktrace:stacktrace() end), 267 io:format("\nInterpreted (no_tail):\n~p", [Res]), 268 {done,Stk} = Res 269 after 270 int:stack_trace(OldStackTraceFlag) 271 end, 272 ok. 273 274maps(Config) when is_list(Config) -> 275 Fun = fun () -> ?IM:empty_map_update([camembert]) end, 276 {'EXIT',{{badmap,[camembert]},_}} = spawn_eval(Fun), 277 [#{hello := 0, price := 0}] = spawn_eval(fun () -> ?IM:update_in_fun() end), 278 ok. 279 280call_inside_binary(Config) when is_list(Config) -> 281 <<"1">> = ?IM:call_inside_binary(fun erlang:integer_to_binary/1), 282 ok. 283 284do_eval(Config, Mod) -> 285 DataDir = proplists:get_value(data_dir, Config), 286 ok = file:set_cwd(DataDir), 287 288 %% Turn off type-based optimizations across function calls, as it 289 %% would turn many body-recursive calls into tail-recursive calls, 290 %% which would change the stacktrace. 291 {ok,Mod} = compile:file(Mod, [no_module_opt,report,debug_info]), 292 {module,Mod} = code:load_file(Mod), 293 CompiledRes = Mod:Mod(), 294 ok = io:format("Compiled:\n~p", [CompiledRes]), 295 io:nl(), 296 297 {module,Mod} = int:i(Mod), 298 IntRes = Mod:Mod(), 299 ok = io:format("Interpreted:\n~p", [IntRes]), 300 301 CompiledRes = IntRes. 302 303%% 304%% Evaluate in another process, to prevent the test_case process to become 305%% interpreted. 306%% 307 308spawn_eval(Fun) -> 309 Self = self(), 310 spawn_link(fun() -> Self ! (catch Fun()) end), 311 receive 312 Result -> 313 Result 314 end. 315