aboutsummaryrefslogtreecommitdiffstats
path: root/lib/debugger
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2010-04-09 15:50:17 +0200
committerBjörn Gustavsson <[email protected]>2011-11-07 13:57:03 +0100
commitff432e262e65243cbc983fcb002527f8fae8c78b (patch)
tree1966b14f94663375eb0333c3e2d707aab501a960 /lib/debugger
parent33c08d87a9395ae088eebcad9fb62193d26b6846 (diff)
downloadotp-ff432e262e65243cbc983fcb002527f8fae8c78b.tar.gz
otp-ff432e262e65243cbc983fcb002527f8fae8c78b.tar.bz2
otp-ff432e262e65243cbc983fcb002527f8fae8c78b.zip
EEP-23: Allow variables in fun M:F/A
Currently, the external fun syntax "fun M:F/A" only supports literals. That is, "fun lists:reverse/1" is allowed but not "fun M:F/A". In many real-life situations, some or all of M, F, A are not known until run-time, and one is forced to either use the undocumented erlang:make_fun/3 BIF or to use a "tuple fun" (which is deprecated). EEP-23 suggests that the parser (erl_parse) should immediately transform "fun M:F/A" to "erlang:make_fun(M, F, A)". We have not followed that approach in this implementation, because we want the abstract code to mirror the source code as closely as possible, and we also consider erlang:make_fun/3 to be an implementation detail that we might want to remove in the future. Instead, we will change the abstract format for "fun M:F/A" (in a way that is not backwards compatible), and while we are at it, we will move the translation from "fun M:F/A" to "erlang:make_fun(M, F, A)" from sys_pre_expand down to the v3_core pass. We will also update the debugger and xref to use the new format. We did consider making the abstract format backward compatible if no variables were used in the fun, but decided against it. Keeping it backward compatible would mean that there would be different abstract formats for the no-variable and variable case, and tools would have to handle both formats, probably forever. Reference: http://www.erlang.org/eeps/eep-0023.html
Diffstat (limited to 'lib/debugger')
-rw-r--r--lib/debugger/src/dbg_ieval.erl15
-rw-r--r--lib/debugger/src/dbg_iload.erl8
-rw-r--r--lib/debugger/test/fun_SUITE.erl52
3 files changed, 72 insertions, 3 deletions
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index df725ed9e5..2e88c35741 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -768,6 +768,21 @@ expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) ->
end,
{value,Fun,Bs};
+%% Construct an external fun.
+expr({make_ext_fun,Line,MFA0}, Bs0, Ieval0) ->
+ {[M,F,A],Bs} = eval_list(MFA0, Bs0, Ieval0),
+ try erlang:make_fun(M, F, A) of
+ Value ->
+ {value,Value,Bs}
+ catch
+ error:badarg ->
+ Ieval1 = Ieval0#ieval{line=Line},
+ Ieval2 = dbg_istk:push(Bs0, Ieval1, false),
+ Ieval = Ieval2#ieval{module=erlang,function=make_fun,
+ arguments=[M,F,A],line=-1},
+ exception(error, badarg, Bs, Ieval, true)
+ end;
+
%% Common test adaptation
expr({call_remote,0,ct_line,line,As0,Lc}, Bs0, Ieval0) ->
{As,_Bs} = eval_list(As0, Bs0, Ieval0),
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index ce5631e45f..3c95ef8068 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -369,6 +369,14 @@ expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}, _Lc) ->
As = new_vars(A, Line),
Cs = [{clause,Line,As,[],[{local_call,Line,F,As,true}]}],
{make_fun,Line,Name,Cs};
+expr({'fun',Line,{function,{atom,_,M},{atom,_,F},{integer,_,A}}}, _Lc)
+ when 0 =< A, A =< 255 ->
+ %% New format in R15 for fun M:F/A (literal values).
+ {value,Line,erlang:make_fun(M, F, A)};
+expr({'fun',Line,{function,M,F,A}}, _Lc) ->
+ %% New format in R15 for fun M:F/A (one or more variables).
+ MFA = expr_list([M,F,A]),
+ {make_ext_fun,Line,MFA};
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}, _Lc) ->
{dbg,Line,self,[]};
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}, _Lc) ->
diff --git a/lib/debugger/test/fun_SUITE.erl b/lib/debugger/test/fun_SUITE.erl
index 8103d9c692..a06cdc7165 100644
--- a/lib/debugger/test/fun_SUITE.erl
+++ b/lib/debugger/test/fun_SUITE.erl
@@ -24,8 +24,10 @@
init_per_testcase/2,end_per_testcase/2,
init_per_suite/1,end_per_suite/1,
good_call/1,bad_apply/1,bad_fun_call/1,badarity/1,
- ext_badarity/1,otp_6061/1]).
--export([nothing/0]).
+ ext_badarity/1,otp_6061/1,external/1]).
+
+%% Internal exports.
+-export([nothing/0,call_me/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -46,7 +48,7 @@ end_per_group(_GroupName, Config) ->
cases() ->
[good_call, bad_apply, bad_fun_call, badarity,
- ext_badarity, otp_6061].
+ ext_badarity, otp_6061, external].
init_per_testcase(_Case, Config) ->
test_lib:interpret(?MODULE),
@@ -244,3 +246,47 @@ test_otp_6061(Starter) ->
fun() -> Starter ! working end,
fun() -> Starter ! not_working end],
lists:foreach(fun(P)->(lists:nth(P,PassesF))() end,Passes).
+
+-define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)).
+-define(APPLY2(M, F, A),
+ (fun(Map) ->
+ Id = fun(I) -> I end,
+ List = [x,y],
+ List = Map(Id, List),
+ {type,external} = erlang:fun_info(Map, type)
+ end)(fun M:F/A)).
+
+external(Config) when is_list(Config) ->
+ Mod = id(?MODULE),
+ Func = id(call_me),
+ Arity = id(1),
+
+ ?APPLY(?MODULE, call_me, 1),
+ ?APPLY(?MODULE, call_me, Arity),
+ ?APPLY(?MODULE, Func, 1),
+ ?APPLY(?MODULE, Func, Arity),
+ ?APPLY(Mod, call_me, 1),
+ ?APPLY(Mod, call_me, Arity),
+ ?APPLY(Mod, Func, 1),
+ ?APPLY(Mod, Func, Arity),
+
+ ListsMod = id(lists),
+ ListsMap = id(map),
+ ListsArity = id(2),
+
+ ?APPLY2(lists, map, 2),
+ ?APPLY2(lists, map, ListsArity),
+ ?APPLY2(lists, ListsMap, 2),
+ ?APPLY2(lists, ListsMap, ListsArity),
+ ?APPLY2(ListsMod, map, 2),
+ ?APPLY2(ListsMod, map, ListsArity),
+ ?APPLY2(ListsMod, ListsMap, 2),
+ ?APPLY2(ListsMod, ListsMap, ListsArity),
+
+ ok.
+
+call_me(I) ->
+ {ok,I}.
+
+id(I) ->
+ I.