1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-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(error_handler).
21
22%% See the comment before the int/0 function for an explanation
23%% why this option is needed.
24-compile(no_module_opt).
25
26%% Callbacks called from the run-time system.
27-export([undefined_function/3,undefined_lambda/3,breakpoint/3]).
28
29%% Exported utility functions.
30-export([raise_undef_exception/3]).
31-export([stub_function/3]).
32
33-spec undefined_function(Module, Function, Args) ->
34	any() when
35      Module :: atom(),
36      Function :: atom(),
37      Args :: list().
38
39undefined_function(Module, Func, Args) ->
40    case ensure_loaded(Module) of
41	{module, Module} ->
42	    case erlang:function_exported(Module, Func, length(Args)) of
43		true ->
44		    apply(Module, Func, Args);
45		false ->
46		    call_undefined_function_handler(Module, Func, Args)
47	    end;
48	{module, _} ->
49	    crash(Module, Func, Args);
50	_Other ->
51	    crash(Module, Func, Args)
52    end.
53
54-spec undefined_lambda(Module, Fun, Args) -> term() when
55      Module :: atom(),
56      Fun :: fun(),
57      Args :: list().
58
59undefined_lambda(Module, Fun, Args) ->
60    case ensure_loaded(Module) of
61	{module, Module} ->
62	    %% There is no need (and no way) to test if the fun is present.
63	    %% apply/2 will not call us again if the fun is missing.
64	    apply(Fun, Args);
65	{module, _} ->
66	    crash(Fun, Args);
67	_Other ->
68	    crash(Fun, Args)
69    end.
70
71-spec breakpoint(Module :: atom(), Function :: atom(), Args :: [_]) ->
72	any().
73
74breakpoint(Module, Func, Args) ->
75    (int()):eval(Module, Func, Args).
76
77-spec raise_undef_exception(Module, Function, Args) -> no_return() when
78      Module :: atom(),
79      Function :: atom(),
80      Args :: list().
81
82raise_undef_exception(Module, Func, Args) ->
83    crash({Module,Func,Args,[]}).
84
85%% Used to make the call to the 'int' module a "weak" one, to avoid
86%% making Kernel a visible dependency to Debugger in xref. (To ensure
87%% that the call in breakpoint/3 is kept as an apply to an unknown
88%% module, this module must be compiled with the 'no_module_opt'
89%% option to turn off inter-function type analysis.)
90
91int() -> int.
92
93%%
94%% Crash providing a beautiful stack backtrace.
95%%
96-spec crash(atom(), [term()]) -> no_return().
97
98crash(Fun, Args) ->
99    crash({Fun,Args,[]}).
100
101-spec crash(atom(), atom(), arity() | [term()]) -> no_return().
102
103crash(M, F, A) ->
104    crash({M,F,A,[]}).
105
106-spec crash(tuple()) -> no_return().
107
108crash(Tuple) ->
109    try erlang:error(undef)
110    catch
111	error:undef:Stacktrace ->
112	    Stk = [Tuple|tl(Stacktrace)],
113	    erlang:raise(error, undef, Stk)
114    end.
115
116%% If the code_server has not been started yet dynamic code loading
117%% is handled by init.
118ensure_loaded(Module) ->
119    Self = self(),
120    case whereis(code_server) of
121	%% Perhaps double fault should be detected in code:ensure_loaded/1
122	%% instead, since this error handler cannot know whether the
123	%% code server can resolve the problem or not.
124	%% An {error, Reason} return from there would crash the code server and
125	%% bring down the node.
126	Self ->
127	    Error = "The code server called the unloaded module `" ++
128		atom_to_list(Module) ++ "'",
129	    halt(Error);
130	Pid when is_pid(Pid) ->
131	    code:ensure_loaded(Module);
132	_ ->
133	    init:ensure_loaded(Module)
134    end.
135
136-spec stub_function(atom(), atom(), [_]) -> no_return().
137
138stub_function(Mod, Func, Args) ->
139    exit({undef,[{Mod,Func,Args,[]}]}).
140
141call_undefined_function_handler(Module, Func, Args) ->
142    Handler = '$handle_undefined_function',
143    case erlang:function_exported(Module, Handler, 2) of
144	false ->
145	    crash(Module, Func, Args);
146	true ->
147	    Module:Handler(Func, Args)
148    end.
149