Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
3331-Pass-Anno-to-ExternalFunctionHandler-in-er...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 3331-Pass-Anno-to-ExternalFunctionHandler-in-erl_eval.patch of Package erlang
From 1499ec5ad07906ab892424dfd93180834e2b4bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@dashbit.co> Date: Fri, 14 Jan 2022 18:08:42 +0100 Subject: [PATCH] Pass Anno to ExternalFunctionHandler in erl_eval This allows code using `erl_eval` to provide better stacktraces if desired. For this purpose, we also make sure all errors raised from `erl_eval` pass through the ExternalFunctionHandler. --- lib/debugger/src/dbg_ieval.erl | 9 +- .../src/big_external_type.erl | 2 +- .../opaque_SUITE_data/src/big_local_type.erl | 2 +- .../src/big_external_type.erl | 2 +- .../small_SUITE_data/src/big_local_type.erl | 2 +- lib/stdlib/doc/src/erl_eval.xml | 11 +- lib/stdlib/doc/src/shell.xml | 8 + lib/stdlib/src/erl_eval.erl | 484 +++++++++--------- lib/stdlib/src/erl_parse.yrl | 2 +- lib/stdlib/src/eval_bits.erl | 132 ++--- lib/stdlib/src/ms_transform.erl | 2 +- lib/stdlib/test/erl_eval_SUITE.erl | 197 ++++++- lib/stdlib/test/shell_SUITE.erl | 2 + lib/syntax_tools/src/erl_syntax.erl | 2 +- 14 files changed, 509 insertions(+), 348 deletions(-) diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index e03ca7fa11..18f226967d 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -1043,9 +1043,7 @@ expr({bin,Line,Fs}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line,top=false}, try eval_bits:expr_grp(Fs, Bs0, - fun (E, B) -> expr(E, B, Ieval) end, - [], - false) + fun (E, B) -> expr(E, B, Ieval) end) catch Class:Reason -> exception(Class, Reason, Bs0, Ieval) @@ -1504,7 +1502,7 @@ guard_expr({bin,_,Flds}, Bs) -> fun(E,B) -> {value,V} = guard_expr(E,B), {value,V,B} - end, [], false), + end), {value,V}. %% guard_case_clauses(Value, Clauses, Bindings, Error, Ieval) %% Error = try_clause | case_clause @@ -1583,8 +1581,7 @@ match1({map,_,Fields}, Map, Bs, BBs) when is_map(Map) -> match1({bin,_,Fs}, B, Bs0, BBs) when is_bitstring(B) -> try eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), - fun(E, Bs) -> expr(E, Bs, #ieval{}) end, - false) + fun(E, Bs) -> expr(E, Bs, #ieval{}) end) catch _:_ -> throw(nomatch) end; diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl index d286a378ed..7ad3f9366b 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl @@ -409,7 +409,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl index 7daceb5260..958ebeff93 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl @@ -406,7 +406,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl b/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl index 9ad4810a5e..3fa55dcc9c 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl @@ -412,7 +412,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl b/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl index fe567ff10d..89e0942ab5 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl @@ -409,7 +409,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml index 41566d9aec..71347b4319 100644 --- a/lib/stdlib/doc/src/erl_eval.xml +++ b/lib/stdlib/doc/src/erl_eval.xml @@ -273,10 +273,13 @@ Func(Name, Arguments, Bindings)</code> <taglist> <tag><c>{value,Func}</c></tag> <item> - <p>This defines a non-local function handler that is called with:</p> - <code type="none"> -Func(FuncSpec, Arguments)</code> - <p><c>FuncSpec</c> is the name of the function on the form + <p>This defines a non-local function handler. The function + may be called with two arguments:</p> + <code type="none">Func(FuncSpec, Arguments)</code> + <p>or three arguments:</p> + <code type="none">Func(Anno, FuncSpec, Arguments)</code> + <p><c>Anno</c> is the <c>erl_anno:anno()</c> of the node, + <c>FuncSpec</c> is the name of the function on the form <c>{Module,Function}</c> or a fun, and <c>Arguments</c> is a list of the <em>evaluated</em> arguments. The function handler returns the value of the function. To diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index dc68af4328..39912a4bcf 100644 --- a/lib/stdlib/doc/src/shell.xml +++ b/lib/stdlib/doc/src/shell.xml @@ -789,6 +789,14 @@ q - quit erlang manual page. (Arguments in <c>ArgList</c> are evaluated before the callback functions are called.)</p> + <p>From OTP 25.0, if there are errors evaluating Erlang constructs, + such as <c>badmatch</c> during pattern matching or <c>bad_generator</c> + in a comprehension, the evaluator will dispatch to + <c>erlang:raise(error, Reason, Stacktrace)</c>. This call will be + checked against the <c>non_local_allowed/3</c> callback function. + You can either forbid it, allow it, or redirect to another call of + your choice.</p> + <p>Argument <c>State</c> is a tuple <c>{ShellState,ExprState}</c>. The return value <c>NewState</c> has the same form. This can be used to carry a state between calls diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 7a809c4402..fc2e2a4194 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -61,8 +61,9 @@ -type(func_spec() :: {Module :: module(), Function :: atom()} | function()). -type(nlfun_handler() :: fun((FuncSpec :: func_spec(), - Arguments :: [term()]) -> - term())). + Arguments :: [term()]) -> term()) + | fun((Anno :: erl_anno:anno(), FuncSpec :: func_spec(), + Arguments :: [term()]) -> term())). -type(non_local_function_handler() :: {value, nlfun_handler()} | none). @@ -197,9 +198,9 @@ fun_data(F) when is_function(F) -> case erlang:fun_info(F, module) of {module,?MODULE} -> case erlang:fun_info(F, env) of - {env,[{FBs,_FLf,_FEf,_FUVs,FCs}]} -> + {env,[{_FAnno,FBs,_FLf,_FEf,_FUVs,FCs}]} -> {fun_data,FBs,FCs}; - {env,[{FBs,_FLf,_FEf,_FUVs,FCs,FName}]} -> + {env,[{_FAnno,FBs,_FLf,_FEf,_FUVs,FCs,FName}]} -> {named_fun_data,FBs,FName,FCs} end; _ -> @@ -232,12 +233,12 @@ expr(Expr, Bs, Lf, Ef, Rbs) -> FunUsedVars :: erl_lint:fun_used_vars(), Value :: value(), NewBindings :: binding_struct()). -expr({var,_,V}, Bs, _Lf, _Ef, RBs, _FUVs) -> +expr({var,Anno,V}, Bs, _Lf, Ef, RBs, _FUVs) -> case binding(V, Bs) of {value,Val} -> ret_expr(Val, Bs, RBs); unbound -> % Cannot not happen if checked by erl_lint - erlang:raise(error, {unbound,V}, ?STACKTRACE) + apply_error({unbound,V}, ?STACKTRACE, Anno, Bs, Ef, RBs) end; expr({char,_,C}, Bs, _Lf, _Ef, RBs, _FUVs) -> ret_expr(C, Bs, RBs); @@ -251,10 +252,10 @@ expr({string,_,S}, Bs, _Lf, _Ef, RBs, _FUVs) -> ret_expr(S, Bs, RBs); expr({nil, _}, Bs, _Lf, _Ef, RBs, _FUVs) -> ret_expr([], Bs, RBs); -expr({cons,_,H0,T0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({cons,Anno,H0,T0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,H,Bs1} = expr(H0, Bs0, Lf, Ef, none, FUVs), {value,T,Bs2} = expr(T0, Bs0, Lf, Ef, none, FUVs), - ret_expr([H|T], merge_bindings(Bs1, Bs2), RBs); + ret_expr([H|T], merge_bindings(Bs1, Bs2, Anno, Ef), RBs); expr({lc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) -> eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs); expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) -> @@ -262,17 +263,17 @@ expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) -> expr({tuple,_,Es}, Bs0, Lf, Ef, RBs, FUVs) -> {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef, FUVs), ret_expr(list_to_tuple(Vs), Bs, RBs); -expr({record_field,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, {undef_record,Name}, ?STACKTRACE); -expr({record_index,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, {undef_record,Name}, ?STACKTRACE); -expr({record,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, {undef_record,Name}, ?STACKTRACE); -expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, {undef_record,Name}, ?STACKTRACE); +expr({record_field,Anno,_,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) -> + apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs); +expr({record_index,Anno,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) -> + apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs); +expr({record,Anno,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) -> + apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs); +expr({record,Anno,_,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) -> + apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs); %% map -expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({map,Anno,Binding,Es}, Bs0, Lf, Ef, RBs, FUVs) -> {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, none, FUVs), {Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef, FUVs), _ = maps:put(k, v, Map0), %Validate map. @@ -281,7 +282,7 @@ expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs, FUVs) -> ({map_exact,K,V}, Mi) -> maps:update(K, V, Mi) end, Map0, Vs), - ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs); + ret_expr(Map1, merge_bindings(Bs2, Bs1, Anno, Ef), RBs); expr({map,_,Es}, Bs0, Lf, Ef, RBs, FUVs) -> {Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef, FUVs), ret_expr(lists:foldl(fun @@ -290,13 +291,13 @@ expr({map,_,Es}, Bs0, Lf, Ef, RBs, FUVs) -> expr({block,_,Es}, Bs, Lf, Ef, RBs, FUVs) -> exprs(Es, Bs, Lf, Ef, RBs, FUVs); -expr({'if',_,Cs}, Bs, Lf, Ef, RBs, FUVs) -> - if_clauses(Cs, Bs, Lf, Ef, RBs, FUVs); -expr({'case',_,E,Cs}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({'if',Anno,Cs}, Bs, Lf, Ef, RBs, FUVs) -> + if_clauses(Cs, Anno, Bs, Lf, Ef, RBs, FUVs); +expr({'case',Anno,E,Cs}, Bs0, Lf, Ef, RBs, FUVs) -> {value,Val,Bs} = expr(E, Bs0, Lf, Ef, none, FUVs), - case_clauses(Val, Cs, Bs, Lf, Ef, RBs, FUVs); -expr({'try',_,B,Cases,Catches,AB}, Bs, Lf, Ef, RBs, FUVs) -> - try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs); + case_clauses(Val, Cs, Anno, Bs, Lf, Ef, RBs, FUVs); +expr({'try',Anno,B,Cases,Catches,AB}, Bs, Lf, Ef, RBs, FUVs) -> + try_clauses(B, Cases, Catches, AB, Anno, Bs, Lf, Ef, RBs, FUVs); expr({'receive',_,Cs}, Bs, Lf, Ef, RBs, FUVs) -> receive_clauses(Cs, Bs, Lf, Ef, RBs, FUVs); expr({'receive',_, Cs, E, TB}, Bs0, Lf, Ef, RBs, FUVs) -> @@ -306,12 +307,12 @@ expr({'fun',_Anno,{function,Mod0,Name0,Arity0}}, Bs0, Lf, Ef, RBs, FUVs) -> {[Mod,Name,Arity],Bs} = expr_list([Mod0,Name0,Arity0], Bs0, Lf, Ef, FUVs), F = erlang:make_fun(Mod, Name, Arity), ret_expr(F, Bs, RBs); -expr({'fun',_Anno,{function,Name,Arity}}, _Bs0, _Lf, _Ef, _RBs, _FUVs) -> % R8 +expr({'fun',Anno,{function,Name,Arity}}, Bs0, _Lf, Ef, RBs, _FUVs) -> % R8 %% Don't know what to do... - erlang:raise(error, undef, [{?MODULE,Name,Arity}|?STACKTRACE]); + apply_error(undef, [{?MODULE,Name,Arity}|?STACKTRACE], Anno, Bs0, Ef, RBs); expr({'fun',Anno,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs, FUVs) -> {En,NewFUVs} = fun_used_bindings(Ex, Cs, Bs, FUVs), - Info = {En,Lf,Ef,NewFUVs,Cs}, + Info = {Anno,En,Lf,Ef,NewFUVs,Cs}, %% This is a really ugly hack! F = @@ -350,13 +351,13 @@ expr({'fun',Anno,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs, FUVs) -> eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], Info) end; _Other -> L = erl_anno:location(Anno), - erlang:raise(error, {'argument_limit',{'fun',L,to_terms(Cs)}}, - ?STACKTRACE) + Reason = {'argument_limit',{'fun',L,to_terms(Cs)}}, + apply_error(Reason, ?STACKTRACE, Anno, Bs, Ef, RBs) end, ret_expr(F, Bs, RBs); expr({named_fun,Anno,Name,Cs} = Ex, Bs, Lf, Ef, RBs, FUVs) -> {En,NewFUVs} = fun_used_bindings(Ex, Cs, Bs, FUVs), - Info = {En,Lf,Ef,NewFUVs,Cs,Name}, + Info = {Anno,En,Lf,Ef,NewFUVs,Cs,Name}, %% This is a really ugly hack! F = @@ -400,9 +401,8 @@ expr({named_fun,Anno,Name,Cs} = Ex, Bs, Lf, Ef, RBs, FUVs) -> RF, Info) end; _Other -> L = erl_anno:location(Anno), - erlang:raise(error, {'argument_limit', - {named_fun,L,Name,to_terms(Cs)}}, - ?STACKTRACE) + Reason = {'argument_limit',{named_fun,L,Name,to_terms(Cs)}}, + apply_error(Reason, ?STACKTRACE, Anno, Bs, Ef, RBs) end, ret_expr(F, Bs, RBs); expr({call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[{lc,_,_E,_Qs}=LC | As0]}, @@ -422,33 +422,33 @@ expr({call,A1,{remote,A2,{record_field,_,{atom,_,''},{atom,_,qlc}=Mod}, [{lc,_,_E,_Qs} | As0]=As}, Bs, Lf, Ef, RBs, FUVs) when length(As0) =< 1 -> expr({call,A1,{remote,A2,Mod,Func},As}, Bs, Lf, Ef, RBs, FUVs); -expr({call,_,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({call,Anno,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,M,Bs1} = expr(Mod, Bs0, Lf, Ef, none, FUVs), {value,F,Bs2} = expr(Func, Bs0, Lf, Ef, none, FUVs), - {As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2), Lf, Ef, FUVs), + {As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2, Anno, Ef), Lf, Ef, FUVs), %% M could be a parameterized module (not an atom). case is_atom(M) andalso erl_internal:bif(M, F, length(As)) of true -> - bif(F, As, Bs3, Ef, RBs); + bif(F, As, Anno, Bs3, Ef, RBs); false -> - do_apply(M, F, As, Bs3, Ef, RBs) + do_apply(M, F, As, Anno, Bs3, Ef, RBs) end; -expr({call,_,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({call,Anno,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) -> case erl_internal:bif(Func, length(As0)) of true -> {As,Bs} = expr_list(As0, Bs0, Lf, Ef), - bif(Func, As, Bs, Ef, RBs); + bif(Func, As, Anno, Bs, Ef, RBs); false -> - local_func(Func, As0, Bs0, Lf, Ef, RBs, FUVs) + local_func(Func, As0, Anno, Bs0, Lf, Ef, RBs, FUVs) end; -expr({call,_,Func0,As0}, Bs0, Lf, Ef, RBs, FUVs) -> % function or {Mod,Fun} +expr({call,Anno,Func0,As0}, Bs0, Lf, Ef, RBs, FUVs) -> % function or {Mod,Fun} {value,Func,Bs1} = expr(Func0, Bs0, Lf, Ef, none, FUVs), {As,Bs2} = expr_list(As0, Bs1, Lf, Ef, FUVs), case Func of {M,F} when is_atom(M), is_atom(F) -> - erlang:raise(error, {badfun,Func}, ?STACKTRACE); + apply_error({badfun,Func}, ?STACKTRACE, Anno, Bs0, Ef, RBs); _ -> - do_apply(Func, As, Bs2, Ef, RBs) + do_apply(Func, As, Anno, Bs2, Ef, RBs) end; expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs, FUVs) -> try expr(Expr, Bs0, Lf, Ef, none, FUVs) of @@ -462,46 +462,50 @@ expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs, FUVs) -> error:Reason:Stacktrace -> ret_expr({'EXIT',{Reason,Stacktrace}}, Bs0, RBs) end; -expr({match,_,Lhs,Rhs0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({match,Anno,Lhs,Rhs0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,Rhs,Bs1} = expr(Rhs0, Bs0, Lf, Ef, none, FUVs), - case match(Lhs, Rhs, Bs1) of + case match(Lhs, Rhs, Anno, Bs1, Bs1, Ef) of {match,Bs} -> ret_expr(Rhs, Bs, RBs); - nomatch -> erlang:raise(error, {badmatch,Rhs}, ?STACKTRACE) + nomatch -> apply_error({badmatch,Rhs}, ?STACKTRACE, Anno, Bs0, Ef, RBs) end; -expr({op,_,Op,A0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({op,Anno,Op,A0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,A,Bs} = expr(A0, Bs0, Lf, Ef, none, FUVs), - eval_op(Op, A, Bs, Ef, RBs); -expr({op,_,'andalso',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) -> + eval_op(Op, A, Anno, Bs, Ef, RBs); +expr({op,Anno,'andalso',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,L,Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), V = case L of true -> {value,R,_} = expr(R0, Bs1, Lf, Ef, none, FUVs), R; false -> false; - _ -> erlang:raise(error, {badarg,L}, ?STACKTRACE) + _ -> apply_error({badarg,L}, ?STACKTRACE, Anno, Bs0, Ef, RBs) end, ret_expr(V, Bs1, RBs); -expr({op,_,'orelse',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({op,Anno,'orelse',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,L,Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), V = case L of true -> true; false -> {value,R,_} = expr(R0, Bs1, Lf, Ef, none, FUVs), R; - _ -> erlang:raise(error, {badarg,L}, ?STACKTRACE) + _ -> apply_error({badarg,L}, ?STACKTRACE, Anno, Bs0, Ef, RBs) end, ret_expr(V, Bs1, RBs); -expr({op,_,Op,L0,R0}, Bs0, Lf, Ef, RBs, FUVs) -> +expr({op,Anno,Op,L0,R0}, Bs0, Lf, Ef, RBs, FUVs) -> {value,L,Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), {value,R,Bs2} = expr(R0, Bs0, Lf, Ef, none, FUVs), - eval_op(Op, L, R, merge_bindings(Bs1, Bs2), Ef, RBs); + eval_op(Op, L, R, Anno, merge_bindings(Bs1, Bs2, Anno, Ef), Ef, RBs); expr({bin,_,Fs}, Bs0, Lf, Ef, RBs, FUVs) -> EvalFun = fun(E, B) -> expr(E, B, Lf, Ef, none, FUVs) end, - {value,V,Bs} = eval_bits:expr_grp(Fs, Bs0, EvalFun), + ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, RBs) end, + {value,V,Bs} = eval_bits:expr_grp(Fs, Bs0, EvalFun, ErrorFun), ret_expr(V, Bs, RBs); -expr({remote,_,_,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, {badexpr,':'}, ?STACKTRACE). +expr({remote,Anno,_,_}, Bs0, _Lf, Ef, RBs, _FUVs) -> + apply_error({badexpr,':'}, ?STACKTRACE, Anno, Bs0, Ef, RBs). + +apply_error(Reason, Stack, Anno, Bs0, Ef, RBs) -> + do_apply(erlang, raise, [error, Reason, Stack], Anno, Bs0, Ef, RBs). find_maxline(LC) -> put('$erl_eval_max_line', 0), @@ -584,62 +588,62 @@ unhide_calls([E | Es], MaxLine, D) -> unhide_calls(E, _MaxLine, _D) -> E. -%% local_func(Function, Arguments, Bindings, LocalFuncHandler, +%% local_func(Function, Arguments, Anno, Bindings, LocalFuncHandler, %% ExternalFuncHandler, RBs, FunUsedVars) -> %% {value,Value,Bindings} | Value when %% LocalFuncHandler = {value,F} | {value,F,Eas} | %% {eval,F} | {eval,F,Eas} | none. -local_func(Func, As0, Bs0, {value,F}, Ef, value, FUVs) -> +local_func(Func, As0, _Anno, Bs0, {value,F}, Ef, value, FUVs) -> {As1,_Bs1} = expr_list(As0, Bs0, {value,F}, Ef, FUVs), %% Make tail recursive calls when possible. F(Func, As1); -local_func(Func, As0, Bs0, {value,F}, Ef, RBs, FUVs) -> +local_func(Func, As0, _Anno, Bs0, {value,F}, Ef, RBs, FUVs) -> {As1,Bs1} = expr_list(As0, Bs0, {value,F}, Ef, FUVs), ret_expr(F(Func, As1), Bs1, RBs); -local_func(Func, As0, Bs0, {value,F,Eas}, Ef, RBs, FUVs) -> +local_func(Func, As0, Anno, Bs0, {value,F,Eas}, Ef, RBs, FUVs) -> Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end, - local_func(Func, As0, Bs0, {value, Fun}, Ef, RBs, FUVs); -local_func(Func, As, Bs, {eval,F}, _Ef, RBs, _FUVs) -> - local_func2(F(Func, As, Bs), RBs); -local_func(Func, As, Bs, {eval,F,Eas}, _Ef, RBs, _FUVs) -> - local_func2(apply(F, [Func,As,Bs|Eas]), RBs); + local_func(Func, As0, Anno, Bs0, {value, Fun}, Ef, RBs, FUVs); +local_func(Func, As, Anno, Bs, {eval,F}, _Ef, RBs, _FUVs) -> + local_func2(F(Func, As, Bs), Anno, RBs); +local_func(Func, As, Anno, Bs, {eval,F,Eas}, _Ef, RBs, _FUVs) -> + local_func2(apply(F, [Func,As,Bs|Eas]), Anno, RBs); %% These two clauses are for backwards compatibility. -local_func(Func, As0, Bs0, {M,F}, Ef, RBs, FUVs) -> +local_func(Func, As0, _Anno, Bs0, {M,F}, Ef, RBs, FUVs) -> {As1,Bs1} = expr_list(As0, Bs0, {M,F}, Ef, FUVs), ret_expr(M:F(Func,As1), Bs1, RBs); -local_func(Func, As, _Bs, {M,F,Eas}, _Ef, RBs, _FUVs) -> - local_func2(apply(M, F, [Func,As|Eas]), RBs); +local_func(Func, As, Anno, _Bs, {M,F,Eas}, _Ef, RBs, _FUVs) -> + local_func2(apply(M, F, [Func,As|Eas]), Anno, RBs); %% Default unknown function handler to undefined function. -local_func(Func, As0, _Bs0, none, _Ef, _RBs, _FUVs) -> - erlang:raise(error, undef, [{?MODULE,Func,length(As0)}|?STACKTRACE]). +local_func(Func, As0, Anno, Bs0, none, Ef, RBs, _FUVs) -> + apply_error(undef, [{?MODULE,Func,length(As0)}|?STACKTRACE], Anno, Bs0, Ef, RBs). -local_func2({value,V,Bs}, RBs) -> +local_func2({value,V,Bs}, _Anno, RBs) -> ret_expr(V, Bs, RBs); -local_func2({eval,F,As,Bs}, RBs) -> % This reply is not documented. +local_func2({eval,F,As,Bs}, Anno, RBs) -> % This reply is not documented. %% The shell found F. erl_eval tries to do a tail recursive call, %% something the shell cannot do. Do not use Ef here. - do_apply(F, As, Bs, none, RBs). + do_apply(F, As, Anno, Bs, none, RBs). %% bif(Name, Arguments, RBs) %% Evaluate the Erlang auto-imported function Name. erlang:apply/2,3 %% are "hidden" from the external function handler. -bif(apply, [erlang,apply,As], Bs, Ef, RBs) -> - bif(apply, As, Bs, Ef, RBs); -bif(apply, [M,F,As], Bs, Ef, RBs) -> - do_apply(M, F, As, Bs, Ef, RBs); -bif(apply, [F,As], Bs, Ef, RBs) -> - do_apply(F, As, Bs, Ef, RBs); -bif(Name, As, Bs, Ef, RBs) -> - do_apply(erlang, Name, As, Bs, Ef, RBs). +bif(apply, [erlang,apply,As], Anno, Bs, Ef, RBs) -> + bif(apply, As, Anno, Bs, Ef, RBs); +bif(apply, [M,F,As], Anno, Bs, Ef, RBs) -> + do_apply(M, F, As, Anno, Bs, Ef, RBs); +bif(apply, [F,As], Anno, Bs, Ef, RBs) -> + do_apply(F, As, Anno, Bs, Ef, RBs); +bif(Name, As, Anno, Bs, Ef, RBs) -> + do_apply(erlang, Name, As, Anno, Bs, Ef, RBs). %% do_apply(Func, Arguments, Bindings, ExternalFuncHandler, RBs) -> %% {value,Value,Bindings} | Value when %% ExternalFuncHandler = {value,F} | none, %% Func = fun() -do_apply(Func, As, Bs0, Ef, RBs) -> +do_apply(Func, As, Anno, Bs0, Ef, RBs) -> Env = if is_function(Func) -> case {erlang:fun_info(Func, module), @@ -653,7 +657,7 @@ do_apply(Func, As, Bs0, Ef, RBs) -> no_env end, case {Env,Ef} of - {{env,[{FBs,FLf,FEf,FFUVs,FCs}]},_} -> + {{env,[{FAnno,FBs,FLf,FEf,FFUVs,FCs}]},_} -> %% If we are evaluting within another function body %% (RBs =/= none), we return RBs when this function body %% has been evalutated, otherwise we return Bs0, the @@ -664,20 +668,20 @@ do_apply(Func, As, Bs0, Ef, RBs) -> end, case {erlang:fun_info(Func, arity), length(As)} of {{arity, Arity}, Arity} -> - eval_fun(FCs, As, FBs, FLf, FEf, NRBs, FFUVs); + eval_fun(FCs, As, FAnno, FBs, FLf, FEf, NRBs, FFUVs); _ -> - erlang:raise(error, {badarity,{Func,As}},?STACKTRACE) + apply_error({badarity,{Func,As}}, ?STACKTRACE, Anno, Bs0, Ef, RBs) end; - {{env,[{FBs,FLf,FEf,FFUVs,FCs,FName}]},_} -> + {{env,[{FAnno,FBs,FLf,FEf,FFUVs,FCs,FName}]},_} -> NRBs = if RBs =:= none -> Bs0; true -> RBs end, case {erlang:fun_info(Func, arity), length(As)} of {{arity, Arity}, Arity} -> - eval_named_fun(FCs, As, FBs, FLf, FEf, FName, Func, NRBs, FFUVs); + eval_named_fun(FCs, As, FAnno, FBs, FLf, FEf, FName, Func, NRBs, FFUVs); _ -> - erlang:raise(error, {badarity,{Func,As}},?STACKTRACE) + apply_error({badarity,{Func,As}}, ?STACKTRACE, Anno, Bs0, Ef, RBs) end; {no_env,none} when RBs =:= value -> %% Make tail recursive calls when possible. @@ -685,12 +689,12 @@ do_apply(Func, As, Bs0, Ef, RBs) -> {no_env,none} -> ret_expr(apply(Func, As), Bs0, RBs); {no_env,{value,F}} when RBs =:= value -> - F(Func,As); + do_apply(F, Anno, Func, As); {no_env,{value,F}} -> - ret_expr(F(Func, As), Bs0, RBs) + ret_expr(do_apply(F, Anno, Func, As), Bs0, RBs) end. -do_apply(Mod, Func, As, Bs0, Ef, RBs) -> +do_apply(Mod, Func, As, Anno, Bs0, Ef, RBs) -> case Ef of none when RBs =:= value -> %% Make tail recursive calls when possible. @@ -698,11 +702,16 @@ do_apply(Mod, Func, As, Bs0, Ef, RBs) -> none -> ret_expr(apply(Mod, Func, As), Bs0, RBs); {value,F} when RBs =:= value -> - F({Mod,Func}, As); + do_apply(F, Anno, {Mod,Func}, As); {value,F} -> - ret_expr(F({Mod,Func}, As), Bs0, RBs) + ret_expr(do_apply(F, Anno, {Mod,Func}, As), Bs0, RBs) end. +do_apply(F, Anno, FunOrModFun, Args) when is_function(F, 3) -> + F(Anno, FunOrModFun, Args); +do_apply(F, _Anno, FunOrModFun, Args) when is_function(F, 2) -> + F(FunOrModFun, Args). + %% eval_lc(Expr, [Qualifier], Bindings, LocalFunctionHandler, %% ExternalFuncHandler, RetBindings) -> %% {value,Value,Bindings} | Value @@ -710,14 +719,14 @@ do_apply(Mod, Func, As, Bs0, Ef, RBs) -> eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs) -> ret_expr(lists:reverse(eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, [])), Bs, RBs). -eval_lc1(E, [{generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> +eval_lc1(E, [{generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> {value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end, - eval_generate(L1, P, Bs0, Lf, Ef, CompFun, Acc0); -eval_lc1(E, [{b_generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> + eval_generate(L1, P, Anno, Bs0, Lf, Ef, CompFun, Acc0); +eval_lc1(E, [{b_generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> {value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end, - eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0); + eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0); eval_lc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) -> CompFun = fun(Bs) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end, eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc); @@ -732,14 +741,14 @@ eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) -> eval_bc(E, Qs, Bs, Lf, Ef, RBs, FUVs) -> ret_expr(eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, <<>>), Bs, RBs). -eval_bc1(E, [{b_generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> +eval_bc1(E, [{b_generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> {value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end, - eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0); -eval_bc1(E, [{generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> + eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0); +eval_bc1(E, [{generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) -> {value,List,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs), CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end, - eval_generate(List, P, Bs0, Lf, Ef, CompFun, Acc0); + eval_generate(List, P, Anno, Bs0, Lf, Ef, CompFun, Acc0); eval_bc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) -> CompFun = fun(Bs) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end, eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc); @@ -747,35 +756,36 @@ eval_bc1(E, [], Bs, Lf, Ef, FUVs, Acc) -> {value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs), <<Acc/bitstring,V/bitstring>>. -eval_generate([V|Rest], P, Bs0, Lf, Ef, CompFun, Acc) -> - case match(P, V, new_bindings(Bs0), Bs0) of +eval_generate([V|Rest], P, Anno, Bs0, Lf, Ef, CompFun, Acc) -> + case match(P, V, Anno, new_bindings(Bs0), Bs0, Ef) of {match,Bsn} -> Bs2 = add_bindings(Bsn, Bs0), NewAcc = CompFun(Bs2, Acc), - eval_generate(Rest, P, Bs0, Lf, Ef, CompFun, NewAcc); + eval_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, NewAcc); nomatch -> - eval_generate(Rest, P, Bs0, Lf, Ef, CompFun, Acc) + eval_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, Acc) end; -eval_generate([], _P, _Bs0, _Lf, _Ef, _CompFun, Acc) -> +eval_generate([], _P, _Anno, _Bs0, _Lf, _Ef, _CompFun, Acc) -> Acc; -eval_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) -> - erlang:raise(error, {bad_generator,Term}, ?STACKTRACE). +eval_generate(Term, _P, Anno, Bs0, _Lf, Ef, _CompFun, _Acc) -> + apply_error({bad_generator,Term}, ?STACKTRACE, Anno, Bs0, Ef, none). -eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, Lf, Ef, CompFun, Acc) -> - Mfun = match_fun(Bs0), +eval_b_generate(<<_/bitstring>>=Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc) -> + Mfun = match_fun(Bs0, Ef), Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end, - case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun) of + ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end, + case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of {match, Rest, Bs1} -> Bs2 = add_bindings(Bs1, Bs0), NewAcc = CompFun(Bs2, Acc), - eval_b_generate(Rest, P, Bs0, Lf, Ef, CompFun, NewAcc); + eval_b_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, NewAcc); {nomatch, Rest} -> - eval_b_generate(Rest, P, Bs0, Lf, Ef, CompFun, Acc); + eval_b_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, Acc); done -> Acc end; -eval_b_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) -> - erlang:raise(error, {bad_generator,Term}, ?STACKTRACE). +eval_b_generate(Term, _P, Anno, Bs0, _Lf, Ef, _CompFun, _Acc) -> + apply_error({bad_generator,Term}, ?STACKTRACE, Anno, Bs0, Ef, none). eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) -> case erl_lint:is_guard_test(F) of @@ -789,7 +799,7 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) -> {value,true,Bs1} -> CompFun(Bs1); {value,false,_} -> Acc; {value,V,_} -> - erlang:raise(error, {bad_filter,V}, ?STACKTRACE) + apply_error({bad_filter,V}, ?STACKTRACE, element(2, F), Bs0, Ef, none) end end. @@ -824,48 +834,48 @@ ret_expr(V, Bs, none) -> ret_expr(V, _Bs, RBs) when is_list(RBs); is_map(RBs) -> {value,V,RBs}. -%% eval_fun(Arguments, {Bindings,LocalFunctionHandler, +%% eval_fun(Arguments, {Anno,Bindings,LocalFunctionHandler, %% ExternalFunctionHandler,FunUsedVars,Clauses}) -> Value %% This function is called when the fun is called from compiled code %% or from apply. -eval_fun(As, {Bs0,Lf,Ef,FUVs,Cs}) -> - eval_fun(Cs, As, Bs0, Lf, Ef, value, FUVs). +eval_fun(As, {Anno,Bs0,Lf,Ef,FUVs,Cs}) -> + eval_fun(Cs, As, Anno, Bs0, Lf, Ef, value, FUVs). -eval_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, RBs, FUVs) -> - case match_list(H, As, new_bindings(Bs0), Bs0) of +eval_fun([{clause,_,H,G,B}|Cs], As, Anno, Bs0, Lf, Ef, RBs, FUVs) -> + case match_list(H, As, Anno, new_bindings(Bs0), Bs0, Ef) of {match,Bsn} -> % The new bindings for the head Bs1 = add_bindings(Bsn, Bs0), % which then shadow! case guard(G, Bs1, Lf, Ef) of true -> exprs(B, Bs1, Lf, Ef, RBs, FUVs); - false -> eval_fun(Cs, As, Bs0, Lf, Ef, RBs, FUVs) + false -> eval_fun(Cs, As, Anno, Bs0, Lf, Ef, RBs, FUVs) end; nomatch -> - eval_fun(Cs, As, Bs0, Lf, Ef, RBs, FUVs) + eval_fun(Cs, As, Anno, Bs0, Lf, Ef, RBs, FUVs) end; -eval_fun([], As, _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, function_clause, - [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE]). +eval_fun([], As, Anno, Bs, _Lf, Ef, RBs, _FUVs) -> + Stack = [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE], + apply_error(function_clause, Stack, Anno, Bs, Ef, RBs). -eval_named_fun(As, Fun, {Bs0,Lf,Ef,FUVs,Cs,Name}) -> - eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, value, FUVs). +eval_named_fun(As, Fun, {Anno,Bs0,Lf,Ef,FUVs,Cs,Name}) -> + eval_named_fun(Cs, As, Anno, Bs0, Lf, Ef, Name, Fun, value, FUVs). -eval_named_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) -> +eval_named_fun([{clause,_,H,G,B}|Cs], As, Anno, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) -> Bs1 = add_binding(Name, Fun, Bs0), - case match_list(H, As, new_bindings(Bs0), Bs1) of + case match_list(H, As, Anno, new_bindings(Bs0), Bs1, Ef) of {match,Bsn} -> % The new bindings for the head Bs2 = add_bindings(Bsn, Bs1), % which then shadow! case guard(G, Bs2, Lf, Ef) of true -> exprs(B, Bs2, Lf, Ef, RBs, FUVs); - false -> eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) + false -> eval_named_fun(Cs, As, Anno, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) end; nomatch -> - eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) + eval_named_fun(Cs, As, Anno, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) end; -eval_named_fun([], As, _Bs, _Lf, _Ef, _Name, _Fun, _RBs, _FUVs) -> - erlang:raise(error, function_clause, - [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE]). +eval_named_fun([], As, Anno, Bs, _Lf, Ef, _Name, _Fun, RBs, _FUVs) -> + Stack = [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE], + apply_error(function_clause, Stack, Anno, Bs, Ef, RBs). %% expr_list(ExpressionList, Bindings) @@ -908,31 +918,31 @@ expr_list(Es, Bs, Lf, Ef, FUVs) -> expr_list([E|Es], Vs, BsOrig, Bs0, Lf, Ef, FUVs) -> {value,V,Bs1} = expr(E, BsOrig, Lf, Ef, none, FUVs), - expr_list(Es, [V|Vs], BsOrig, merge_bindings(Bs1, Bs0), Lf, Ef, FUVs); + expr_list(Es, [V|Vs], BsOrig, merge_bindings(Bs1, Bs0, element(2, E), Ef), Lf, Ef, FUVs); expr_list([], Vs, _, Bs, _Lf, _Ef, _FUVs) -> {reverse(Vs),Bs}. -eval_op(Op, Arg1, Arg2, Bs, Ef, RBs) -> - do_apply(erlang, Op, [Arg1,Arg2], Bs, Ef, RBs). +eval_op(Op, Arg1, Arg2, Anno, Bs, Ef, RBs) -> + do_apply(erlang, Op, [Arg1,Arg2], Anno, Bs, Ef, RBs). -eval_op(Op, Arg, Bs, Ef, RBs) -> - do_apply(erlang, Op, [Arg], Bs, Ef, RBs). +eval_op(Op, Arg, Anno, Bs, Ef, RBs) -> + do_apply(erlang, Op, [Arg], Anno, Bs, Ef, RBs). -%% if_clauses(Clauses, Bindings, LocalFuncHandler, ExtFuncHandler, RBs) +%% if_clauses(Clauses, Anno, Bindings, LocalFuncHandler, ExtFuncHandler, RBs) -if_clauses([{clause,_,[],G,B}|Cs], Bs, Lf, Ef, RBs, FUVs) -> +if_clauses([{clause,_,[],G,B}|Cs], Anno, Bs, Lf, Ef, RBs, FUVs) -> case guard(G, Bs, Lf, Ef) of true -> exprs(B, Bs, Lf, Ef, RBs, FUVs); - false -> if_clauses(Cs, Bs, Lf, Ef, RBs, FUVs) + false -> if_clauses(Cs, Anno, Bs, Lf, Ef, RBs, FUVs) end; -if_clauses([], _Bs, _Lf, _Ef, _RBs, _FUVs) -> - erlang:raise(error, if_clause, ?STACKTRACE). +if_clauses([], Anno, Bs, _Lf, Ef, RBs, _FUVs) -> + apply_error(if_clause, ?STACKTRACE, Anno, Bs, Ef, RBs). -%% try_clauses(Body, CaseClauses, CatchClauses, AfterBody, Bindings, +%% try_clauses(Body, CaseClauses, CatchClauses, AfterBody, Anno, Bindings, %% LocalFuncHandler, ExtFuncHandler, RBs) -try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs) -> - check_stacktrace_vars(Catches, Bs), +try_clauses(B, Cases, Catches, AB, Anno, Bs, Lf, Ef, RBs, FUVs) -> + check_stacktrace_vars(Catches, Anno, Bs, Ef, RBs), try exprs(B, Bs, Lf, Ef, none, FUVs) of {value,V,Bs1} when Cases =:= [] -> ret_expr(V, Bs1, RBs); @@ -941,7 +951,7 @@ try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs) -> {B2,Bs2} -> exprs(B2, Bs2, Lf, Ef, RBs, FUVs); nomatch -> - erlang:raise(error, {try_clause,V}, ?STACKTRACE) + apply_error({try_clause, V}, ?STACKTRACE, Anno, Bs, Ef, RBs) end catch Class:Reason:Stacktrace when Catches =:= [] -> @@ -962,36 +972,36 @@ try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs) -> end end. -check_stacktrace_vars([{clause,_,[{tuple,_,[_,_,STV]}],_,_}|Cs], Bs) -> + +check_stacktrace_vars([{clause,_,[{tuple,_,[_,_,STV]}],_,_}|Cs], Anno, Bs, Ef, RBs) -> case STV of {var,_,V} -> case binding(V, Bs) of {value, _} -> - erlang:raise(error, stacktrace_bound, ?STACKTRACE); + apply_error(stacktrace_bound, ?STACKTRACE, Anno, Bs, Ef, RBs); unbound -> - check_stacktrace_vars(Cs, Bs) + check_stacktrace_vars(Cs, Anno, Bs, Ef, RBs) end; _ -> - erlang:raise(error, - {illegal_stacktrace_variable,STV}, - ?STACKTRACE) + Reason = {illegal_stacktrace_variable,STV}, + apply_error(Reason, ?STACKTRACE, Anno, Bs, Ef, RBs) end; -check_stacktrace_vars([], _Bs) -> +check_stacktrace_vars([], _Anno, _Bs, _Ef, _RBs) -> ok. -%% case_clauses(Value, Clauses, Bindings, LocalFuncHandler, ExtFuncHandler, -%% RBs) +%% case_clauses(Value, Clauses, Anno, Bindings, LocalFuncHandler, +%% ExtFuncHandler, RBs) -case_clauses(Val, Cs, Bs, Lf, Ef, RBs, FUVs) -> +case_clauses(Val, Cs, Anno, Bs, Lf, Ef, RBs, FUVs) -> case match_clause(Cs, [Val], Bs, Lf, Ef) of {B, Bs1} -> exprs(B, Bs1, Lf, Ef, RBs, FUVs); nomatch -> - erlang:raise(error, {case_clause,Val}, ?STACKTRACE) + apply_error({case_clause,Val}, ?STACKTRACE, Anno, Bs, Ef, RBs) end. %% -%% receive_clauses(Clauses, Bindings, LocalFuncHnd,ExtFuncHnd, RBs) +%% receive_clauses(Clauses, Bindings, LocalFuncHnd, ExtFuncHnd, RBs) %% receive_clauses(Cs, Bs, Lf, Ef, RBs, FUVs) -> receive_clauses(infinity, Cs, unused, Bs, Lf, Ef, RBs, FUVs). @@ -1023,8 +1033,8 @@ receive_clauses(T, Cs, TB, Bs, Lf, Ef, RBs, FUVs) -> match_clause(Cs, Vs, Bs, Lf) -> match_clause(Cs, Vs, Bs, Lf, none). -match_clause([{clause,_,H,G,B}|Cs], Vals, Bs, Lf, Ef) -> - case match_list(H, Vals, Bs) of +match_clause([{clause,Anno,H,G,B}|Cs], Vals, Bs, Lf, Ef) -> + case match_list(H, Vals, Anno, Bs, Bs, Ef) of {match, Bs1} -> case guard(G, Bs1, Lf, Ef) of true -> {B, Bs1}; @@ -1062,7 +1072,7 @@ guard0([G|Gs], Bs0, Lf, Ef) -> {value,false,_} -> false end; false -> - erlang:raise(error, guard_expr, ?STACKTRACE) + apply_error(guard_expr, ?STACKTRACE, element(2, G), Bs0, Ef, none) end; guard0([], _Bs, _Lf, _Ef) -> true. @@ -1101,23 +1111,19 @@ type_test(record) -> is_record; type_test(map) -> is_map; type_test(Test) -> Test. - -%% match(Pattern, Term, Bindings) -> -%% {match,NewBindings} | nomatch +%% match(Pattern, Term, Anno, NewBindings, Bindings, ExternalFunHnd) -> +%% {match,NewBindings} | nomatch %% or erlang:error({illegal_pattern, Pattern}). -%% Try to match Pattern against Term with the current bindings. - -match(Pat, Term, Bs) -> - match(Pat, Term, Bs, Bs). - +%% +%% Try to match Pattern against Term with the current bindings. %% Bs are the bindings that are augmented with new bindings. BBs are %% the bindings used for "binsize" variables (in <<X:Y>>, Y is a %% binsize variable). -match(Pat, Term, Bs, BBs) -> - case catch match1(Pat, Term, Bs, BBs) of +match(Pat, Term, Anno, Bs, BBs, Ef) -> + case catch match1(Pat, Term, Bs, BBs, Ef) of invalid -> - erlang:raise(error, {illegal_pattern,to_term(Pat)}, ?STACKTRACE); + apply_error({illegal_pattern,to_term(Pat)}, ?STACKTRACE, Anno, Bs, Ef, none); Other -> Other end. @@ -1126,29 +1132,29 @@ string_to_conses([], _, Tail) -> Tail; string_to_conses([E|Rest], Anno, Tail) -> {cons, Anno, {integer, Anno, E}, string_to_conses(Rest, Anno, Tail)}. -match1({atom,_,A0}, A, Bs, _BBs) -> +match1({atom,_,A0}, A, Bs, _BBs, _Ef) -> case A of A0 -> {match,Bs}; _ -> throw(nomatch) end; -match1({integer,_,I0}, I, Bs, _BBs) -> +match1({integer,_,I0}, I, Bs, _BBs, _Ef) -> case I of I0 -> {match,Bs}; _ -> throw(nomatch) end; -match1({float,_,F0}, F, Bs, _BBs) -> +match1({float,_,F0}, F, Bs, _BBs, _Ef) -> case F of F0 -> {match,Bs}; _ -> throw(nomatch) end; -match1({char,_,C0}, C, Bs, _BBs) -> +match1({char,_,C0}, C, Bs, _BBs, _Ef) -> case C of C0 -> {match,Bs}; _ -> throw(nomatch) end; -match1({var,_,'_'}, _, Bs, _BBs) -> %Anonymous variable matches +match1({var,_,'_'}, _, Bs, _BBs, _Ef) -> %Anonymous variable matches {match,Bs}; % everything, no new bindings -match1({var,_,Name}, Term, Bs, _BBs) -> +match1({var,_,Name}, Term, Bs, _BBs, _Ef) -> case binding(Name, Bs) of {value,Term} -> {match,Bs}; @@ -1157,34 +1163,34 @@ match1({var,_,Name}, Term, Bs, _BBs) -> unbound -> {match,add_binding(Name, Term, Bs)} end; -match1({match,_,Pat1,Pat2}, Term, Bs0, BBs) -> - {match, Bs1} = match1(Pat1, Term, Bs0, BBs), - match1(Pat2, Term, Bs1, BBs); -match1({string,_,S0}, S, Bs, _BBs) -> +match1({match,_,Pat1,Pat2}, Term, Bs0, BBs, Ef) -> + {match, Bs1} = match1(Pat1, Term, Bs0, BBs, Ef), + match1(Pat2, Term, Bs1, BBs, Ef); +match1({string,_,S0}, S, Bs, _BBs, _Ef) -> case S of S0 -> {match,Bs}; _ -> throw(nomatch) end; -match1({nil,_}, Nil, Bs, _BBs) -> +match1({nil,_}, Nil, Bs, _BBs, _Ef) -> case Nil of [] -> {match,Bs}; _ -> throw(nomatch) end; -match1({cons,_,H,T}, [H1|T1], Bs0, BBs) -> - {match,Bs} = match1(H, H1, Bs0, BBs), - match1(T, T1, Bs, BBs); -match1({cons,_,_,_}, _, _Bs, _BBs) -> +match1({cons,_,H,T}, [H1|T1], Bs0, BBs, Ef) -> + {match,Bs} = match1(H, H1, Bs0, BBs, Ef), + match1(T, T1, Bs, BBs, Ef); +match1({cons,_,_,_}, _, _Bs, _BBs, _Ef) -> throw(nomatch); -match1({tuple,_,Elts}, Tuple, Bs, BBs) +match1({tuple,_,Elts}, Tuple, Bs, BBs, Ef) when length(Elts) =:= tuple_size(Tuple) -> - match_tuple(Elts, Tuple, 1, Bs, BBs); -match1({tuple,_,_}, _, _Bs, _BBs) -> + match_tuple(Elts, Tuple, 1, Bs, BBs, Ef); +match1({tuple,_,_}, _, _Bs, _BBs, _Ef) -> throw(nomatch); -match1({map,_,Fs}, #{}=Map, Bs, BBs) -> - match_map(Fs, Map, Bs, BBs); -match1({map,_,_}, _, _Bs, _BBs) -> +match1({map,_,Fs}, #{}=Map, Bs, BBs, Ef) -> + match_map(Fs, Map, Bs, BBs, Ef); +match1({map,_,_}, _, _Bs, _BBs, _Ef) -> throw(nomatch); -match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) -> +match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs, Ef) -> EvalFun = fun(E, Bs) -> case erl_lint:is_guard_expr(E) of true -> ok; @@ -1197,74 +1203,72 @@ match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) -> throw(invalid) end end, - eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), EvalFun); -match1({bin,_,_}, _, _Bs, _BBs) -> + ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end, + eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs, Ef), EvalFun, ErrorFun); +match1({bin,_,_}, _, _Bs, _BBs, _Ef) -> throw(nomatch); -match1({op,_,'++',{nil,_},R}, Term, Bs, BBs) -> - match1(R, Term, Bs, BBs); -match1({op,_,'++',{cons,Ai,{integer,A2,I},T},R}, Term, Bs, BBs) -> - match1({cons,Ai,{integer,A2,I},{op,Ai,'++',T,R}}, Term, Bs, BBs); -match1({op,_,'++',{cons,Ai,{char,A2,C},T},R}, Term, Bs, BBs) -> - match1({cons,Ai,{char,A2,C},{op,Ai,'++',T,R}}, Term, Bs, BBs); -match1({op,_,'++',{string,Ai,L},R}, Term, Bs, BBs) -> - match1(string_to_conses(L, Ai, R), Term, Bs, BBs); -match1({op,Anno,Op,A}, Term, Bs, BBs) -> +match1({op,_,'++',{nil,_},R}, Term, Bs, BBs, Ef) -> + match1(R, Term, Bs, BBs, Ef); +match1({op,_,'++',{cons,Ai,{integer,A2,I},T},R}, Term, Bs, BBs, Ef) -> + match1({cons,Ai,{integer,A2,I},{op,Ai,'++',T,R}}, Term, Bs, BBs, Ef); +match1({op,_,'++',{cons,Ai,{char,A2,C},T},R}, Term, Bs, BBs, Ef) -> + match1({cons,Ai,{char,A2,C},{op,Ai,'++',T,R}}, Term, Bs, BBs, Ef); +match1({op,_,'++',{string,Ai,L},R}, Term, Bs, BBs, Ef) -> + match1(string_to_conses(L, Ai, R), Term, Bs, BBs, Ef); +match1({op,Anno,Op,A}, Term, Bs, BBs, Ef) -> case partial_eval({op,Anno,Op,A}) of {op,Anno,Op,A} -> throw(invalid); X -> - match1(X, Term, Bs, BBs) + match1(X, Term, Bs, BBs, Ef) end; -match1({op,Anno,Op,L,R}, Term, Bs, BBs) -> +match1({op,Anno,Op,L,R}, Term, Bs, BBs, Ef) -> case partial_eval({op,Anno,Op,L,R}) of {op,Anno,Op,L,R} -> throw(invalid); X -> - match1(X, Term, Bs, BBs) + match1(X, Term, Bs, BBs, Ef) end; -match1(_, _, _Bs, _BBs) -> +match1(_, _, _Bs, _BBs, _Ef) -> throw(invalid). -match_fun(BBs) -> - fun(match, {L,R,Bs}) -> match1(L, R, Bs, BBs); +match_fun(BBs, Ef) -> + fun(match, {L,R,Bs}) -> match1(L, R, Bs, BBs, Ef); (binding, {Name,Bs}) -> binding(Name, Bs); (add_binding, {Name,Val,Bs}) -> add_binding(Name, Val, Bs) end. -match_tuple([E|Es], Tuple, I, Bs0, BBs) -> - {match,Bs} = match1(E, element(I, Tuple), Bs0, BBs), - match_tuple(Es, Tuple, I+1, Bs, BBs); -match_tuple([], _, _, Bs, _BBs) -> +match_tuple([E|Es], Tuple, I, Bs0, BBs, Ef) -> + {match,Bs} = match1(E, element(I, Tuple), Bs0, BBs, Ef), + match_tuple(Es, Tuple, I+1, Bs, BBs, Ef); +match_tuple([], _, _, Bs, _BBs, _Ef) -> {match,Bs}. -match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) -> +match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs, Ef) -> Vm = try {value, Ke, _} = expr(K, BBs), maps:get(Ke,Map) catch error:_ -> throw(nomatch) end, - {match, Bs} = match1(V, Vm, Bs0, BBs), - match_map(Fs, Map, Bs, BBs); -match_map([], _, Bs, _) -> + {match, Bs} = match1(V, Vm, Bs0, BBs, Ef), + match_map(Fs, Map, Bs, BBs, Ef); +match_map([], _, Bs, _, _) -> {match, Bs}. -%% match_list(PatternList, TermList, Bindings) -> +%% match_list(PatternList, TermList, Anno, NewBindings, Bindings, ExternalFunHnd) -> %% {match,NewBindings} | nomatch %% Try to match a list of patterns against a list of terms with the %% current bindings. -match_list(Ps, Ts, Bs) -> - match_list(Ps, Ts, Bs, Bs). - -match_list([P|Ps], [T|Ts], Bs0, BBs) -> - case match(P, T, Bs0, BBs) of - {match,Bs1} -> match_list(Ps, Ts, Bs1, BBs); +match_list([P|Ps], [T|Ts], Anno, Bs0, BBs, Ef) -> + case match(P, T, Anno, Bs0, BBs, Ef) of + {match,Bs1} -> match_list(Ps, Ts, Anno, Bs1, BBs, Ef); nomatch -> nomatch end; -match_list([], [], Bs, _BBs) -> +match_list([], [], _Anno, Bs, _BBs, _Ef) -> {match,Bs}; -match_list(_, _, _Bs, _BBs) -> +match_list(_, _, _Anno, _Bs, _BBs, _Ef) -> nomatch. %% new_bindings() @@ -1313,17 +1317,17 @@ add_bindings(Bs1, Bs2) -> foldl(fun ({Name,Val}, Bs) -> orddict:store(Name, Val, Bs) end, Bs2, orddict:to_list(Bs1)). -merge_bindings(Bs1, Bs2) when is_map(Bs1), is_map(Bs2) -> +merge_bindings(Bs1, Bs2, Anno, Ef) when is_map(Bs1), is_map(Bs2) -> maps:merge_with(fun (_K, V, V) -> V; - (_K, _, V) -> erlang:raise(error, {badmatch,V}, ?STACKTRACE) + (_K, _, V) -> apply_error({badmatch,V}, ?STACKTRACE, Anno, Bs1, Ef, none) end, Bs2, Bs1); -merge_bindings(Bs1, Bs2) -> +merge_bindings(Bs1, Bs2, Anno, Ef) -> foldl(fun ({Name,Val}, Bs) -> case orddict:find(Name, Bs) of {ok,Val} -> Bs; %Already with SAME value {ok,V1} -> - erlang:raise(error, {badmatch,V1}, ?STACKTRACE); + apply_error({badmatch,V1}, ?STACKTRACE, Anno, Bs1, Ef, none); error -> orddict:store(Name, Val, Bs) end end, Bs2, orddict:to_list(Bs1)). @@ -1413,10 +1417,10 @@ expr_fixup([E0|Es0]) -> expr_fixup(T) -> T. -string_fixup(Ann, String, Token) -> - Text = erl_anno:text(Ann), +string_fixup(Anno, String, Token) -> + Text = erl_anno:text(Anno), FixupTag = fixup_tag(Text, String), - fixup_ast(FixupTag, Ann, String, Token). + fixup_ast(FixupTag, Anno, String, Token). reset_token_anno(Tokens) -> [setelement(2, T, (reset_anno())(element(2, T))) || T <- Tokens]. @@ -1503,7 +1507,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 33569d4a8d..f562c6d50a 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -1397,7 +1397,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl index 966bc65902..09ec175dc4 100644 --- a/lib/stdlib/src/eval_bits.erl +++ b/lib/stdlib/src/eval_bits.erl @@ -22,8 +22,8 @@ %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). --export([expr_grp/3,expr_grp/5,match_bits/6, - match_bits/7,bin_gen/6]). +-export([expr_grp/3,expr_grp/4,match_bits/6, + match_bits/7,bin_gen/6,bin_gen/7]). -define(STACKTRACE, element(2, erlang:process_info(self(), current_stacktrace))). @@ -40,11 +40,13 @@ %% lookup a variable in the bindings, or add a new binding %% %% @type field(). Represents a field in a "bin". +%% +%% @type errorfun(). A closure invoked in case of errors. %%% Part 1: expression evaluation (binary construction) %% @spec expr_grp(Fields::[field()], Bindings::bindings(), -%% EvalFun::evalfun(), term(), term()) -> +%% EvalFun::evalfun(), ErrorFun::errorfun()) -> %% {value, binary(), bindings()} %% %% @doc Returns a tuple with {value,Bin,Bs} where Bin is the binary @@ -52,29 +54,23 @@ %% contains the present bindings. This function can also throw an %% exception if the construction fails. -expr_grp(Fields, Bindings, EvalFun, [], _) -> - expr_grp(Fields, Bindings, EvalFun, <<>>); -expr_grp(Fields, Bindings, EvalFun, ListOfBits, _) -> - Bin = convert_list(ListOfBits), - expr_grp(Fields, Bindings, EvalFun, Bin). - -convert_list(List) -> - << <<X:1>> || X <- List >>. +expr_grp(Fields, Bindings, EvalFun, ErrorFun) -> + expr_grp(Fields, Bindings, EvalFun, ErrorFun, <<>>). expr_grp(Fields, Bindings, EvalFun) -> - expr_grp(Fields, Bindings, EvalFun, <<>>). + expr_grp(Fields, Bindings, EvalFun, fun default_error/3, <<>>). -expr_grp(FS, Bs0, Ef, Acc) -> +expr_grp(FS, Bs0, EvalFun, ErrorFun, Acc) -> %% Separate the evaluation of values, sizes, and TLS:s from the %% creation of the binary in order to mimic compiled code when it %% comes to loops and failures. - {ListOfEvalField,Bs1} = expr_grp1(FS, Bs0, Ef, []), + {ListOfEvalField,Bs1} = expr_grp1(FS, Bs0, EvalFun, ErrorFun, []), {value,create_binary(ListOfEvalField, Acc),Bs1}. -expr_grp1([Field | FS], Bs0, Ef, ListOfEvalField) -> - {EvalField,Bs} = eval_field(Field, Bs0, Ef), - expr_grp1(FS, Bs, Ef, [EvalField|ListOfEvalField]); -expr_grp1([], Bs, _Ef, ListOfFieldData) -> +expr_grp1([Field | FS], Bs0, EvalFun, ErrorFun, ListOfEvalField) -> + {EvalField,Bs} = eval_field(Field, Bs0, EvalFun, ErrorFun), + expr_grp1(FS, Bs, EvalFun, ErrorFun, [EvalField|ListOfEvalField]); +expr_grp1([], Bs, _EvalFun, _ErrorFun, ListOfFieldData) -> {lists:reverse(ListOfFieldData),Bs}. create_binary([EvalField|ListOfEvalField], Acc) -> @@ -83,44 +79,44 @@ create_binary([EvalField|ListOfEvalField], Acc) -> create_binary([], Acc) -> Acc. -eval_field({bin_element, _, {string, _, S}, {integer,_,8}, [integer,{unit,1},unsigned,big]}, Bs0, _Fun) -> +eval_field({bin_element, _, {string, _, S}, {integer,_,8}, [integer,{unit,1},unsigned,big]}, Bs0, _Fun, _ErrorFun) -> Latin1 = [C band 16#FF || C <- S], {fun() -> list_to_binary(Latin1) end,Bs0}; -eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun) -> +eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun, _ErrorFun) -> Latin1 = [C band 16#FF || C <- S], {fun() ->list_to_binary(Latin1) end,Bs0}; -eval_field({bin_element, Anno, {string, _, S}, Size0, Options0}, Bs0, Fun) -> +eval_field({bin_element, Anno, {string, _, S}, Size0, Options0}, Bs0, Fun, ErrorFun) -> {Size1,[Type,{unit,Unit},Sign,Endian]} = - make_bit_type(Anno, Size0, Options0), + make_bit_type(Anno, Size0, Options0, ErrorFun), {value,Size,Bs1} = Fun(Size1, Bs0), {fun() -> - Res = << <<(eval_exp_field1(C, Size, Unit, - Type, Endian, Sign))/bitstring>> || + Res = << <<(eval_exp_field1(Anno, C, Size, Unit, + Type, Endian, Sign, ErrorFun))/bitstring>> || C <- S >>, case S of "" -> % find errors also when the string is empty - _ = eval_exp_field1(0, Size, Unit, Type, Endian, Sign), + _ = eval_exp_field1(Anno, 0, Size, Unit, Type, Endian, Sign, ErrorFun), ok; _ -> ok end, Res end,Bs1}; -eval_field({bin_element,Anno,E,Size0,Options0}, Bs0, Fun) -> +eval_field({bin_element,Anno,E,Size0,Options0}, Bs0, Fun, ErrorFun) -> {value,V,Bs1} = Fun(E, Bs0), {Size1,[Type,{unit,Unit},Sign,Endian]} = - make_bit_type(Anno, Size0, Options0), + make_bit_type(Anno, Size0, Options0, ErrorFun), {value,Size,Bs} = Fun(Size1, Bs1), - {fun() -> eval_exp_field1(V, Size, Unit, Type, Endian, Sign) end,Bs}. + {fun() -> eval_exp_field1(Anno, V, Size, Unit, Type, Endian, Sign, ErrorFun) end,Bs}. -eval_exp_field1(V, Size, Unit, Type, Endian, Sign) -> +eval_exp_field1(Anno, V, Size, Unit, Type, Endian, Sign, ErrorFun) -> try eval_exp_field(V, Size, Unit, Type, Endian, Sign) catch error:system_limit -> - erlang:raise(error, system_limit, ?STACKTRACE); + ErrorFun(Anno, system_limit, ?STACKTRACE); error:_ -> - erlang:raise(error, badarg, ?STACKTRACE) + ErrorFun(Anno, badarg, ?STACKTRACE) end. eval_exp_field(Val, Size, Unit, integer, little, signed) -> @@ -169,7 +165,8 @@ eval_exp_field(Val, Size, Unit, binary, _, _) -> %%% Part 2: matching in binary comprehensions %% @spec bin_gen(BinPattern::{bin,integer(),[field()]}, Bin::binary(), %% GlobalEnv::bindings(), LocalEnv::bindings(), -%% MatchFun::matchfun(), EvalFun::evalfun()) -> +%% MatchFun::matchfun(), EvalFun::evalfun(), +%% ErrorFun::errorfun()) -> %% {match, binary(), bindings()} | {nomatch, binary()} | done %% %% @doc Used to perform matching in a comprehension. If the match @@ -178,25 +175,28 @@ eval_exp_field(Val, Size, Unit, binary, _, _) -> %% If nothing remains of the binary the atom 'done' is returned. bin_gen({bin,_,Fs}, Bin, Bs0, BBs0, Mfun, Efun) -> - bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, true). + bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, fun default_error/3, true). -bin_gen([F|Fs], Bin, Bs0, BBs0, Mfun, Efun, Flag) +bin_gen({bin,_,Fs}, Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) -> + bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, ErrorFun, true). + +bin_gen([F|Fs], Bin, Bs0, BBs0, Mfun, Efun, ErrorFun, Flag) when is_function(Mfun, 2), is_function(Efun, 2) -> - case bin_gen_field(F, Bin, Bs0, BBs0, Mfun, Efun) of + case bin_gen_field(F, Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) of {match,Bs,BBs,Rest} -> - bin_gen(Fs, Rest, Bs, BBs, Mfun, Efun, Flag); + bin_gen(Fs, Rest, Bs, BBs, Mfun, Efun, ErrorFun, Flag); {nomatch,Rest} -> - bin_gen(Fs, Rest, Bs0, BBs0, Mfun, Efun, false); + bin_gen(Fs, Rest, Bs0, BBs0, Mfun, Efun, ErrorFun, false); done -> done end; -bin_gen([], Bin, Bs0, _BBs0, _Mfun, _Efun, true) -> +bin_gen([], Bin, Bs0, _BBs0, _Mfun, _Efun, _ErrorFun, true) -> {match, Bin, Bs0}; -bin_gen([], Bin, _Bs0, _BBs0, _Mfun, _Efun, false) -> +bin_gen([], Bin, _Bs0, _BBs0, _Mfun, _Efun, _ErrorFun, false) -> {nomatch, Bin}. bin_gen_field({bin_element,_,{string,_,S},default,default}, - Bin, Bs, BBs, _Mfun, _Efun) -> + Bin, Bs, BBs, _Mfun, _Efun, _ErrorFun) -> Bits = try list_to_binary(S) catch _:_ -> <<>> end, @@ -210,9 +210,9 @@ bin_gen_field({bin_element,_,{string,_,S},default,default}, done end; bin_gen_field({bin_element,Anno,{string,SAnno,S},Size0,Options0}, - Bin0, Bs0, BBs0, Mfun, Efun) -> + Bin0, Bs0, BBs0, Mfun, Efun, ErrorFun) -> {Size1, [Type,{unit,Unit},Sign,Endian]} = - make_bit_type(Anno, Size0, Options0), + make_bit_type(Anno, Size0, Options0, ErrorFun), case catch Efun(Size1, BBs0) of {value, Size, _BBs} -> % F = fun(C, Bin, Bs, BBs) -> @@ -222,9 +222,9 @@ bin_gen_field({bin_element,Anno,{string,SAnno,S},Size0,Options0}, bin_gen_field_string(S, Bin0, Bs0, BBs0, F) end; bin_gen_field({bin_element,Anno,VE,Size0,Options0}, - Bin, Bs0, BBs0, Mfun, Efun) -> + Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) -> {Size1, [Type,{unit,Unit},Sign,Endian]} = - make_bit_type(Anno, Size0, Options0), + make_bit_type(Anno, Size0, Options0, ErrorFun), V = erl_eval:partial_eval(VE), NewV = coerce_to_float(V, Type), case catch Efun(Size1, BBs0) of @@ -264,41 +264,42 @@ bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun) -> %%% Part 3: binary pattern matching %% @spec match_bits(Fields::[field()], Bin::binary(), %% GlobalEnv::bindings(), LocalEnv::bindings(), -%% MatchFun::matchfun(),EvalFun::evalfun(), term()) -> -%% {match, bindings()} +%% MatchFun::matchfun(),EvalFun::evalfun(), +%% ErrorFun::errorfun()) -> +%% {match, bindings()} %% @doc Used to perform matching. If the match succeeds a new %% environment is returned. If the match have some syntactic or %% semantic problem which would have been caught at compile time this %% function throws 'invalid', if the matching fails for other reasons %% the function throws 'nomatch' -match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, _) -> - match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun). +match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun) -> + match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, fun default_error/3). -match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun) - when is_function(Mfun, 2), is_function(Efun, 2) -> - case catch match_bits_1(Fs, Bin, Bs0, BBs, Mfun, Efun) of +match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, ErrorFun) + when is_function(Mfun, 2), is_function(Efun, 2), is_function(ErrorFun, 3) -> + case catch match_bits_1(Fs, Bin, Bs0, BBs, Mfun, Efun, ErrorFun) of {match,Bs} -> {match,Bs}; invalid -> throw(invalid); _Error -> throw(nomatch) end. -match_bits_1([], <<>>, Bs, _BBs, _Mfun, _Efun) -> +match_bits_1([], <<>>, Bs, _BBs, _Mfun, _Efun, _ErrorFun) -> {match,Bs}; -match_bits_1([F|Fs], Bits0, Bs0, BBs0, Mfun, Efun) -> - {Bs,BBs,Bits} = match_field_1(F, Bits0, Bs0, BBs0, Mfun, Efun), - match_bits_1(Fs, Bits, Bs, BBs, Mfun, Efun). +match_bits_1([F|Fs], Bits0, Bs0, BBs0, Mfun, Efun, ErrorFun) -> + {Bs,BBs,Bits} = match_field_1(F, Bits0, Bs0, BBs0, Mfun, Efun, ErrorFun), + match_bits_1(Fs, Bits, Bs, BBs, Mfun, Efun, ErrorFun). match_field_1({bin_element,_,{string,_,S},default,default}, - Bin, Bs, BBs, _Mfun, _Efun) -> + Bin, Bs, BBs, _Mfun, _Efun, _ErrorFun) -> Bits = list_to_binary(S), % fails if there are characters > 255 Size = byte_size(Bits), <<Bits:Size/binary,Rest/binary-unit:1>> = Bin, {Bs,BBs,Rest}; match_field_1({bin_element,Anno,{string,SAnno,S},Size0,Options0}, - Bin0, Bs0, BBs0, Mfun, Efun) -> + Bin0, Bs0, BBs0, Mfun, Efun, ErrorFun) -> {Size1, [Type,{unit,Unit},Sign,Endian]} = - make_bit_type(Anno, Size0, Options0), + make_bit_type(Anno, Size0, Options0, ErrorFun), Size2 = erl_eval:partial_eval(Size1), {value, Size, _BBs} = Efun(Size2, BBs0), F = fun(C, Bin, Bs, BBs) -> @@ -307,9 +308,9 @@ match_field_1({bin_element,Anno,{string,SAnno,S},Size0,Options0}, end, match_field_string(S, Bin0, Bs0, BBs0, F); match_field_1({bin_element,Anno,VE,Size0,Options0}, - Bin, Bs0, BBs0, Mfun, Efun) -> + Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) -> {Size1, [Type,{unit,Unit},Sign,Endian]} = - make_bit_type(Anno, Size0, Options0), + make_bit_type(Anno, Size0, Options0, ErrorFun), V = erl_eval:partial_eval(VE), NewV = coerce_to_float(V, Type), Size2 = erl_eval:partial_eval(Size1), @@ -410,15 +411,18 @@ get_float(Bin, Size, big) -> {Val,Rest}. %% Identical to the one in sys_pre_expand. -make_bit_type(Anno, default, Type0) -> +make_bit_type(Anno, default, Type0, ErrorFun) -> case erl_bits:set_bit_type(default, Type0) of {ok,all,Bt} -> {{atom,Anno,all},erl_bits:as_list(Bt)}; {ok,undefined,Bt} -> {{atom,Anno,undefined},erl_bits:as_list(Bt)}; {ok,Size,Bt} -> {{integer,Anno,Size},erl_bits:as_list(Bt)}; - {error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE) + {error,Reason} -> ErrorFun(Anno, Reason, ?STACKTRACE) end; -make_bit_type(_Anno, Size, Type0) -> %Size evaluates to an integer or 'all' +make_bit_type(Anno, Size, Type0, ErrorFun) -> %Size evaluates to an integer or 'all' case erl_bits:set_bit_type(Size, Type0) of {ok,Size,Bt} -> {Size,erl_bits:as_list(Bt)}; - {error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE) + {error,Reason} -> ErrorFun(Anno, Reason, ?STACKTRACE) end. + +default_error(_Anno, Reason, Stacktrace) -> + erlang:raise(error, Reason, Stacktrace). diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index b9fa534a69..51d4600d40 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -1126,7 +1126,7 @@ normalise({bin,_,Fs}) -> eval_bits:expr_grp(Fs, [], fun(E, _) -> {value, normalise(E), []} - end, [], true), + end), B; normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index f4d58e61c5..16a92f7ff5 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -43,6 +43,7 @@ otp_13228/1, otp_14826/1, funs/1, + custom_stacktrace/1, try_catch/1, eval_expr_5/1, zero_width/1, @@ -91,7 +92,7 @@ all() -> simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, otp_6787, otp_6977, otp_7550, otp_8133, otp_10622, otp_13228, otp_14826, - funs, try_catch, eval_expr_5, zero_width, + funs, custom_stacktrace, try_catch, eval_expr_5, zero_width, eep37, eep43, otp_15035, otp_16439, otp_14708, otp_16545, otp_16865, binary_skip]. @@ -1000,7 +1001,7 @@ otp_14826(_Config) -> backtrace_check("fun(P) when is_pid(P) -> true end(a).", function_clause, [{erl_eval,'-inside-an-interpreted-fun-',[a],[]}, - {erl_eval,eval_fun,7}, + {erl_eval,eval_fun,8}, ?MODULE]), backtrace_check("B.", {unbound_var, 'B'}, @@ -1012,11 +1013,11 @@ otp_14826(_Config) -> backtrace_check("1/0.", badarith, [{erlang,'/',[1,0],[]}, - {erl_eval,do_apply,6}]), + {erl_eval,do_apply,7}]), backtrace_catch("catch 1/0.", badarith, [{erlang,'/',[1,0],[]}, - {erl_eval,do_apply,6}]), + {erl_eval,do_apply,7}]), check(fun() -> catch exit(foo) end, "catch exit(foo).", {'EXIT', foo}), @@ -1026,33 +1027,33 @@ otp_14826(_Config) -> backtrace_check("try 1/0 after foo end.", badarith, [{erlang,'/',[1,0],[]}, - {erl_eval,do_apply,6}]), + {erl_eval,do_apply,7}]), backtrace_catch("catch (try 1/0 after foo end).", badarith, [{erlang,'/',[1,0],[]}, - {erl_eval,do_apply,6}]), + {erl_eval,do_apply,7}]), backtrace_catch("try catch 1/0 after foo end.", badarith, [{erlang,'/',[1,0],[]}, - {erl_eval,do_apply,6}]), + {erl_eval,do_apply,7}]), backtrace_check("try a of b -> bar after foo end.", {try_clause,a}, - [{erl_eval,try_clauses,9}]), + [{erl_eval,try_clauses,10}]), check(fun() -> X = try foo:bar() catch A:B:C -> {A,B} end, X end, "try foo:bar() catch A:B:C -> {A,B} end.", {error, undef}), backtrace_check("C = 4, try foo:bar() catch A:B:C -> {A,B,C} end.", stacktrace_bound, - [{erl_eval,check_stacktrace_vars,2}, - {erl_eval,try_clauses,9}], + [{erl_eval,check_stacktrace_vars,5}, + {erl_eval,try_clauses,10}], none, none), backtrace_catch("catch (try a of b -> bar after foo end).", {try_clause,a}, - [{erl_eval,try_clauses,9}]), + [{erl_eval,try_clauses,10}]), backtrace_check("try 1/0 catch exit:a -> foo end.", badarith, [{erlang,'/',[1,0],[]}, - {erl_eval,do_apply,6}]), + {erl_eval,do_apply,7}]), Es = [{'try',1,[{call,1,{remote,1,{atom,1,foo},{atom,1,bar}},[]}], [], [{clause,1,[{tuple,1,[{var,1,'A'},{var,1,'B'},{atom,1,'C'}]}], @@ -1062,8 +1063,8 @@ otp_14826(_Config) -> ct:fail(stacktrace_variable) catch error:{illegal_stacktrace_variable,{atom,1,'C'}}:S -> - [{erl_eval,check_stacktrace_vars,2,_}, - {erl_eval,try_clauses,9,_}|_] = S + [{erl_eval,check_stacktrace_vars,5,_}, + {erl_eval,try_clauses,10,_}|_] = S end, backtrace_check("{1,1} = {A = 1, A = 2}.", {badmatch, 1}, @@ -1073,40 +1074,40 @@ otp_14826(_Config) -> [{erl_eval,guard0,4}], none, none), backtrace_check("case a of foo() -> ok end.", {illegal_pattern,{call,1,{atom,1,foo},[]}}, - [{erl_eval,match,4}], none, none), + [{erl_eval,match,6}], none, none), backtrace_check("case a of b -> ok end.", {case_clause,a}, - [{erl_eval,case_clauses,7}, ?MODULE]), + [{erl_eval,case_clauses,8}, ?MODULE]), backtrace_check("if a =:= b -> ok end.", if_clause, - [{erl_eval,if_clauses,6}, ?MODULE]), + [{erl_eval,if_clauses,7}, ?MODULE]), backtrace_check("fun A(b) -> ok end(a).", function_clause, [{erl_eval,'-inside-an-interpreted-fun-',[a],[]}, - {erl_eval,eval_named_fun,9}, + {erl_eval,eval_named_fun,10}, ?MODULE]), backtrace_check("[A || A <- a].", {bad_generator, a}, - [{erl_eval,eval_generate,7}, {erl_eval, eval_lc, 7}]), + [{erl_eval,eval_generate,8}, {erl_eval, eval_lc, 7}]), backtrace_check("<< <<A>> || <<A>> <= a>>.", {bad_generator, a}, - [{erl_eval,eval_b_generate,7}, {erl_eval, eval_bc, 7}]), + [{erl_eval,eval_b_generate,8}, {erl_eval, eval_bc, 7}]), backtrace_check("[A || A <- [1], begin a end].", {bad_filter, a}, - [{erl_eval,eval_filter,7}, {erl_eval, eval_generate, 7}]), + [{erl_eval,eval_filter,7}, {erl_eval, eval_generate, 8}]), fun() -> {'EXIT', {{badarity, {_Fun, []}}, BT}} = (catch parse_and_run("fun(A) -> A end().")), - check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT) + check_backtrace([{erl_eval,do_apply,6}, ?MODULE], BT) end(), fun() -> {'EXIT', {{badarity, {_Fun, []}}, BT}} = (catch parse_and_run("fun F(A) -> A end().")), - check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT) + check_backtrace([{erl_eval,do_apply,6}, ?MODULE], BT) end(), backtrace_check("foo().", undef, - [{erl_eval,foo,0},{erl_eval,local_func,7}], + [{erl_eval,foo,0},{erl_eval,local_func,8}], none, none), backtrace_check("a orelse false.", {badarg, a}, @@ -1141,16 +1142,16 @@ otp_14826(_Config) -> %% eval_bits backtrace_check("<<100:8/bitstring>>.", badarg, - [{eval_bits,eval_exp_field1,6}, + [{eval_bits,eval_exp_field1,8}, eval_bits,eval_bits,erl_eval]), backtrace_check("<<100:8/foo>>.", {undefined_bittype,foo}, - [{eval_bits,make_bit_type,3},eval_bits, + [{eval_bits,make_bit_type,4},eval_bits, eval_bits,eval_bits], none, none), backtrace_check("B = <<\"foo\">>, <<B/binary-unit:7>>.", badarg, - [{eval_bits,eval_exp_field1,6}, + [{eval_bits,eval_exp_field1,8}, eval_bits,eval_bits,erl_eval], none, none), ok. @@ -1172,13 +1173,143 @@ simple1() -> WillNeverHappen -> WillNeverHappen end. +custom_stacktrace(Config) when is_list(Config) -> + EFH = {value, fun custom_stacktrace_eval_handler/3}, + + backtrace_check("1 + atom.", badarith, + [{erlang,'+',[1,atom]}, mystack(1)], none, EFH), + backtrace_check("\n1 + atom.", badarith, + [{erlang,'+',[1,atom]}, mystack(2)], none, EFH), + + backtrace_check("lists:flatten(atom).", function_clause, + [{lists,flatten,[atom]}, mystack(1)], none, EFH), + + backtrace_check("invalid andalso true.", {badarg, invalid}, + [mystack(1)], none, EFH), + backtrace_check("invalid orelse true.", {badarg, invalid}, + [mystack(1)], none, EFH), + + backtrace_check("invalid = valid.", {badmatch, valid}, + [erl_eval, mystack(1)], none, EFH), + + backtrace_check("1:2.", {badexpr, ':'}, + [erl_eval, mystack(1)], none, EFH), + + backtrace_check("Unknown.", {unbound, 'Unknown'}, + [erl_eval, mystack(1)], none, EFH), + + backtrace_check("#unknown{}.", {undef_record,unknown}, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("#unknown{foo=bar}.", {undef_record,unknown}, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("#unknown.index.", {undef_record,unknown}, + [erl_eval, mystack(1)], none, EFH), + + backtrace_check("fun foo/2.", undef, + [{erl_eval, foo, 2}, erl_eval, mystack(1)], none, EFH), + backtrace_check("foo(1, 2).", undef, + [{erl_eval, foo, 2}, erl_eval, mystack(1)], none, EFH), + + fun() -> + {'EXIT', {{badarity, {_Fun, []}}, BT}} = + (catch parse_and_run("fun(A) -> A end().", none, EFH)), + check_backtrace([erl_eval, mystack(1)], BT) + end(), + + fun() -> + {'EXIT', {{badarity, {_Fun, []}}, BT}} = + (catch parse_and_run("fun F(A) -> A end().", none, EFH)), + check_backtrace([erl_eval, mystack(1)], BT) + end(), + + backtrace_check("[X || X <- 1].", {bad_generator, 1}, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("[X || <<X>> <= 1].", {bad_generator, 1}, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("<<X || X <- 1>>.", {bad_generator, 1}, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("<<X || <<X>> <= 1>>.", {bad_generator, 1}, + [erl_eval, mystack(1)], none, EFH), + + backtrace_check("if false -> true end.", if_clause, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("case 0 of 1 -> true end.", {case_clause, 0}, + [erl_eval, mystack(1)], none, EFH), + backtrace_check("try 0 of 1 -> true after ok end.", {try_clause, 0}, + [mystack(1)], none, EFH), + + backtrace_check("fun(0) -> 1 end(1).", function_clause, + [{erl_eval,'-inside-an-interpreted-fun-', [1]}, erl_eval, mystack(1)], + none, EFH), + backtrace_check("fun F(0) -> 1 end(1).", function_clause, + [{erl_eval,'-inside-an-interpreted-fun-', [1]}, erl_eval, mystack(1)], + none, EFH), + + fun() -> + {'EXIT', {{illegal_pattern,_}, BT}} = + (catch parse_and_run("make_ref() = 1.", none, EFH)), + check_backtrace([erl_eval, mystack(1)], BT) + end(), + + %% eval_bits + backtrace_check("<<100:8/bitstring>>.", + badarg, + [{eval_bits,eval_exp_field1,8}, mystack(1)], + none, EFH), + backtrace_check("<<100:8/foo>>.", + {undefined_bittype,foo}, + [{eval_bits,make_bit_type,4}, mystack(1)], + none, EFH), + backtrace_check("B = <<\"foo\">>, <<B/binary-unit:7>>.", + badarg, + [{eval_bits,eval_exp_field1,8}, mystack(1)], + none, EFH), + + ok. + +mystack(Line) -> + {my_module, my_function, 0, [{file, "evaluator"}, {line, Line}]}. + +custom_stacktrace_eval_handler(Ann, FunOrModFun, Args) -> + try + case FunOrModFun of + {Mod, Fun} -> apply(Mod, Fun, Args); + Fun -> apply(Fun, Args) + end + catch + Kind:Reason:Stacktrace -> + %% Take everything up to the evaluation function + Pruned = + lists:takewhile(fun + ({erl_eval_SUITE,backtrace_check,5,_}) -> false; + (_) -> true + end, Stacktrace), + %% Now we prune any shared code path from erl_eval + {current_stacktrace, Current} = + erlang:process_info(self(), current_stacktrace), + Reversed = drop_common(lists:reverse(Current), lists:reverse(Pruned)), + Location = [{file, "evaluator"}, {line, erl_anno:line(Ann)}], + %% Add our file+line information at the bottom + Custom = lists:reverse([{my_module, my_function, 0, Location} | Reversed]), + erlang:raise(Kind, Reason, Custom) + end. + +drop_common([H | T1], [H | T2]) -> drop_common(T1, T2); +drop_common([H | T1], T2) -> drop_common(T1, T2); +drop_common([], [{?MODULE, custom_stacktrace_eval_handler, _, _} | T2]) -> T2; +drop_common([], T2) -> T2. + %% Simple cases, just to cover some code. funs(Config) when is_list(Config) -> do_funs(none, none), do_funs(lfh(), none), + do_funs(none, efh()), do_funs(lfh(), efh()), + do_funs(none, ann_efh()), + do_funs(lfh(), ann_efh()), error_check("nix:foo().", {access_not_allowed,nix}, lfh(), efh()), + error_check("nix:foo().", {access_not_allowed,nix}, lfh(), ann_efh()), error_check("bar().", undef, none, none), check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end, @@ -1217,6 +1348,15 @@ funs(Config) when is_list(Config) -> error_check("apply(timer, sleep, [1]).", got_it, none, EFH), error_check("begin F = fun(T) -> timer:sleep(T) end,F(1) end.", got_it, none, EFH), + + AnnEF = fun(1, {timer,sleep}, As) when length(As) == 1 -> exit({got_it,sleep}); + (1, {M,F}, As) -> apply(M, F, As) + end, + AnnEFH = {value, AnnEF}, + error_check("apply(timer, sleep, [1]).", got_it, none, AnnEFH), + error_check("begin F = fun(T) -> timer:sleep(T) end,F(1) end.", + got_it, none, AnnEFH), + error_check("fun c/1.", undef), error_check("fun a:b/0().", undef), @@ -1398,6 +1538,9 @@ local_func_value(F, As) when is_atom(F) -> efh() -> {value, fun(F, As) -> external_func(F, As) end}. +ann_efh() -> + {value, fun(_Ann, F, As) -> external_func(F, As) end}. + external_func({M,_}, _As) when M == nix -> exit({{access_not_allowed,M},[mfa]}); external_func(F, As) when is_function(F) -> diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 56f1bb5b6f..89b745057f 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -256,6 +256,8 @@ restricted_local(Config) when is_list(Config) -> local_allowed(_,_,State) -> {false,State}. + non_local_allowed({erlang,raise},[error, _, _],State) -> + {true,State}; non_local_allowed({shell,stop_restricted},[],State) -> {true,State}; non_local_allowed(_,_,State) -> diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 354b65c9a3..f62cfb4108 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -7310,7 +7310,7 @@ concrete(Node) -> eval_bits:expr_grp(Fs, [], fun(F, _) -> {value, concrete(F), []} - end, [], true), + end), B; arity_qualifier -> A = erl_syntax:arity_qualifier_argument(Node), -- 2.34.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor