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