%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(error_handler). %% FIXME: remove no_native directive after HiPE has been changed to make %% remote calls link to the target's Export* like BEAM does. %% For a detailed explanation see the commit titled %% "error_handler: add no_native compiler directive" -compile(no_native). %% Callbacks called from the run-time system. -export([undefined_function/3,undefined_lambda/3,breakpoint/3]). %% Exported utility functions. -export([raise_undef_exception/3]). -export([stub_function/3]). -spec undefined_function(Module, Function, Args) -> any() when Module :: atom(), Function :: atom(), Args :: list(). undefined_function(Module, Func, Args) -> case ensure_loaded(Module) of {module, Module} -> case erlang:function_exported(Module, Func, length(Args)) of true -> apply(Module, Func, Args); false -> call_undefined_function_handler(Module, Func, Args) end; {module, _} -> crash(Module, Func, Args); _Other -> crash(Module, Func, Args) end. -spec undefined_lambda(Module, Fun, Args) -> term() when Module :: atom(), Fun :: fun(), Args :: list(). undefined_lambda(Module, Fun, Args) -> case ensure_loaded(Module) of {module, Module} -> %% There is no need (and no way) to test if the fun is present. %% apply/2 will not call us again if the fun is missing. apply(Fun, Args); {module, _} -> crash(Fun, Args); _Other -> crash(Fun, Args) end. -spec breakpoint(Module :: atom(), Function :: atom(), Args :: [_]) -> any(). breakpoint(Module, Func, Args) -> (int()):eval(Module, Func, Args). -spec raise_undef_exception(Module, Function, Args) -> no_return() when Module :: atom(), Function :: atom(), Args :: list(). raise_undef_exception(Module, Func, Args) -> crash({Module,Func,Args,[]}). %% Used to make the call to the 'int' module a "weak" one, to avoid %% building strong components in xref or dialyzer. int() -> int. %% %% Crash providing a beautiful stack backtrace. %% -spec crash(atom(), [term()]) -> no_return(). crash(Fun, Args) -> crash({Fun,Args,[]}). -spec crash(atom(), atom(), arity() | [term()]) -> no_return(). crash(M, F, A) -> crash({M,F,A,[]}). -spec crash(tuple()) -> no_return(). crash(Tuple) -> try erlang:error(undef) catch error:undef -> Stk = [Tuple|tl(erlang:get_stacktrace())], erlang:raise(error, undef, Stk) end. %% If the code_server has not been started yet dynamic code loading %% is handled by init. ensure_loaded(Module) -> Self = self(), case whereis(code_server) of %% Perhaps double fault should be detected in code:ensure_loaded/1 %% instead, since this error handler cannot know whether the %% code server can resolve the problem or not. %% An {error, Reason} return from there would crash the code server and %% bring down the node. Self -> Error = "The code server called the unloaded module `" ++ atom_to_list(Module) ++ "'", halt(Error); Pid when is_pid(Pid) -> code:ensure_loaded(Module); _ -> init:ensure_loaded(Module) end. -spec stub_function(atom(), atom(), [_]) -> no_return(). stub_function(Mod, Func, Args) -> exit({undef,[{Mod,Func,Args,[]}]}). call_undefined_function_handler(Module, Func, Args) -> Handler = '$handle_undefined_function', case erlang:function_exported(Module, Handler, 2) of false -> crash(Module, Func, Args); true -> Module:Handler(Func, Args) end.