diff options
-rw-r--r-- | lib/kernel/doc/src/error_handler.xml | 38 | ||||
-rw-r--r-- | lib/kernel/src/error_handler.erl | 53 | ||||
-rw-r--r-- | lib/kernel/test/Makefile | 1 | ||||
-rw-r--r-- | lib/kernel/test/error_handler_SUITE.erl | 68 |
4 files changed, 119 insertions, 41 deletions
diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml index acbf9a2c6e..610b65f0a2 100644 --- a/lib/kernel/doc/src/error_handler.xml +++ b/lib/kernel/doc/src/error_handler.xml @@ -43,19 +43,39 @@ A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> </type_desc> <desc> - <p>This function is evaluated if a call is made to + <p>This function is called by the run-time system if a call is made to <c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> and <c><anno>Module</anno>:<anno>Function</anno>/N</c> is undefined. Note that <c>undefined_function/3</c> is evaluated inside the process making the original call.</p> - <p>If <c><anno>Module</anno></c> is interpreted, the interpreter is invoked - and the return value of the interpreted - <c><anno>Function</anno>(Arg1,.., ArgN)</c> call is returned.</p> - <p>Otherwise, it returns, if possible, the value of - <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> after an attempt has been - made to autoload <c><anno>Module</anno></c>. If this is not possible, the - call to <c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> fails with - exit reason <c>undef</c>.</p> + + <p>This function will first attempt to autoload + <c><anno>Module</anno></c>. If that is not possible, + an <c>undef</c> exception will be raised.</p> + + <p>If it was possible to load <c><anno>Module</anno></c> + and the function <c><anno>Function</anno>/N</c> is exported, + it will be called.</p> + + <p>Otherwise, if the function <c>'$handle_undefined_function'/2</c> + is exported, it will be called as + <c>'$handle_undefined_function'(</c><anno>Function</anno>, + <anno>Args</anno>). + </p> + <p>Otherwise an <c>undef</c> exception will be raised.</p> + </desc> + </func> + <func> + <name name="raise_undef_exception" arity="3"/> + <fsummary>Raise an undef exception</fsummary> + <type_desc variable="Args"> + A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> + </type_desc> + <desc> + <p>Raise an <c>undef</c> exception with a stacktrace indicating + that <c><anno>Module</anno>:<anno>Function</anno>/N</c> is + undefined. + </p> </desc> </func> <func> diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl index f8bc5f499c..a3aa1f1dcf 100644 --- a/lib/kernel/src/error_handler.erl +++ b/lib/kernel/src/error_handler.erl @@ -23,10 +23,12 @@ %% "error_handler: add no_native compiler directive" -compile(no_native). -%% A simple error handler. +%% Callbacks called from the run-time system. +-export([undefined_function/3,undefined_lambda/3,breakpoint/3]). --export([undefined_function/3, undefined_lambda/3, stub_function/3, - breakpoint/3]). +%% Exported utility functions. +-export([raise_undef_exception/3]). +-export([stub_function/3]). -spec undefined_function(Module, Function, Args) -> any() when @@ -41,12 +43,7 @@ undefined_function(Module, Func, Args) -> true -> apply(Module, Func, Args); false -> - case check_inheritance(Module, Args) of - {value, Base, Args1} -> - apply(Base, Func, Args1); - none -> - crash(Module, Func, Args) - end + call_undefined_function_handler(Module, Func, Args) end; {module, _} -> crash(Module, Func, Args); @@ -77,6 +74,14 @@ undefined_lambda(Module, Fun, Args) -> 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. @@ -130,27 +135,11 @@ ensure_loaded(Module) -> stub_function(Mod, Func, Args) -> exit({undef,[{Mod,Func,Args,[]}]}). -check_inheritance(Module, Args) -> - Attrs = erlang:get_module_info(Module, attributes), - case lists:keyfind(extends, 1, Attrs) of - {extends, [Base]} when is_atom(Base), Base =/= Module -> - %% This is just a heuristic for detecting abstract modules - %% with inheritance so they can be handled; it would be - %% much better to do it in the emulator runtime - case lists:keyfind(abstract, 1, Attrs) of - {abstract, [true]} -> - case lists:reverse(Args) of - [M|Rs] when tuple_size(M) > 1, - element(1,M) =:= Module, - tuple_size(element(2,M)) > 0, - is_atom(element(1,element(2,M))) -> - {value, Base, lists:reverse(Rs, [element(2,M)])}; - _ -> - {value, Base, Args} - end; - _ -> - {value, Base, Args} - end; - _ -> - none +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. diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 7fd3afe93c..8d2d55777b 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -48,6 +48,7 @@ MODULES= \ erl_distribution_SUITE \ erl_distribution_wb_SUITE \ erl_prim_loader_SUITE \ + error_handler_SUITE \ error_logger_SUITE \ error_logger_warn_SUITE \ file_SUITE \ diff --git a/lib/kernel/test/error_handler_SUITE.erl b/lib/kernel/test/error_handler_SUITE.erl new file mode 100644 index 0000000000..2a86d39b74 --- /dev/null +++ b/lib/kernel/test/error_handler_SUITE.erl @@ -0,0 +1,68 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(error_handler_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + undefined_function_handler/1]). + +%% Callback from error_handler. +-export(['$handle_undefined_function'/2]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [undefined_function_handler]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%%----------------------------------------------------------------- + +undefined_function_handler(_) -> + 42 = ?MODULE:forty_two(), + 42 = (id(?MODULE)):forty_two(), + {ok,{a,b,c}} = ?MODULE:one_arg({a,b,c}), + {ok,{a,b,c}} = (id(?MODULE)):one_arg({a,b,c}), + {'EXIT',{undef,[{?MODULE,undef_and_not_handled,[[1,2,3]],[]}|_]}} = + (catch ?MODULE:undef_and_not_handled([1,2,3])), + ok. + +'$handle_undefined_function'(forty_two, []) -> + 42; +'$handle_undefined_function'(one_arg, [Arg]) -> + {ok,Arg}; +'$handle_undefined_function'(Func, Args) -> + error_handler:raise_undef_exception(?MODULE, Func, Args). + +id(I) -> + I. |