1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2020. 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(apply_SUITE).
21
22-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24         mfa/1,fun_apply/1,involved/1]).
25
26-export([foo/0,bar/1,baz/2]).
27
28-include_lib("common_test/include/ct.hrl").
29
30suite() -> [{ct_hooks,[ts_install_cth]}].
31
32all() ->
33    [{group,p}].
34
35groups() ->
36    [{p,test_lib:parallel(),
37      [mfa,
38       fun_apply,
39       involved
40      ]}].
41
42init_per_suite(Config) ->
43    test_lib:recompile(?MODULE),
44    Config.
45
46end_per_suite(_Config) ->
47    ok.
48
49init_per_group(_GroupName, Config) ->
50    Config.
51
52end_per_group(_GroupName, Config) ->
53    Config.
54
55
56-define(APPLY0(M, F), (fun(Res) -> Res = M:F() end)(apply(M, F, []))).
57-define(APPLY1(M, F, A1), (fun(Res) -> Res = M:F(A1) end)(apply(M, F, [A1]))).
58-define(APPLY2(M, F, A1, A2), (fun(Res) -> Res = M:F(A1, A2) end)(apply(M, F, [A1,A2]))).
59
60mfa(Config) when is_list(Config) ->
61    ok = ?APPLY0(?MODULE, foo),
62    {[a,b]} = ?APPLY1(?MODULE, bar, [a,b]),
63    {39,{a}} = ?APPLY2(?MODULE, baz, 39, {a}),
64
65    Mod = id(?MODULE),
66    ok = ?APPLY0(Mod, foo),
67    {[a,b]} = ?APPLY1(Mod, bar, [a,b]),
68    {39,{a}} = ?APPLY2(Mod, baz, 39, {a}),
69
70    ok = ?APPLY0(?MODULE, (id(foo))),
71    {[a,b]} = ?APPLY1(?MODULE, (id(bar)), [a,b]),
72    {39,{a}} = ?APPLY2(?MODULE, (id(baz)), 39, {a}),
73
74    ok = ?APPLY0(Mod, (id(foo))),
75    {[a,b]} = ?APPLY1(Mod, (id(bar)), [a,b]),
76    {39,{a}} = ?APPLY2(Mod, (id(baz)), 39, {a}),
77
78    {'EXIT',_} = (catch ?APPLY2(Mod, (id(bazzzzzz)), a, b)),
79    {'EXIT',_} = (catch ?APPLY2({}, baz, a, b)),
80    {'EXIT',_} = (catch ?APPLY2(?MODULE, [], a, b)),
81    {'EXIT',_} = (catch bad_literal_call(1)),
82
83    ok = apply(Mod, foo, id([])),
84    {[a,b|c]} = apply(Mod, bar, id([[a,b|c]])),
85    {[xx],{a}} = apply(?MODULE, baz, id([[xx],{a}])),
86
87    Erlang = id(erlang),
88    Self = self(),
89    Self = ?APPLY0(Erlang, self),
90    42.0 = ?APPLY1(Erlang, abs, -42.0),
91    b = ?APPLY2(Erlang, element, 2, {a,b,c}),
92    true = ?APPLY1(Erlang, is_function, fun erlang:list_to_binary/1),
93    true = ?APPLY1(Erlang, is_function, fun() -> ok end),
94    false = ?APPLY1(Erlang, is_function, blurf),
95    true = ?APPLY2(Erlang, is_function, fun erlang:list_to_binary/1, 1),
96    true = ?APPLY2(Erlang, is_function, fun() -> ok end, 0),
97    false = ?APPLY2(Erlang, is_function, blurf, 0),
98
99    apply(Mod, foo, []).
100
101%% The single call to this function with a literal argument caused type
102%% optimization to swap out the 'mod' field of a #b_remote{}, which was
103%% mishandled during code generation as it assumed that the module would always
104%% be an atom.
105bad_literal_call(I) ->
106    I:foo().
107
108foo() ->
109    ok.
110
111bar(A) ->
112    {A}.
113
114baz(A, B) ->
115    {A,B}.
116
117-define(FUNAPPLY0(F), (fun(Res) -> Res = F() end)(apply(F, []))).
118-define(FUNAPPLY1(F, A1), (fun(Res) -> Res = F(A1) end)(apply(F, [A1]))).
119-define(FUNAPPLY2(F, A1, A2), (fun(Res) -> Res = F(A1, A2) end)(apply(F, [A1,A2]))).
120
121fun_apply(Config) when is_list(Config) ->
122    Self = self(),
123
124    Self = ?FUNAPPLY0(fun() -> self() end),
125    Self = ?FUNAPPLY0((id(fun() -> self() end))),
126    ok = ?FUNAPPLY0(fun ?MODULE:foo/0),
127    ok = ?FUNAPPLY0((id(fun ?MODULE:foo/0))),
128
129    -42 = ?FUNAPPLY1(fun(A) -> -A end, 42),
130    [x,yy] = ?FUNAPPLY1((id(fun(T) -> [x|T] end)), [yy]),
131    {[a|b]} = ?FUNAPPLY1(fun ?MODULE:bar/1, [a|b]),
132    {[a|b]} = ?FUNAPPLY1((id(fun ?MODULE:bar/1)), [a|b]),
133
134    {a,b} = ?FUNAPPLY2(fun(A, B) -> {A,B} end, a, b),
135    {a,[b]} = ?FUNAPPLY2((id(fun(A, B) -> {A,B} end)), a, [b]),
136    {42,{a}} = ?FUNAPPLY2((id(fun ?MODULE:baz/2)), 42, {a}),
137
138    ok.
139
140involved(_Config) ->
141    self() ! message,
142    ok = involved_1(),
143
144    self() ! message,
145    error = involved_2(),
146    ok.
147
148involved_1() ->
149    try
150        receive
151            _ ->
152                fun erlang:atom_to_list/1('')
153        end
154    of
155        [] ->
156            ok
157    catch
158        _:_ ->
159            error
160    end.
161
162involved_2() ->
163    try
164        receive
165            _ ->
166                fun erlang:atom_to_list/1()
167        end
168    of
169        [] ->
170            ok
171    catch
172        _:_ ->
173            error
174    end.
175
176id(I) -> I.
177