diff options
Diffstat (limited to 'lib/debugger')
37 files changed, 1329 insertions, 5884 deletions
diff --git a/lib/debugger/doc/src/book.xml b/lib/debugger/doc/src/book.xml index b8440eed2b..82ae6b5b94 100644 --- a/lib/debugger/doc/src/book.xml +++ b/lib/debugger/doc/src/book.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/doc/src/debugger.xml b/lib/debugger/doc/src/debugger.xml index 479c4271b0..a04dce92d2 100644 --- a/lib/debugger/doc/src/debugger.xml +++ b/lib/debugger/doc/src/debugger.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2002</year><year>2009</year> + <year>2002</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/doc/src/debugger_chapter.xml b/lib/debugger/doc/src/debugger_chapter.xml index d309b839a4..99b6d07355 100644 --- a/lib/debugger/doc/src/debugger_chapter.xml +++ b/lib/debugger/doc/src/debugger_chapter.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> diff --git a/lib/debugger/doc/src/fascicules.xml b/lib/debugger/doc/src/fascicules.xml index 1b9d6bc94d..154c8a3b6d 100644 --- a/lib/debugger/doc/src/fascicules.xml +++ b/lib/debugger/doc/src/fascicules.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> diff --git a/lib/debugger/doc/src/i.xml b/lib/debugger/doc/src/i.xml index 9560b43665..fb7641c30e 100644 --- a/lib/debugger/doc/src/i.xml +++ b/lib/debugger/doc/src/i.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1998</year><year>2009</year> + <year>1998</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/doc/src/int.xml b/lib/debugger/doc/src/int.xml index 0794685f34..3a5886ceb9 100644 --- a/lib/debugger/doc/src/int.xml +++ b/lib/debugger/doc/src/int.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1998</year><year>2011</year> + <year>1998</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/doc/src/part.xml b/lib/debugger/doc/src/part.xml index b5646235d4..0c90feab66 100644 --- a/lib/debugger/doc/src/part.xml +++ b/lib/debugger/doc/src/part.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/doc/src/ref_man.xml b/lib/debugger/doc/src/ref_man.xml index 62bef83646..9ecc1f8879 100644 --- a/lib/debugger/doc/src/ref_man.xml +++ b/lib/debugger/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/priv/erlang_bug.png b/lib/debugger/priv/erlang_bug.png Binary files differindex 87c8279654..200f531484 100644 --- a/lib/debugger/priv/erlang_bug.png +++ b/lib/debugger/priv/erlang_bug.png diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile index 85754da219..90189dd297 100644 --- a/lib/debugger/src/Makefile +++ b/lib/debugger/src/Makefile @@ -45,20 +45,6 @@ MODULES= \ dbg_iload \ dbg_iserver \ dbg_istk \ - dbg_ui_break \ - dbg_ui_break_win \ - dbg_ui_edit \ - dbg_ui_edit_win \ - dbg_ui_filedialog_win \ - dbg_ui_interpret \ - dbg_ui_mon \ - dbg_ui_mon_win \ - dbg_ui_settings \ - dbg_ui_trace \ - dbg_ui_trace_win \ - dbg_ui_view \ - dbg_ui_win \ - dbg_ui_winman \ dbg_wx_break \ dbg_wx_break_win \ dbg_wx_code \ diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index b230efaa7a..b1bf4ebecc 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -399,7 +399,7 @@ eval_restricted({From,_Mod,Cmd,SP}, Bs) -> eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP < Le-> %% Evaluate in stack - eval_restricted({From, Mod, Cmd, SP}, Bs), + _ = eval_restricted({From, Mod, Cmd, SP}, Bs), Bs; eval_nonrestricted({From, _Mod, Cmd, _SP}, Bs, #ieval{level=Le,module=M,line=Line}=Ieval) -> @@ -465,7 +465,8 @@ tell_attached(Msg) -> case get(attached) of undefined -> ignore; AttPid -> - AttPid ! {self(), Msg} + AttPid ! {self(), Msg}, + ignore end. %%==================================================================== diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 6ebce9b890..1d36aae8ee 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -204,7 +204,8 @@ meta(Int, Debugged, M, F, As) -> %% If it's a fun we're evaluating, show a text %% representation of the fun and its arguments, %% not dbg_ieval:eval_fun(...) - {dbg_ieval, eval_fun} -> + {dbg_ieval, EvalFun} when EvalFun =:= eval_fun; + EvalFun =:= eval_named_fun -> {Mx, Fx} = lists:last(As), {Mx, Fx, lists:nth(2, As)}; _ -> @@ -432,7 +433,8 @@ eval_function(Mod, Name, As, Bs, Called, Ieval0, Lc) -> do_eval_function(Mod, Fun, As0, Bs0, _, Ieval0) when is_function(Fun); Mod =:= ?MODULE, - Fun =:= eval_fun -> + Fun =:= eval_fun orelse + Fun =:= eval_named_fun -> #ieval{level=Le,line=Li,top=Top} = Ieval0, case lambda(Fun, As0) of {[{clause,Fc,_,_,_}|_]=Cs,Module,Name,As,Bs} -> @@ -487,13 +489,29 @@ lambda(eval_fun, [Cs,As,Bs,{Mod,Name}=F]) -> true -> {error,{badarity,{F,As}}} end; +lambda(eval_named_fun, [Cs,As,Bs0,FName,RF,{Mod,Name}=F]) -> + %% Fun defined in interpreted code, called from outside + if + length(element(3,hd(Cs))) =:= length(As) -> + db_ref(Mod), %% Adds ref between module and process + Bs1 = add_binding(FName, RF, Bs0), + {Cs,Mod,Name,As,Bs1}; + true -> + {error,{badarity,{F,As}}} + end; lambda(Fun, As) when is_function(Fun) -> %% Fun called from within interpreted code... case erlang:fun_info(Fun, module) of %% ... and the fun was defined in interpreted code {module, ?MODULE} -> - {env, [{Mod,Name},Bs,Cs]} = erlang:fun_info(Fun, env), + {Mod,Name,Bs, Cs} = + case erlang:fun_info(Fun, env) of + {env,[{{M,F},Bs0,Cs0}]} -> + {M,F,Bs0, Cs0}; + {env,[{{M,F},Bs0,Cs0,FName}]} -> + {M,F,add_binding(FName, Fun, Bs0), Cs0} + end, {arity, Arity} = erlang:fun_info(Fun, arity), if length(As) =:= Arity -> @@ -636,6 +654,21 @@ expr({tuple,Line,Es0}, Bs0, Ieval) -> {Vs,Bs} = eval_list(Es0, Bs0, Ieval#ieval{line=Line}), {value,list_to_tuple(Vs),Bs}; +%% Map +expr({map,Line,Fs0}, Bs0, Ieval) -> + {Fs,Bs} = eval_map_fields(Fs0, Bs0, Ieval#ieval{line=Line,top=false}), + Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) end, + #{}, Fs), + {value,Value,Bs}; +expr({map,Line,E0,Fs0}, Bs0, Ieval0) -> + Ieval = Ieval0#ieval{line=Line,top=false}, + {value,E,Bs1} = expr(E0, Bs0, Ieval), + {Fs,Bs2} = eval_map_fields(Fs0, Bs1, Ieval), + Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi); + ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end, + E, Fs), + {value,Value,Bs2}; + %% A block of statements expr({block,Line,Es},Bs,Ieval) -> seq(Es, Bs, Ieval#ieval{line=Line}); @@ -694,23 +727,25 @@ expr({'if',Line,Cs}, Bs, Ieval) -> if_clauses(Cs, Bs, Ieval#ieval{line=Line}); %% Andalso/orelse -expr({'andalso',Line,E1,E2}, Bs, Ieval) -> - case expr(E1, Bs, Ieval#ieval{line=Line, top=false}) of - {value,false,_}=Res -> - Res; - {value,true,_} -> - expr(E2, Bs, Ieval#ieval{line=Line, top=false}); - {value,Val,Bs} -> - exception(error, {badarg,Val}, Bs, Ieval) +expr({'andalso',Line,E1,E2}, Bs0, Ieval) -> + case expr(E1, Bs0, Ieval#ieval{line=Line, top=false}) of + {value,false,_}=Res -> + Res; + {value,true,Bs} -> + {value,Val,_} = expr(E2, Bs, Ieval#ieval{line=Line, top=false}), + {value,Val,Bs}; + {value,Val,Bs} -> + exception(error, {badarg,Val}, Bs, Ieval) end; -expr({'orelse',Line,E1,E2}, Bs, Ieval) -> - case expr(E1, Bs, Ieval#ieval{line=Line, top=false}) of - {value,true,_}=Res -> - Res; - {value,false,_} -> - expr(E2, Bs, Ieval#ieval{line=Line, top=false}); - {value,Val,_} -> - exception(error, {badarg,Val}, Bs, Ieval) +expr({'orelse',Line,E1,E2}, Bs0, Ieval) -> + case expr(E1, Bs0, Ieval#ieval{line=Line, top=false}) of + {value,true,_}=Res -> + Res; + {value,false,Bs} -> + {value,Val,_} = expr(E2, Bs, Ieval#ieval{line=Line, top=false}), + {value,Val,Bs}; + {value,Val,Bs} -> + exception(error, {badarg,Val}, Bs, Ieval) end; %% Matching expression @@ -727,50 +762,121 @@ expr({match,Line,Lhs,Rhs0}, Bs0, Ieval0) -> %% Construct a fun expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) -> Arity = length(element(3,hd(Cs))), - Info = {Module,Name}, + Info = {{Module,Name},Bs,Cs}, Fun = case Arity of - 0 -> fun() -> eval_fun(Cs, [], Bs, Info) end; - 1 -> fun(A) -> eval_fun(Cs, [A], Bs,Info) end; - 2 -> fun(A,B) -> eval_fun(Cs, [A,B], Bs,Info) end; - 3 -> fun(A,B,C) -> eval_fun(Cs, [A,B,C], Bs,Info) end; - 4 -> fun(A,B,C,D) -> eval_fun(Cs, [A,B,C,D], Bs,Info) end; - 5 -> fun(A,B,C,D,E) -> eval_fun(Cs, [A,B,C,D,E], Bs,Info) end; - 6 -> fun(A,B,C,D,E,F) -> eval_fun(Cs, [A,B,C,D,E,F], Bs,Info) end; + 0 -> fun() -> eval_fun([], Info) end; + 1 -> fun(A) -> eval_fun([A], Info) end; + 2 -> fun(A,B) -> eval_fun([A,B], Info) end; + 3 -> fun(A,B,C) -> eval_fun([A,B,C], Info) end; + 4 -> fun(A,B,C,D) -> eval_fun([A,B,C,D], Info) end; + 5 -> fun(A,B,C,D,E) -> eval_fun([A,B,C,D,E], Info) end; + 6 -> fun(A,B,C,D,E,F) -> eval_fun([A,B,C,D,E,F], Info) end; 7 -> fun(A,B,C,D,E,F,G) -> - eval_fun(Cs, [A,B,C,D,E,F,G], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G], Info) end; 8 -> fun(A,B,C,D,E,F,G,H) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H], Info) end; 9 -> fun(A,B,C,D,E,F,G,H,I) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I], Info) end; 10 -> fun(A,B,C,D,E,F,G,H,I,J) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J], Info) end; 11 -> fun(A,B,C,D,E,F,G,H,I,J,K) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K], Info) end; 12 -> fun(A,B,C,D,E,F,G,H,I,J,K,L) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L], Info) end; 13 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M], Info) end; 14 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N], Info) end; 15 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], Info) end; 16 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], Info) end; 17 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], Info) end; 18 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R], Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R], Info) end; 19 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S],Bs,Info) end; + eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S],Info) end; 20 -> fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) -> - eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T],Bs,Info) end; + 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 -> exception(error, {'argument_limit',{'fun',Cs}}, Bs, Ieval#ieval{line=Line}) end, {value,Fun,Bs}; +%% Construct a fun +expr({make_named_fun,Line,Name,FName,Cs}, Bs, #ieval{module=Module}=Ieval) -> + Arity = length(element(3,hd(Cs))), + Info = {{Module,Name},Bs,Cs,FName}, + Fun = + case Arity of + 0 -> fun RF() -> eval_named_fun([], RF, Info) end; + 1 -> fun RF(A) -> eval_named_fun([A], RF, Info) end; + 2 -> fun RF(A,B) -> + eval_named_fun([A,B], RF, Info) end; + 3 -> fun RF(A,B,C) -> + eval_named_fun([A,B,C], RF, Info) end; + 4 -> fun RF(A,B,C,D) -> + eval_named_fun([A,B,C,D], RF, Info) end; + 5 -> fun RF(A,B,C,D,E) -> + eval_named_fun([A,B,C,D,E], + RF, Info) end; + 6 -> fun RF(A,B,C,D,E,F) -> + eval_named_fun([A,B,C,D,E,F], + RF, Info) end; + 7 -> fun RF(A,B,C,D,E,F,G) -> + eval_named_fun([A,B,C,D,E,F,G], + RF, Info) end; + 8 -> fun RF(A,B,C,D,E,F,G,H) -> + eval_named_fun([A,B,C,D,E,F,G,H], + RF, Info) end; + 9 -> fun RF(A,B,C,D,E,F,G,H,I) -> + eval_named_fun([A,B,C,D,E,F,G,H,I], + RF, Info) end; + 10 -> fun RF(A,B,C,D,E,F,G,H,I,J) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J], + RF, Info) end; + 11 -> fun RF(A,B,C,D,E,F,G,H,I,J,K) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K], + RF, Info) end; + 12 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L], + RF, Info) end; + 13 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M], + RF, Info) end; + 14 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N], + RF, Info) end; + 15 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], + RF, Info) end; + 16 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], + RF, Info) end; + 17 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], + RF, Info) end; + 18 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q, + R], + RF, Info) end; + 19 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q, + R,S], + RF, Info) end; + 20 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) -> + eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q, + R,S,T], + RF, Info) end; + _Other -> + exception(error, {'argument_limit',{named_fun,FName,Cs}}, Bs, + Ieval#ieval{line=Line}) + 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), @@ -960,9 +1066,13 @@ expr(E, _Bs, _Ieval) -> erlang:error({'NYI',E}). %% Interpreted fun() called from uninterpreted module, recurse -eval_fun(Cs, As, Bs, Info) -> +eval_fun(As, {Info,Bs,Cs}) -> dbg_debugged:eval(?MODULE, eval_fun, [Cs,As,Bs,Info]). +%% Interpreted named fun() called from uninterpreted module, recurse +eval_named_fun(As, RF, {Info,Bs,Cs,FName}) -> + dbg_debugged:eval(?MODULE, eval_named_fun, [Cs,As,Bs,FName,RF,Info]). + %% eval_lc(Expr,[Qualifier],Bindings,IevalState) -> %% {value,Value,Bindings}. %% This is evaluating list comprehensions "straight out of the book". @@ -1373,6 +1483,19 @@ guard_expr({cons,_,H0,T0}, Bs) -> guard_expr({tuple,_,Es0}, Bs) -> {values,Es} = guard_exprs(Es0, Bs), {value,list_to_tuple(Es)}; +guard_expr({map,_,Fs0}, Bs) -> + Fs = eval_map_fields_guard(Fs0, Bs), + Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) end, + #{}, Fs), + {value,Value}; +guard_expr({map,_,E0,Fs0}, Bs) -> + {value,E} = guard_expr(E0, Bs), + Fs = eval_map_fields_guard(Fs0, Bs), + Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi); + ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end, + E, Fs), + io:format("~p~n", [{E,Value}]), + {value,Value}; guard_expr({bin,_,Flds}, Bs) -> {value,V,_Bs} = eval_bits:expr_grp(Flds, Bs, @@ -1382,6 +1505,37 @@ guard_expr({bin,_,Flds}, Bs) -> end, [], false), {value,V}. + +%% eval_map_fields([Field], Bindings, IEvalState) -> +%% {[{map_assoc | map_exact,Key,Value}],Bindings} + +eval_map_fields(Fs, Bs, Ieval) -> + eval_map_fields(Fs, Bs, Ieval, fun expr/3). + +eval_map_fields_guard(Fs0, Bs) -> + {Fs,_} = eval_map_fields(Fs0, Bs, #ieval{}, + fun (G0, Bs0, _) -> + {value,G} = guard_expr(G0, Bs0), + {value,G,Bs0} + end), + Fs. + +eval_map_fields(Fs, Bs, Ieval, F) -> + eval_map_fields(Fs, Bs, Ieval, F, []). + +eval_map_fields([{map_field_assoc,Line,K0,V0}|Fs], Bs0, Ieval0, F, Acc) -> + Ieval = Ieval0#ieval{line=Line}, + {value,K,Bs1} = F(K0, Bs0, Ieval), + {value,V,Bs2} = F(V0, Bs1, Ieval), + eval_map_fields(Fs, Bs2, Ieval0, F, [{map_assoc,K,V}|Acc]); +eval_map_fields([{map_field_exact,Line,K0,V0}|Fs], Bs0, Ieval0, F, Acc) -> + Ieval = Ieval0#ieval{line=Line}, + {value,K,Bs1} = F(K0, Bs0, Ieval), + {value,V,Bs2} = F(V0, Bs1, Ieval), + eval_map_fields(Fs, Bs2, Ieval0, F, [{map_exact,K,V}|Acc]); +eval_map_fields([], Bs, _Ieval, _F, Acc) -> + {lists:reverse(Acc),Bs}. + %% match(Pattern,Term,Bs) -> {match,Bs} | nomatch match(Pat, Term, Bs) -> try match1(Pat, Term, Bs, Bs) @@ -1411,6 +1565,8 @@ match1({cons,_,H,T}, [H1|T1], Bs0, BBs) -> match1({tuple,_,Elts}, Tuple, Bs, BBs) when length(Elts) =:= tuple_size(Tuple) -> match_tuple(Elts, Tuple, 1, Bs, BBs); +match1({map,_,Fields}, Map, Bs, BBs) when is_map(Map) -> + match_map(Fields, Map, Bs, BBs); match1({bin,_,Fs}, B, Bs0, BBs) when is_bitstring(B) -> try eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), @@ -1434,6 +1590,17 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) -> match_tuple([], _, _, Bs, _BBs) -> {match,Bs}. +match_map([{map_field_exact,_,K0,Pat}|Fs], Map, Bs0, BBs) -> + {value,K,BBs} = expr(K0, BBs, #ieval{}), + case maps:find(K, Map) of + {ok,Value} -> + {match,Bs} = match1(Pat, Value, Bs0, BBs), + match_map(Fs, Map, Bs, BBs); + error -> throw(nomatch) + end; +match_map([], _, Bs, _BBs) -> + {match,Bs}. + head_match([Par|Pars], [Arg|Args], Bs0, BBs) -> try match1(Par, Arg, Bs0, BBs) of {match,Bs} -> head_match(Pars, Args, Bs, BBs) diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index 7edfb1d515..266cf239dd 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -194,6 +194,11 @@ pattern({cons,Line,H0,T0}) -> pattern({tuple,Line,Ps0}) -> Ps1 = pattern_list(Ps0), {tuple,Line,Ps1}; +pattern({map,Line,Fs0}) -> + Fs1 = lists:map(fun ({map_field_exact,L,K,V}) -> + {map_field_exact,L,expr(K, false),pattern(V)} + end, Fs0), + {map,Line,Fs1}; pattern({op,_,'-',{integer,Line,I}}) -> {value,Line,-I}; pattern({op,_,'+',{integer,Line,I}}) -> @@ -262,6 +267,8 @@ guard_test({string,Line,_}) -> {value,Line,false}; guard_test({nil,Line}) -> {value,Line,false}; guard_test({cons,Line,_,_}) -> {value,Line,false}; guard_test({tuple,Line,_}) -> {value,Line,false}; +guard_test({map,Line,_}) -> {value,Line,false}; +guard_test({map,Line,_,_}) -> {value,Line,false}; guard_test({bin,Line,_}) -> {value,Line,false}. gexpr({var,Line,V}) -> {var,Line,V}; @@ -279,6 +286,13 @@ gexpr({cons,Line,H0,T0}) -> gexpr({tuple,Line,Es0}) -> Es1 = gexpr_list(Es0), {tuple,Line,Es1}; +gexpr({map,Line,Fs0}) -> + Fs1 = map_fields(Fs0, fun gexpr/1), + {map,Line,Fs1}; +gexpr({map,Line,E0,Fs0}) -> + E1 = gexpr(E0), + Fs1 = map_fields(Fs0, fun gexpr/1), + {map,Line,E1,Fs1}; gexpr({bin,Line,Flds0}) -> Flds = gexpr_list(Flds0), {bin,Line,Flds}; @@ -341,6 +355,13 @@ expr({cons,Line,H0,T0}, _Lc) -> expr({tuple,Line,Es0}, _Lc) -> Es1 = expr_list(Es0), {tuple,Line,Es1}; +expr({map,Line,Fs0}, _Lc) -> + Fs1 = map_fields(Fs0), + {map,Line,Fs1}; +expr({map,Line,E0,Fs0}, _Lc) -> + E1 = expr(E0, false), + Fs1 = map_fields(Fs0), + {map,Line,E1,Fs1}; expr({block,Line,Es0}, Lc) -> %% Unfold block into a sequence. Es1 = exprs(Es0, Lc), @@ -369,6 +390,9 @@ 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({named_fun,Line,FName,Cs0,{_,_,Name}}, _Lc) when is_atom(Name) -> + Cs = fun_clauses(Cs0), + {make_named_fun,Line,Name,FName,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). @@ -511,6 +535,15 @@ fun_clauses([{clause,L,H,G,B}|Cs]) -> [{clause,L,head(H),guard(G),exprs(B, true)}|fun_clauses(Cs)]; fun_clauses([]) -> []. +map_fields(Fs) -> + map_fields(Fs, fun (E) -> expr(E, false) end). + +map_fields([{map_field_assoc,L,N,V}|Fs], F) -> + [{map_field_assoc,L,F(N),F(V)}|map_fields(Fs)]; +map_fields([{map_field_exact,L,N,V}|Fs], F) -> + [{map_field_exact,L,F(N),F(V)}|map_fields(Fs)]; +map_fields([], _) -> []. + %% new_var_name() -> VarName. new_var_name() -> diff --git a/lib/debugger/src/dbg_ui_break.erl b/lib/debugger/src/dbg_ui_break.erl deleted file mode 100644 index 8b9a236ce7..0000000000 --- a/lib/debugger/src/dbg_ui_break.erl +++ /dev/null @@ -1,98 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. 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(dbg_ui_break). - -%% External exports --export([start/3, start/4, start/5]). - -%% Internal exports --export([init/5]). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(GS, Pos, Type) -%% start(GS, Pos, Type, Module, Line) -%% GS = graphics system identifier -%% Pos = {X, Y} -%% X = Y = integer() -%% Type = line | conditional | function -%% Module = atom() -%% Line = integer() -%%-------------------------------------------------------------------- -start(GS, Pos, Type) -> - start(GS, Pos, Type, "", ""). -start(GS, Pos, Type, Mod) -> - start(GS, Pos, Type, Mod, ""). -start(GS, Pos, Type, Mod, Line) -> - spawn_link(?MODULE, init, [GS, Pos, Type, Mod, Line]). - - -%%==================================================================== -%% Internal exports -%%==================================================================== - -init(GS, Pos, Type, Mod, Line) -> - Win = dbg_ui_break_win:create_win(GS, Pos, Type, Mod, Line), - if - Type==function, is_atom(Mod) -> - Win2 = gui_cmd({module, Mod}, Win), - loop(Win2); - true -> - loop(Win) - end. - -loop(Win) -> - receive - - %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> - Cmd = dbg_ui_break_win:handle_event(GuiEvent, Win), - Win2 = gui_cmd(Cmd, Win), - loop(Win2) - end. - -gui_cmd(ignore, Win) -> - Win; -gui_cmd(stopped, _Win) -> - exit(normal); -gui_cmd({win, Win2}, _Win) -> - Win2; -gui_cmd({module, Mod}, Win) -> - Funcs = int:functions(Mod), - dbg_ui_break_win:update_functions(Win, Funcs); -gui_cmd({break, DataL, Action}, _Win) -> - Fun = - fun(Data) -> - case Data of - [Mod, Line] -> - int:break(Mod, Line), - int:action_at_break(Mod, Line, Action); - [Mod, Line, CMod, CFunc] -> - int:break(Mod, Line), - int:test_at_break(Mod, Line, {CMod, CFunc}), - int:action_at_break(Mod, Line, Action); - [Mod, Func, Arity] -> - int:break_in(Mod, Func, Arity) - end - end, - lists:foreach(Fun, DataL), - exit(normal). diff --git a/lib/debugger/src/dbg_ui_break_win.erl b/lib/debugger/src/dbg_ui_break_win.erl deleted file mode 100644 index 11d810ccab..0000000000 --- a/lib/debugger/src/dbg_ui_break_win.erl +++ /dev/null @@ -1,314 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2012. 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(dbg_ui_break_win). --compile([{nowarn_deprecated_function,{gs,button,2}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,create,3}}, - {nowarn_deprecated_function,{gs,entry,2}}, - {nowarn_deprecated_function,{gs,frame,2}}, - {nowarn_deprecated_function,{gs,label,2}}, - {nowarn_deprecated_function,{gs,listbox,2}}, - {nowarn_deprecated_function,{gs,menu,2}}, - {nowarn_deprecated_function,{gs,menuitem,2}}, - {nowarn_deprecated_function,{gs,radiobutton,2}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,window,2}}]). - -%% External exports --export([create_win/5, - update_functions/2, - handle_event/2]). - --record(winInfo, {type, % line | conditional | function - win, % gsobj() - packer, % gsobj() | undefined - entries, % [{atom|integer, GSobj()}] - trigger, % enable | disable | delete - ok, % gsobj() - cancel, % gsobj() - listbox, % gsobj() - funcs=[] % [[Name, Arity]] - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% create_win(GS, Pos, Type, Mod, Line) -> #winInfo{} -%% GS = graphics system identifier -%% Pos = {X, Y} -%% X = Y = integer() -%% Type = line | conditional | function -%% Mod = atom() | "" -%% Line = integer() | "" -%%-------------------------------------------------------------------- -create_win(GS, {X, Y}, function, Mod, _Line) -> - Pad = 8, - W = 230, - - Font = dbg_ui_win:font(normal), - - %% Window - Win = gs:window(GS, [{title, "Function Break"}, {x, X}, {y, Y}, - {destroy, true}, {configure, true}, - {keypress, true}, {data, window}]), - - %% Frame - Frm = gs:frame(Win, [{x, 0}, {y, 0}, {width, W}, {height, 190}, - {packer_x, [{fixed, 70}, {stretch, 1, W-80}, - {fixed, 10}]}, - {packer_y, [{fixed, 10}, {fixed, 30}, - {stretch, 1, 100}, {fixed, 40}]}]), - - %% Create input field (label+entry) - gs:label(Frm, [{label, {text,"Module:"}}, {font, Font}, {align, e}, - {pack_x, 1}, {pack_y, 2}]), - Ent = gs:entry(Frm, [{text, Mod}, - {pack_x, 2}, {pack_y, 2}, - {keypress, true}, {setfocus, true}, - {buttonpress, true}]), - Entries = [{Ent, atom}], - - %% Create a listbox containing the functions of the module - gs:label(Frm, [{label, {text,"Function:"}}, {font, Font}, {align, ne}, - {pack_x, 1}, {pack_y, 3}]), - Lb = gs:listbox(Frm, [{bw, 2}, {relief, ridge}, {vscroll, right}, - {pack_x, 2}, {pack_y, 3}, - {selectmode, multiple}]), - - %% Add OK and Cancel buttons - {Wbtn, Hbtn} = dbg_ui_win:min_size(["OK","Cancel"], 70, 30), - Bot = gs:frame(Frm, [{pack_x, {1, 3}}, {pack_y, 4}]), - OK = gs:button(Bot, [{x, Pad}, {y, Pad}, - {width, Wbtn}, {height, Hbtn}, - {label, {text,"OK"}}, {font, Font}]), - Cancel = gs:button(Bot, [{x, W-Pad-Wbtn}, {y, Pad}, - {width, Wbtn}, {height, Hbtn}, - {label, {text,"Cancel"}}, {font, Font}]), - - Wfrm = gs:read(Frm, width), Hfrm = gs:read(Frm, height), - gs:config(Win, [{width, Wfrm}, {height, Hfrm}, {map, true}]), - #winInfo{type=function, win=Win, - packer=Frm, entries=Entries, trigger=enable, - ok=OK, cancel=Cancel, listbox=Lb, funcs=[]}; -create_win(GS, {X, Y}, Type, Mod, Line) -> - Pad = 8, - W = 230, - - Font = dbg_ui_win:font(normal), - - %% Window - Title = case Type of - line -> "Line Break"; - conditional -> "Conditional Break" - end, - Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y}, - {destroy, true}]), - - %% Create input fields (label+entry) - {Wlbl, Hlbl} = dbg_ui_win:min_size(["C-Function:"], 10, 30), - Went = W-Wlbl-2*Pad, - Labels = case Type of - line -> - [{atom,"Module:",Mod}, {integer,"Line:",Line}]; - conditional -> - [{atom,"Module:",Mod}, {integer,"Line:",Line}, - {atom,"C-Module:",""}, {atom,"C-Function:",""}] - end, - Fun = fun({DataType, Label, Default}, Yin) -> - gs:create(label, Win, [{x, Pad}, {y, Yin}, - {width,Wlbl}, {height,Hlbl}, - {label, {text,Label}}, - {font, Font}, {align, e}]), - Ent = gs:create(entry, Win, [{x, Pad+Wlbl}, {y, Yin}, - {width, Went}, - {height, Hlbl}, - {text, Default}, - {keypress, true}]), - {{Ent, DataType}, Yin+Hlbl} - end, - {Entries, Yacc} = lists:mapfoldl(Fun, Pad, Labels), - {First, _DataType} = hd(Entries), - gs:config(First, [{buttonpress, true}, {setfocus, true}]), - - %% Add 'trigger action' buttons - {Wlbl2, Hlbl2} = dbg_ui_win:min_size(["Trigger Action"], 100, 20), - Wfrm = Wlbl2+8, Hfrm = Hlbl2*4+4, - Grp = erlang:now(), - Frm = gs:frame(Win, [{x, W/2-Wfrm/2-2}, {y, Yacc+Pad-2}, - {width, Wfrm}, {height, Hfrm}, {bw, 2}]), - gs:label(Frm, [{label, {text, "Trigger Action"}}, {font, Font}, - {x, 2}, {y, 0}, {width, Wlbl2}, {height, Hlbl2}]), - gs:radiobutton(Frm, [{label, {text, "Enable"}}, {font, Font}, - {x, 10}, {y, Hlbl2}, - {width, Wlbl2-10}, {height, Hlbl2}, - {align, w}, {group, Grp}, - {data, {trigger, enable}}, - {select, true}]), - gs:radiobutton(Frm, [{label, {text, "Disable"}}, {font, Font}, - {x, 10}, {y, Hlbl2*2}, - {width, Wlbl2-10}, {height, Hlbl2}, - {align, w}, {group, Grp}, - {data, {trigger, disable}}]), - gs:radiobutton(Frm, [{label, {text, "Delete"}}, {font, Font}, - {x, 10}, {y, Hlbl2*3}, - {width, Wlbl2-10}, {height, Hlbl2}, - {align, w}, {group, Grp}, - {data, {trigger, delete}}]), - - %% Add OK and Cancel buttons - {Wbtn, Hbtn} = dbg_ui_win:min_size(["OK","Cancel"], 70, 30), - Ybtn = Yacc + Pad + Hfrm + Pad, - OK = gs:button(Win, [{x, Pad}, {y, Ybtn}, - {width, Wbtn}, {height, Hbtn}, - {label, {text,"OK"}}, {font, Font}]), - gs:button(Win, [{x, W-Pad-Wbtn}, {y, Ybtn}, - {width, Wbtn}, {height, Hbtn}, - {label, {text,"Cancel"}}, {font, Font}]), - - Hwin = Ybtn + Hbtn + Pad, - gs:config(Win, [{width, W}, {height, Hwin}, {map, true}]), - - #winInfo{type=Type, win=Win, - entries=Entries, trigger=enable, ok=OK}. - -%%-------------------------------------------------------------------- -%% update_functions(WinInfo, Funcs) -> WinInfo -%% WinInfo = #winInfo{} -%% Funcs = [{Name, Arity}] -%% Name = atom() -%% Arity = integer() -%%-------------------------------------------------------------------- -update_functions(WinInfo, Funcs) -> - Items = lists:map(fun([N, A]) -> io_lib:format("~p/~p", [N, A]) end, - Funcs), - gs:config(WinInfo#winInfo.listbox, [{items, Items}, - {setfocus, true}]), - WinInfo#winInfo{funcs=Funcs}. - -%%-------------------------------------------------------------------- -%% handle_event(GSEvent, WinInfo) -> Command -%% GSEvent = {gs, Id, Event, Data, Arg} -%% WinInfo = #winInfo{} -%% Command = ignore -%% | stopped -%% | {win, WinInfo} -%% | {module, Mod} -%% | {break, [[Mod, Line]], Action} -%% | {break, [[Mod, Line, CMod, CFunc]], Action} -%% | {break, [[Mod, Func, Arity]], Action} -%%-------------------------------------------------------------------- -handle_event({gs, _Id, destroy, _Data, _Arg}, _WinInfo) -> - stopped; -handle_event({gs, _Id, configure, _Data, [W, H|_]}, WinInfo) -> - gs:config(WinInfo#winInfo.packer, [{width, W-10}, {height, H-10}]), - gs:config(WinInfo#winInfo.cancel, [{x, W-80}]), - ignore; -handle_event({gs, Ent, buttonpress, _,[N,X0,Y0|_]}, WinInfo) when N>1 -> - %% Right (middle) mouse button click in module entry, display a - %% menu containing all interpreted modules - Mods = int:interpreted(), - X = gs:read(Ent, x) + X0, - Y = gs:read(Ent, y) + Y0, - Menu = gs:menu(WinInfo#winInfo.win, [{post_at,{X,Y}}]), - lists:foreach(fun(Mod) -> - gs:menuitem(Menu, [{label,{text,Mod}}, - {data,{module,Mod}}]) - end, - Mods), - ignore; -handle_event({gs, LB, keypress, window, [Key|_]}, WinInfo) -> - %% Used for functional break window, since listboxes for some - %% reason doesn't generate keypress events - if - Key/='Tab', Key/='Return' -> - ignore; - true -> - handle_event({gs, LB, click, listbox, ["OK"]}, WinInfo) - end; -handle_event({gs, Ent, keypress, Data, [Key|_]}, WinInfo) -> - case WinInfo#winInfo.type of - function when Key/='Tab', Key/='Return' -> - case gs:read(WinInfo#winInfo.listbox, items) of - [] -> ignore; - _Items -> - gs:config(WinInfo#winInfo.listbox, clear), - {win, WinInfo#winInfo{funcs=[]}} - end; - function -> % 'Return' | 'Tab' pressed in Module entry - case check_input(WinInfo#winInfo.entries) of - error -> ignore; - [Mod] -> {module, Mod} - end; - _Type when Key=='Tab'; Key=='Return' -> - case next_entry(Ent, WinInfo#winInfo.entries) of - last -> - gs:config(WinInfo#winInfo.ok, flash), - handle_event({gs, Ent, click, Data, ["OK"]}, WinInfo); - Next -> - gs:config(Next, {setfocus, true}), - ignore - end; - _Type -> ignore - end; -handle_event({gs, _Id, click, _Data, ["OK"|_]}, WinInfo) -> - case check_input(WinInfo#winInfo.entries) of - error -> ignore; - Data when WinInfo#winInfo.type/=function -> - {break, [Data], WinInfo#winInfo.trigger}; - [Mod] -> % Function break window - case gs:read(WinInfo#winInfo.listbox, selection) of - [] -> - {module, Mod}; - IndexL -> - Funcs = WinInfo#winInfo.funcs, - Breaks = - [[Mod|lists:nth(Index+1, Funcs)] || Index <- IndexL], - {break, Breaks, enable} - end - end; -handle_event({gs, _Id, click, _Data, ["Cancel"|_]}, _WinInfo) -> - stopped; -handle_event({gs, _Id, click, {trigger,Trigger}, _Arg}, WinInfo) -> - {win, WinInfo#winInfo{trigger=Trigger}}; -handle_event({gs, _Id, click, {module, Mod}, _Arg}, WinInfo) -> - {Ent, _DataType} = hd(WinInfo#winInfo.entries), - gs:config(Ent, {insert,{0,Mod}}), - ignore; -handle_event(_GSEvent, _WinInfo) -> - ignore. - -check_input(Entries) -> - check_input(Entries, []). -check_input([{Entry, Type} | Entries], Data) -> - Str = gs:read(Entry, text), - case erl_scan:string(Str) of - {ok, [{Type, _Line, Val}], _EndLine} -> - check_input(Entries, [Val|Data]); - _Error -> error - end; -check_input([], Data) -> lists:reverse(Data). - -next_entry(Entry, [{Entry, _Type}]) -> - last; -next_entry(Entry, [{Entry, _Type1}, {Next, _Type2}|_]) -> - Next; -next_entry(Entry, [_|Entries]) -> - next_entry(Entry, Entries). diff --git a/lib/debugger/src/dbg_ui_edit.erl b/lib/debugger/src/dbg_ui_edit.erl deleted file mode 100644 index 390e6acdb4..0000000000 --- a/lib/debugger/src/dbg_ui_edit.erl +++ /dev/null @@ -1,91 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2009. 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(dbg_ui_edit). - -%% External exports --export([start/5]). - -%% Internal exports --export([init/6]). - --record(state, {win, % term() Edit dialog window data - pid, % pid() Parent - prompt % atom() - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(GS, Pos, Title, Prompt, {Type, Value}) -%% GS = graphics system identifier -%% Pos = {X, Y} -%% X = Y = integer() -%% Title = string() -%% Prompt = atom() -%% Type = term | atom | float | integer | string -%% Value = term() -%%-------------------------------------------------------------------- -start(GS, Pos, Title, Prompt, Edit) -> - case dbg_ui_winman:is_started(Title) of - true -> ignore; - false -> - spawn(?MODULE, init, [self(), GS, Pos, Title, Prompt, Edit]) - end. - - -%%==================================================================== -%% Internal exports -%%==================================================================== - -init(Pid, GS, Pos, Title, Prompt, Edit) -> - - %% Create edit dialog window - Win = dbg_ui_edit_win:create_win(GS, Pos, Title, Prompt, Edit), - Window = dbg_ui_edit_win:get_window(Win), - dbg_ui_winman:insert(Title, Window), - State = #state{win=Win, pid=Pid, prompt=Prompt}, - - loop(State). - -loop(State) -> - receive - - %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> - Cmd = dbg_ui_edit_win:handle_event(GuiEvent, - State#state.win), - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the dbg_ui_winman process (Debugger window manager) - {dbg_ui_winman, update_windows_menu, _Data} -> - loop(State); - {dbg_ui_winman, destroy} -> - exit(normal) - end. - -gui_cmd(ignore, State) -> - State; -gui_cmd(stopped, _State) -> - exit(normal); -gui_cmd({edit, Value}, State) -> - State#state.pid ! {dbg_ui_edit, State#state.prompt, Value}, - exit(normal). diff --git a/lib/debugger/src/dbg_ui_edit_win.erl b/lib/debugger/src/dbg_ui_edit_win.erl deleted file mode 100644 index 2e9a685b57..0000000000 --- a/lib/debugger/src/dbg_ui_edit_win.erl +++ /dev/null @@ -1,128 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2012. 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(dbg_ui_edit_win). --compile([{nowarn_deprecated_function,{gs,button,2}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,entry,2}}, - {nowarn_deprecated_function,{gs,label,2}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,window,2}}]). - -%% External exports --export([create_win/5, get_window/1, - handle_event/2]). - --record(winInfo, {window, % gsobj() - entry, % gsobj() - button, % gsobj() - type % atom() - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% create_win(GS, Pos, Title, Prompt, {Type, Value}) -> #winInfo{} -%% GS = graphics system identifier -%% Pos = {X, Y} -%% X = Y = integer() -%% Title = string() -%% Prompt = atom() -%% Type = term | atom | float | integer | string -%% Value = term() -%%-------------------------------------------------------------------- -create_win(GS, {X, Y}, Title, Prompt, {Type, Value}) -> - Pad=8, - - Font = dbg_ui_win:font(normal), - - %% Window - Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y}, - {destroy, true}]), - - %% Label - {Wlbl, Hlbl} = dbg_ui_win:min_size([Prompt], 50, 30), - gs:label(Win, [{x, Pad}, {y, Pad}, {width, Wlbl}, {height, Hlbl}, - {align, e}, {label, {text, Prompt}}, {font, Font}]), - - - %% Entry - {Went, _Hent} = dbg_ui_win:min_size([Value], 100, 20), - Ent = gs:entry(Win, [{x, Pad+Wlbl}, {y, Pad}, - {width, Went}, {height, Hlbl}, - {text, Value}, - {keypress, true}]), - - %% OK and Cancel buttons - W = Pad + Wlbl + Went + Pad, - {Wbtn, Hbtn} = dbg_ui_win:min_size(["Cancel"], 70, 30), - Ybtn = Pad + Hlbl + Pad, - Btn = gs:button(Win, [{x, Pad}, {y, Ybtn}, - {width, Wbtn}, {height, Hbtn}, - {label, {text,"OK"}}, {font, Font}]), - gs:button(Win, [{x, W-Pad-Wbtn}, {y, Ybtn}, - {width, Wbtn}, {height, Hbtn}, - {label, {text,"Cancel"}}, {font, Font}]), - - H = Ybtn + Hbtn + Pad, - gs:config(Win, [{width, W}, {height, H}, {map, true}]), - - #winInfo{window=Win, entry=Ent, button=Btn, type=Type}. - -%%-------------------------------------------------------------------- -%% get_window(WinInfo) -> Window -%% WinInfo = #winInfo{} -%% Window = gsobj() -%%-------------------------------------------------------------------- -get_window(WinInfo) -> - WinInfo#winInfo.window. - -%%-------------------------------------------------------------------- -%% handle_event(GSEvent, WinInfo) -> Command -%% GSEvent = {gs, Id, Event, Data, Arg} -%% WinInfo = #winInfo{} -%% Command = ignore -%% | stopped -%% | {edit, Value} -%%-------------------------------------------------------------------- -handle_event({gs, _Id, destroy, _Data, _Arg}, _WinInfo) -> - stopped; -handle_event({gs, Id, keypress, Data, ['Return'|_]}, WinInfo) -> - gs:config(WinInfo#winInfo.button, flash), - handle_event({gs, Id, click, Data, ["OK"]}, WinInfo); -handle_event({gs, _Id, click, _Data, ["OK"|_]}, WinInfo) -> - Ent = WinInfo#winInfo.entry, - Str = gs:read(Ent, text), - Type = WinInfo#winInfo.type, - case erl_scan:string(Str) of - {ok, Tokens, _EndLine} when Type==term -> - case erl_parse:parse_term(Tokens++[{dot, 1}]) of - {ok, Value} -> {edit, Value}; - _Error -> ignore - end; - {ok, [{Type, _Line, Value}], _EndLine} when Type/=term -> - {edit, Value}; - _ -> - ignore - end; -handle_event({gs, _Id, click, _Data, ["Cancel"|_]}, _WinInfo) -> - stopped; -handle_event(_GSEvent, _WinInfo) -> - ignore. diff --git a/lib/debugger/src/dbg_ui_filedialog_win.erl b/lib/debugger/src/dbg_ui_filedialog_win.erl deleted file mode 100644 index 52dc2012f3..0000000000 --- a/lib/debugger/src/dbg_ui_filedialog_win.erl +++ /dev/null @@ -1,340 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2012. 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(dbg_ui_filedialog_win). --compile([{nowarn_deprecated_function,{gs,button,2}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,entry,3}}, - {nowarn_deprecated_function,{gs,label,2}}, - {nowarn_deprecated_function,{gs,listbox,3}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,window,2}}]). - -%% External exports --export([create_win/6, create_win/7, get_window/1, - tag/2, - handle_event/2]). - --record(winInfo, {window, % gsobj() - extra, % fun() - cwd, % string() - pattern % string() - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% create_win(GS, Title, Pos, Mode, Filter, Extra) -%% create_win(GS, Title, Pos, Mode, Filter, Extra, FileName) -> #winInfo{} -%% GS = term() -%% Title = string() -%% Pos = {X,Y} -%% Mode = normal | multiselect -%% Filter = string() File name that may include * symbols. -%% Extra = fun(File) -> {true, tag} | true | {error, term()} -%% FileName = string() Suggested file name when saving -%%-------------------------------------------------------------------- -create_win(GS, Title, {X,Y}, Mode, Filter, Extra) -> - create_win(GS, Title, {X,Y}, Mode, Filter, Extra, null). -create_win(GS, Title, {X,Y}, Mode, Filter, Extra, FileName) -> - Pad = 8, - Wlb = 480, Hlb = 130, - - Font = dbg_ui_win:font(normal), - - {Wlbl, Hlbl} = dbg_ui_win:min_size(["Directories"], 80, 20), - {Wbtn, Hbtn} = dbg_ui_win:min_size(["Filter","Cancel"], 70, 30), - - %% Window - Win = gs:window(GS, [{title,Title}, {x, X}, {y,Y}, {destroy,true}]), - - %% 'Filter' label and entry (for selecting directory) - gs:label(Win, [{label, {text,"Filter"}}, {font, Font}, {align, sw}, - {x, Pad+2}, {y, Pad}, {width,Wlbl}, {height,Hlbl}]), - gs:entry('Filter', Win, [{x, Pad}, {y, Pad+Hlbl}, - {width, Wlb}, {height, Hbtn}, - {keypress, true}]), - - %% Listboxes (showing directories and files) - Xmid = Pad + Wlb/2, - Y2 = Pad + Hlbl + Hbtn + Pad, - gs:label(Win, [{label, {text,"Directories"}}, - {font, Font}, {align, sw}, - {x, Pad+2}, {y, Y2}, - {width, Wlbl}, {height, Hlbl}]), - gs:label(Win, [{label, {text,"Files"}}, - {font, Font}, {align, sw}, - {x, Xmid+Pad/2+2}, {y, Y2}, - {width, Wlbl}, {height, Hlbl}]), - gs:listbox('Dirs', Win, [{x, Pad}, {y, Y2+Hlbl}, - {width, Wlb/2-Pad/2}, {height, Hlb}, - {vscroll, right}, - {click, true}, {doubleclick, true}]), - gs:listbox('Files', Win, [{x, Xmid+Pad/2}, {y, Y2+Hlbl}, - {width, Wlb/2-Pad/2}, {height, Hlb}, - {vscroll, right}, - {click, true}, {doubleclick, true}]), - - %% 'Selection' label and entry (for selecting file) - Y3 = Y2 + Hlbl + Hlb, - gs:label(Win, [{label, {text,"Selection"}}, {font,Font}, {align,sw}, - {x, Pad+2}, {y, Y3}, {width, Wlbl}, {height, Hlbl}]), - gs:entry('Selection', Win, [{x, Pad}, {y, Y3+Hlbl}, - {width, Wlb}, {height, Hbtn}, - {keypress, true}]), - - %% Buttons - Y4 = Y3 + Hlbl + Hbtn + Pad, - Wb = Wlb - Wbtn, - Opts = [{y, Y4}, {width, Wbtn}, {height, Hbtn}, {font, Font}], - case Mode of - normal -> - gs:button(Win, [{label, {text,"OK"}}, {x, Pad}, - {data, select} | Opts]), - gs:button(Win, [{label, {text,"Filter"}}, {x, Wlb/2-Wbtn/2}, - {data, filter} | Opts]), - gs:button(Win, [{label, {text,"Cancel"}}, {x, Pad+Wb}, - {data, done} | Opts]); - multiselect -> - gs:button(Win, [{label, {text,"Choose"}}, {x, Pad}, - {data, select} | Opts]), - gs:button(Win, [{label, {text,"All"}}, {x, Pad+Wb/3}, - {data, multiselect} | Opts]), - gs:button(Win, [{label, {text,"Filter"}}, {x, Pad+2*Wb/3}, - {data, filter} | Opts]), - gs:button(Win, [{label, {text,"Done"}}, {x, Pad+Wb}, - {data, done} | Opts]) - end, - - %% Insert contents - {ok, Home} = file:get_cwd(), - {Cwd, Pattern} = update_win(Filter, Extra, Home), - if - is_list(FileName) -> - gs:config('Selection', {text, filename:join(Cwd,FileName)}); - true -> ignore - end, - - Wwin = Pad + Wlb + Pad, - Hwin = Y4 + Hbtn + Pad, - gs:config(Win, [{width, Wwin}, {height, Hwin}, {map, true}]), - - #winInfo{window=Win, extra=Extra, cwd=Cwd, pattern=Pattern}. - -%%-------------------------------------------------------------------- -%% get_window(WinInfo) -> Window -%% WinInfo = #winInfo{} -%% Window = gsobj() -%%-------------------------------------------------------------------- -get_window(WinInfo) -> - WinInfo#winInfo.window. - -%%-------------------------------------------------------------------- -%% tag(WinInfo, File) -%% WinInfo = #winInfo{} -%% File = string() -%%-------------------------------------------------------------------- -tag(WinInfo, File0) -> - File = relfile(WinInfo#winInfo.cwd, File0), - case member(File, gs:read('Files', items)) of - {true, Index} -> gs:config('Files', {change, {Index, tag(File)}}); - false -> ignore - end. - -tag(Str) -> [$*|Str]. -untag([$*|Str]) -> Str; -untag([$(|Str]) -> [$)|Rts] = lists:reverse(Str),lists:reverse(Rts); -untag(Str) -> Str. - -member(E, L) -> member(E, L, 0). -member(E, [E|_], I) -> {true, I}; -member(E, [_|T], I) -> member(E, T, I+1); -member(_E, [], _I) -> false. - -%%-------------------------------------------------------------------- -%% handle_event(GSEvent, WinInfo) -> Command -%% GSEvent = {gs, Id, Event, Data, Arg} -%% WinInfo = #winInfo{} -%% Command = ignore -%% | {stopped, Dir} -%% | {win, WinInfo} -%% | {select, File} | {multiselect, Dir, FileNames} -%%-------------------------------------------------------------------- -handle_event({gs, _Id, destroy, _Data, _Args}, WinInfo) -> - {stopped, WinInfo#winInfo.cwd}; - -handle_event({gs, 'Filter', keypress, _Data, ['Return'|_]}, WinInfo) -> - handle_event({gs, null, click, filter, null}, WinInfo); -handle_event({gs, 'Selection', keypress, _Data, ['Return'|_]}, WinInfo) -> - handle_event({gs, null, click, select, null}, WinInfo); - -handle_event({gs, 'Dirs', click, _Data, [0,"..",true|_]}, WinInfo) -> - Filter = filename:join(filename:dirname(WinInfo#winInfo.cwd), - WinInfo#winInfo.pattern), - gs:config('Filter', {text, Filter}), - ignore; -handle_event({gs, 'Dirs', click, _Data, [_Index,Str,true|_]}, WinInfo) -> - Filter = filename:join([WinInfo#winInfo.cwd, Str, - WinInfo#winInfo.pattern]), - gs:config('Filter', {text, Filter}), - ignore; -handle_event({gs, 'Dirs', doubleclick, _Data, _Arg}, WinInfo) -> - handle_event({gs, null, click, filter, null}, WinInfo); - -handle_event({gs, 'Files', click, _Data, [_Index,Str,true|_]}, WinInfo) -> - Selection = filename:join(WinInfo#winInfo.cwd, untag(Str)), - gs:config('Selection', {text, Selection}), - ignore; -handle_event({gs, 'Files', doubleclick, _Data, _Arg}, WinInfo) -> - handle_event({gs, null, click, select, null}, WinInfo); - -handle_event({gs, _Id, click, select, _Arg}, _WinInfo) -> - {select, gs:read('Selection', text)}; -handle_event({gs, _Id, click, multiselect, _Arg}, WinInfo) -> - Files = [untag(File) || File <- gs:read('Files', items)], - {multiselect, WinInfo#winInfo.cwd, Files}; -handle_event({gs, _Id, click, filter, _Arg}, WinInfo) -> - {Cwd, Pattern} = update_win(gs:read('Filter', text), - WinInfo#winInfo.extra, - WinInfo#winInfo.cwd), - {win, WinInfo#winInfo{cwd=Cwd, pattern=Pattern}}; -handle_event({gs, _Id, click, done, _Arg}, WinInfo) -> - {stopped, WinInfo#winInfo.cwd}; - -handle_event(_GSEvent, _WinInfo) -> - ignore. - -%%==================================================================== -%% Internal functions -%%==================================================================== - -update_win(Filter, ExtraFilter, Prev) -> - {Res, {Filter2, Cwd, FilePattern}} = check_filter(Filter, Prev), - - Dirs = [".." | get_subdirs(Cwd)], - - gs:config('Filter', {text, Filter2}), - gs:config('Dirs', {items, Dirs}), - gs:config('Selection', {text, Cwd}), - - case Res of - ok -> - Matching = lists:sort(filelib:wildcard(Filter2, erl_prim_loader)), - Files = extra_filter(Matching, Cwd, ExtraFilter), - gs:config('Files', {items, Files}); - error -> - gs:config('Files', beep) - end, - - {Cwd, FilePattern}. - -%% check_filter(Filter, Prev) -> {ok, Res} | {error, Res} -%% Res = {Filter, Cwd, FilePattern} -%% Filter = Prev = Cwd = FilePattern = string() -check_filter(Filter0, Prev) -> - Filter = case filename:pathtype(Filter0) of - absolute -> Filter0; - _Relative -> filename:absname(Filter0, Prev) - end, - Comps = filename:split(Filter), - Last = lists:last(Comps), - FilePattern = case is_pattern(Last) of - true -> Last; - false -> "*" - end, - {Cwd, Rest} = max_existing(Comps), - case Rest of - [] -> - %% Filter = existing file or directory - Res = case filelib:is_dir(Filter, erl_prim_loader) of - true -> {filename:join(Filter, "*"), Filter, "*"}; - false -> {Filter, filename:dirname(Filter), - filename:basename(Filter)} - end, - {ok, Res}; - [FilePattern] -> - %% Filter = existing dir and valid pattern - {ok, {Filter, Cwd, FilePattern}}; - Comps -> - %% Filter = garbage - {error, {Prev, Prev, "*"}}; - [Name|_Names] -> - %% Filter = existing dir ++ pattern or non-existing file/dir - case is_pattern(Name) of - true -> {ok, {Filter, Cwd, FilePattern}}; - false -> {error, {Cwd, Cwd, ""}} - end - end. - -max_existing([Name | Names]) -> - case filelib:is_file(Name, erl_prim_loader) of - true -> max_existing(Name, Names); - false -> {[], [Name | Names]} - end. -max_existing(Dir, [Name | Names]) -> - Dir2 = filename:join(Dir, Name), - case filelib:is_file(Dir2, erl_prim_loader) of - true when Names =:= [] -> {Dir2, []}; - true -> max_existing(Dir2, Names); - false -> {Dir, [Name | Names]} - end. - -is_pattern(Str) -> - lists:member($*, Str). - -extra_filter([File|Files], Dir, Fun) -> - case Fun(File) of - true -> - [relfile(Dir, File) | extra_filter(Files, Dir, Fun)]; - {true,tag} -> - [[$*|relfile(Dir,File)] | extra_filter(Files, Dir, Fun)]; - {true,disable} -> - [[$(|relfile(Dir,File)]++[$)] | extra_filter(Files, Dir, Fun)]; - {error, _Reason} -> extra_filter(Files, Dir, Fun) - end; -extra_filter([], _Dir, _Fun) -> []. - -get_subdirs(Dir) -> - case erl_prim_loader:list_dir(Dir) of - {ok, FileNames} -> - X = [FN || FN <- FileNames, - filelib:is_dir(filename:join(Dir, FN), erl_prim_loader)], - lists:sort(X); - _Error -> - [] - end. - -%% Return the "remainder" of a file name relative a dir name, examples: -%% relfile("/home/gunilla", "/home/gunilla/m.erl") -> "m.erl" -%% relfile("/home/gunilla/dir", "/home/gunilla/dir/m.erl") -> "dir/m.erl" -%% relfile("/home/gunilla", "/home/arne/m.erl") -> "/home/arne/m.erl" -relfile(Dir, File) -> - case compare(Dir, File) of - error -> File; - RelFile -> RelFile - end. - -compare([_|Dir], [_|File]) -> - compare(Dir, File); -compare([], [$/|File]) -> - File; -compare(_, _) -> - error. diff --git a/lib/debugger/src/dbg_ui_interpret.erl b/lib/debugger/src/dbg_ui_interpret.erl deleted file mode 100644 index 73392d40cb..0000000000 --- a/lib/debugger/src/dbg_ui_interpret.erl +++ /dev/null @@ -1,161 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2011. 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(dbg_ui_interpret). - --include_lib("kernel/include/file.hrl"). - -%% External exports --export([start/4]). - -%% Internal exports --export([init/6]). - --record(state, {gs, % term() Graphics system id - win, % term() Interpret dialog window data - monitor, % pid() Monitor pid - mode % local | global - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(GS, Pos, Dir, Mode) -%% GS = Graphics system id -%% Dir = string() -%% Pos = {X,Y} -%% Mode = local | global -%%-------------------------------------------------------------------- -start(GS, Pos, Dir, Mode) -> - Title = "Interpret Dialog", - case dbg_ui_winman:is_started(Title) of - true -> ignore; - false -> - spawn(?MODULE, init, [self(), GS, Pos, Title, Dir, Mode]) - end. - -%%==================================================================== -%% Internal exports -%%==================================================================== - -init(Monitor, GS, Pos, Title, Dir, Mode) -> - Filter = filename:join(Dir, "*.erl"), - Extra = fun(File) -> - case int:interpretable(File) of - true -> - ModS = filename:basename(File, ".erl"), - Mod = list_to_atom(ModS), - case int:file(Mod) of - File -> {true, tag}; - _ -> true % {error,not_loaded} | File2 - end; - _Error -> {true,disable} - end - end, - - %% Create interpret dialog window - Win = dbg_ui_filedialog_win:create_win(GS, Title, Pos, multiselect, - Filter, Extra), - Window = dbg_ui_filedialog_win:get_window(Win), - dbg_ui_winman:insert(Title, Window), - - State = #state{gs=GS, win=Win, monitor=Monitor, mode=Mode}, - loop(State). - - -%%==================================================================== -%% Main loop and message handling -%%==================================================================== - -loop(State) -> - receive - - %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> - Cmd = dbg_ui_filedialog_win:handle_event(GuiEvent, - State#state.win), - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the dbg_ui_winman process (Debugger window manager) - {dbg_ui_winman, update_windows_menu, _Data} -> - loop(State); - {dbg_ui_winman, destroy} -> - exit(normal) - end. - -gui_cmd(ignore, State) -> - State; -gui_cmd({stopped, Dir}, State) -> - State#state.monitor ! {dbg_ui_interpret, Dir}, - exit(normal); -gui_cmd({win, Win}, State) -> - State#state{win=Win}; -gui_cmd({select, File}, State) -> - Res = case State#state.mode of - local -> int:i(File); - global -> int:ni(File) - end, - - case Res of - %% Interpretation succeeded, tag the file name - {module, _Mod} -> - dbg_ui_filedialog_win:tag(State#state.win, File); - - %% Interpretation failed - error -> - Error = format_error(int:interpretable(File)), - Msg = ["Error when interpreting:", File, Error], - Window = dbg_ui_filedialog_win:get_window(State#state.win), - tool_utils:notify(Window, Msg) - end, - State; -gui_cmd({multiselect, Dir, FileNames}, State) -> - interpret_all(State, Dir, FileNames), - State. - -interpret_all(State, Dir, [File0|Files]) -> - File = filename:join(Dir, File0), - Res = case State#state.mode of - local -> int:i(File); - global -> int:ni(File) - end, - case Res of - {module, _Mod} -> - dbg_ui_filedialog_win:tag(State#state.win, File), - interpret_all(State, Dir, Files); - error -> - Window = dbg_ui_filedialog_win:get_window(State#state.win), - Error = format_error(int:interpretable(File)), - Msg = ["Error when interpreting:", File, Error, - "OK to continue?"], - case tool_utils:confirm(Window, Msg) of - ok -> interpret_all(State, Dir, Files); - cancel -> true - end - end; -interpret_all(_State, _Dir, []) -> - true. - -format_error({error,no_beam}) -> "No BEAM file"; -format_error({error,no_debug_info}) -> "No debug_info in BEAM file"; -format_error({error,badarg}) -> "File does not exist"; -format_error({error,{app,App}}) -> - "Cannot interpret "++atom_to_list(App)++" modules". diff --git a/lib/debugger/src/dbg_ui_mon.erl b/lib/debugger/src/dbg_ui_mon.erl deleted file mode 100644 index 82fe210968..0000000000 --- a/lib/debugger/src/dbg_ui_mon.erl +++ /dev/null @@ -1,738 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2010. 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(dbg_ui_mon). - --include_lib("kernel/include/file.hrl"). - -%% External exports --export([start/2, stop/0]). - --define(TRACEWIN, ['Button Area', 'Evaluator Area', 'Bindings Area']). --define(BACKTRACE, 100). - --record(pinfo, {pid, % pid() - status % break | exit | idle | running | waiting - }). - --record(state, {mode, % local | global - starter, % bool() 'true' if int was started by me - - gs, % term() Graphics system id - win, % term() Monitor window data - focus, % undefined | #pinfo{} Process in focus - coords, % {X,Y} Mouse pointer position - - intdir, % string() Default dir - pinfos, % [#pinfo{}] Debugged processes - - tracewin, % [Area] Areas shown in trace window - backtrace, % integer() Number of call frames to fetch - - attach, % false | {Flags, Function} - - sfile, % default | string() Settings file - changed % boolean() Settings have been changed - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(Mode, SFile) -> {ok, Pid} | {error, Reason} -%% Mode = local | global -%% SFile = string() | default Settings file -%% Pid = pid() -%% Reason = {already_started,Pid} | term() -%%-------------------------------------------------------------------- -start(Mode, SFile) -> - case whereis(?MODULE) of - undefined -> - CallingPid = self(), - Pid = spawn(fun () -> init(CallingPid, Mode, SFile) end), - receive - {initialization_complete, Pid} -> - {ok, Pid}; - Error -> - Error - end; - - Pid -> - {error, {already_started,Pid}} - end. - -%%-------------------------------------------------------------------- -%% stop() -> ok -%%-------------------------------------------------------------------- -stop() -> - case whereis(?MODULE) of - undefined -> - ok; - Pid -> - Flag = process_flag(trap_exit, true), - link(Pid), - Pid ! stop, - receive - {'EXIT', Pid, stop} -> - process_flag(trap_exit, Flag), - ok - end - end. - - -%%==================================================================== -%% Initialization -%%==================================================================== - -init(CallingPid, Mode, SFile) -> - register(?MODULE, self()), - - %% Graphics system - case catch dbg_ui_mon_win:init() of - {'EXIT', Reason} -> - CallingPid ! {error, Reason}; - GS -> - init2(CallingPid, Mode, SFile, GS) - end. - -init2(CallingPid, Mode, SFile, GS) -> - - %% Start Int if necessary and subscribe to information from it - Bool = case int:start() of - {ok, _Int} -> true; - {error, {already_started, _Int}} -> false - end, - int:subscribe(), - - %% Start other necessary stuff - dbg_ui_winman:start(), % Debugger window manager - - %% Create monitor window - Title = "Monitor", - Win = dbg_ui_mon_win:create_win(GS, Title, menus()), - Window = dbg_ui_mon_win:get_window(Win), - dbg_ui_winman:insert(Title, Window), - - %% Initial process state - State1 = #state{mode = Mode, - starter = Bool, - - gs = GS, - win = Win, - focus = undefined, - coords = {0,0}, - - intdir = element(2, file:get_cwd()), - pinfos = [], - - sfile = SFile, - changed = false - }, - - State2 = init_options(?TRACEWIN, % Trace Window - int:auto_attach(), % Auto Attach - int:stack_trace(), % Stack Trace - ?BACKTRACE, % Back Trace Size - State1), - - State3 = init_contents(int:interpreted(), % Modules - int:all_breaks(), % Breakpoints - int:snapshot(), % Processes - State2), - - %% Disable/enable functionality according to process in focus (none) - gui_enable_functions(State3#state.focus), - - CallingPid ! {initialization_complete, self()}, - - if - SFile =:= default -> - loop(State3); - true -> - loop(load_settings(SFile, State3)) - end. - -init_options(TraceWin, AutoAttach, StackTrace, BackTrace, State) -> - lists:foreach(fun(Area) -> - dbg_ui_mon_win:select(Area, true) - end, - TraceWin), - - case AutoAttach of - false -> ignore; - {Flags, _Function} -> - dbg_ui_mon_win:show_option(State#state.win, - auto_attach, Flags), - lists:foreach(fun(Flag) -> - dbg_ui_mon_win:select(map(Flag), true) - end, - Flags) - end, - - dbg_ui_mon_win:show_option(State#state.win, - stack_trace, StackTrace), - dbg_ui_mon_win:select(map(StackTrace), true), - - dbg_ui_mon_win:show_option(State#state.win, back_trace, BackTrace), - - State#state{tracewin=TraceWin, backtrace=BackTrace}. - -init_contents(Mods, Breaks, Processes, State) -> - Win2 = - lists:foldl(fun(Mod, Win) -> - dbg_ui_mon_win:add_module(Win,'Module',Mod) - end, - State#state.win, - Mods), - - Win3 = - lists:foldl(fun(Break, Win) -> - dbg_ui_mon_win:add_break(Win,'Break',Break) - end, - Win2, - Breaks), - - lists:foldl(fun(PidTuple, State0) -> - int_cmd({new_process, PidTuple}, State0) - end, - State#state{win=Win3}, - Processes). - - -%%==================================================================== -%% Main loop and message handling -%%==================================================================== - -loop(State) -> - receive - - stop -> - gui_cmd(stopped, State); - - %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent) =:= gs -> - Cmd = dbg_ui_mon_win:handle_event(GuiEvent,State#state.win), - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the interpreter process - {int, Cmd} -> - State2 = int_cmd(Cmd, State), - loop(State2); - - %% From the dbg_ui_interpret process - {dbg_ui_interpret, Dir} -> - loop(State#state{intdir=Dir}); - - %% From the dbg_ui_edit process - {dbg_ui_edit, 'Backtrace:', BackTrace} -> - dbg_ui_mon_win:show_option(State#state.win, - back_trace, BackTrace), - loop(State#state{backtrace=BackTrace}); - - %% From the dbg_ui_settings process - {dbg_ui_settings, SFile, Action} -> - State2 = case Action of - load -> load_settings(SFile, State); - save -> save_settings(SFile, State) - end, - loop(State2); - - %% From the dbg_ui_winman process (Debugger window manager) - {dbg_ui_winman, update_windows_menu, Data} -> - dbg_ui_winman:update_windows_menu(Data), - loop(State) - end. - -%%--Commands from the GUI--------------------------------------------- -%% Act upon a command from the GUI. In most cases, it is only necessary -%% to call a relevant int-function. int will then report when the action -%% has been taken. - -gui_cmd(ignore, State) -> - State; -gui_cmd(stopped, State) -> - if - State#state.starter =:= true -> int:stop(); - true -> int:auto_attach(false) - end, - exit(stop); -gui_cmd({coords, Coords}, State) -> - State#state{coords=Coords}; - -gui_cmd({shortcut, Key}, State) -> - case shortcut(Key) of - {always, Cmd} -> gui_cmd(Cmd, State); - {if_enabled, Cmd} -> - case dbg_ui_mon_win:is_enabled(Cmd) of - true -> gui_cmd(Cmd, State); - false -> State - end; - false -> State - end; - -%% File Menu -gui_cmd('Load Settings...', State) -> - Window = dbg_ui_mon_win:get_window(State#state.win), - dbg_ui_settings:start(Window, State#state.coords, - load, State#state.sfile), - State; -gui_cmd('Save Settings...', State) -> - Window = dbg_ui_mon_win:get_window(State#state.win), - dbg_ui_settings:start(Window, State#state.coords, - save, State#state.sfile), - State; -gui_cmd('Exit', State) -> - gui_cmd(stopped, State); - -%% Edit Menu -gui_cmd('Refresh', State) -> - int:clear(), - Win = dbg_ui_mon_win:clear_processes(State#state.win), - gui_enable_functions(undefined), - State2 = State#state{win=Win, focus=undefined, pinfos=[]}, - lists:foldl(fun(PidTuple, S) -> - int_cmd({new_process,PidTuple}, S) - end, - State2, - int:snapshot()); -gui_cmd('Kill All', State) -> - lists:foreach(fun(PInfo) -> - case PInfo#pinfo.status of - exit -> ignore; - _Status -> exit(PInfo#pinfo.pid, kill) - end - end, - State#state.pinfos), - State; - -%% Module Menu -gui_cmd('Interpret...', State) -> - dbg_ui_interpret:start(State#state.gs, State#state.coords, - State#state.intdir, State#state.mode), - State; -gui_cmd('Delete All Modules', State) -> - lists:foreach(fun(Mod) -> int:nn(Mod) end, int:interpreted()), - State; -gui_cmd({module, Mod, What}, State) -> - case What of - delete -> int:nn(Mod); - view -> dbg_ui_view:start(State#state.gs, Mod) - end, - State; - -%% Process Menu -gui_cmd('Step', State) -> - int:step((State#state.focus)#pinfo.pid), - State; -gui_cmd('Next', State) -> - int:next((State#state.focus)#pinfo.pid), - State; -gui_cmd('Continue', State) -> - int:continue((State#state.focus)#pinfo.pid), - State; -gui_cmd('Finish ', State) -> - int:finish((State#state.focus)#pinfo.pid), - State; -gui_cmd('Attach', State) -> - Pid = (State#state.focus)#pinfo.pid, - case dbg_ui_winman:is_started(dbg_ui_trace:title(Pid)) of - true -> ignore; - false -> int:attach(Pid, trace_function(State)) - end, - State; -gui_cmd('Kill', State) -> - exit((State#state.focus)#pinfo.pid, kill), - State; - -%% Break Menu -gui_cmd('Line Break...', State) -> - dbg_ui_break:start(State#state.gs, State#state.coords, line), - State; -gui_cmd('Conditional Break...', State) -> - dbg_ui_break:start(State#state.gs, State#state.coords, conditional), - State; -gui_cmd('Function Break...', State) -> - dbg_ui_break:start(State#state.gs, State#state.coords, function), - State; -gui_cmd('Enable All', State) -> - Breaks = int:all_breaks(), - lists:foreach(fun ({{Mod, Line}, _Options}) -> - int:enable_break(Mod, Line) - end, - Breaks), - State; -gui_cmd('Disable All', State) -> - Breaks = int:all_breaks(), - lists:foreach(fun ({{Mod, Line}, _Options}) -> - int:disable_break(Mod, Line) - end, - Breaks), - State; -gui_cmd('Delete All', State) -> - int:no_break(), - State; -gui_cmd({break, {Mod, Line}, What}, State) -> - case What of - delete -> int:delete_break(Mod, Line); - {status, inactive} -> int:disable_break(Mod, Line); - {status, active} -> int:enable_break(Mod, Line); - {trigger, Action} -> int:action_at_break(Mod, Line, Action) - end, - State; - -%% Options Commands -gui_cmd({'Trace Window', TraceWin}, State) -> - State2 = State#state{tracewin=TraceWin}, - case State#state.attach of - false -> ignore; - {Flags, {dbg_ui_trace, start, StartFlags}} -> - case trace_function(State2) of - {_, _, StartFlags} -> ignore; - NewFunction -> % {_, _, NewStartFlags} - int:auto_attach(Flags, NewFunction) - end; - _AutoAttach -> ignore - end, - State2; -gui_cmd({'Auto Attach', When}, State) -> - if - When =:= [] -> int:auto_attach(false); - true -> - Flags = [map(Name) || Name <- When], - int:auto_attach(Flags, trace_function(State)) - end, - State; -gui_cmd({'Stack Trace', [Name]}, State) -> - int:stack_trace(map(Name)), - State; -gui_cmd('Back Trace Size...', State) -> - dbg_ui_edit:start(State#state.gs, State#state.coords, "Backtrace", - 'Backtrace:', {integer, State#state.backtrace}), - State; - -%% Help Menu -gui_cmd('Debugger', State) -> - HelpFile = filename:join([code:lib_dir(debugger), "doc", "html", "index.html"]), - Window = dbg_ui_mon_win:get_window(State#state.win), - tool_utils:open_help(Window, HelpFile), - State; - -gui_cmd({focus, Pid, Win}, State) -> - {value, PInfo} = - lists:keysearch(Pid, #pinfo.pid, State#state.pinfos), - gui_enable_functions(PInfo), - State#state{win=Win, focus=PInfo}; -gui_cmd(default, State) -> - case lists:member('Attach', menus(enabled, State#state.focus)) of - true -> gui_cmd('Attach', State); - false -> State - end. - -%%--Commands from the interpreter------------------------------------- - -int_cmd({interpret, Mod}, State) -> - Win = dbg_ui_mon_win:add_module(State#state.win, 'Module', Mod), - State#state{win=Win}; -int_cmd({no_interpret, Mod}, State) -> - Win = dbg_ui_mon_win:delete_module(State#state.win, Mod), - State#state{win=Win}; - -int_cmd({new_process, {Pid, Function, Status, Info}}, State) -> - - %% Create record with information about the process - Name = registered_name(Pid), - PInfo = #pinfo{pid=Pid, status=Status}, - - %% Update window - Win = dbg_ui_mon_win:add_process(State#state.win, - Pid, Name, Function, Status, Info), - - %% Store process information - PInfos = State#state.pinfos ++ [PInfo], - State#state{win=Win, pinfos=PInfos}; -int_cmd({new_status, Pid, Status, Info}, State) -> - - %% Find stored information about the process - PInfos = State#state.pinfos, - {value, PInfo} = lists:keysearch(Pid, #pinfo.pid, PInfos), - - %% Update process information - PInfo2 = PInfo#pinfo{status=Status}, - PInfos2 = lists:keyreplace(Pid, #pinfo.pid, PInfos, PInfo2), - State2 = State#state{pinfos=PInfos2}, - - %% Update window - dbg_ui_mon_win:update_process(State2#state.win, Pid, Status, Info), - case State2#state.focus of - #pinfo{pid=Pid} -> - gui_enable_functions(PInfo2), - State2#state{focus=PInfo2}; - _ -> - State2 - end; - -int_cmd({new_break, Break}, State) -> - Win = dbg_ui_mon_win:add_break(State#state.win, 'Break', Break), - State#state{win=Win}; -int_cmd({delete_break, Point}, State) -> - Win = dbg_ui_mon_win:delete_break(State#state.win, Point), - State#state{win=Win}; -int_cmd({break_options, Break}, State) -> - dbg_ui_mon_win:update_break(State#state.win, Break), - State; -int_cmd(no_break, State) -> - Win = dbg_ui_mon_win:clear_breaks(State#state.win), - State#state{win=Win}; -int_cmd({no_break, Mod}, State) -> - Win = dbg_ui_mon_win:clear_breaks(State#state.win, Mod), - State#state{win=Win}; - -int_cmd({auto_attach, AutoAttach}, State) -> - OnFlags = case AutoAttach of - false -> []; - {Flags, _Function} -> Flags - end, - OffFlags = [init, exit, break] -- OnFlags, - dbg_ui_mon_win:show_option(State#state.win, auto_attach, OnFlags), - lists:foreach(fun(Flag) -> - dbg_ui_mon_win:select(map(Flag), true) - end, - OnFlags), - lists:foreach(fun(Flag) -> - dbg_ui_mon_win:select(map(Flag), false) - end, - OffFlags), - State#state{attach=AutoAttach}; -int_cmd({stack_trace, Flag}, State) -> - dbg_ui_mon_win:show_option(State#state.win, stack_trace, Flag), - dbg_ui_mon_win:select(map(Flag), true), - State. - - -%%==================================================================== -%% GUI auxiliary functions -%%==================================================================== - -menus() -> - [{'File', [{'Load Settings...', 0}, - {'Save Settings...', 2}, - separator, - {'Exit', 0}]}, - {'Edit', [{'Refresh', no}, - {'Kill All', no}]}, - {'Module', [{'Interpret...', 0}, - {'Delete All Modules', no}, - separator]}, - {'Process', [{'Step', 0}, - {'Next', 0}, - {'Continue', 0}, - {'Finish ', 0}, - separator, - {'Attach', 0}, - {'Kill', no}]}, - {'Break', [{'Line Break...', 5}, - {'Conditional Break...', no}, - {'Function Break...', no}, - separator, - {'Enable All', no}, - {'Disable All', no}, - {'Delete All', 0}, - separator]}, - {'Options', [{'Trace Window', no, cascade, - [{'Button Area', no, check}, - {'Evaluator Area', no, check}, - {'Bindings Area', no, check}, - {'Trace Area', no, check}]}, - {'Auto Attach', no, cascade, - [{'First Call', no, check}, - {'On Break', no, check}, - {'On Exit', no, check}]}, - {'Stack Trace', no, cascade, - [{'Stack On, Tail', no, radio}, - {'Stack On, No Tail', no, radio}, - {'Stack Off', no, radio}]}, - {'Back Trace Size...', no}]}, - {'Help', [{'Debugger', no}]}]. - -menus(enabled, undefined) -> - []; -menus(disabled, undefined) -> - ['Step','Next','Continue','Finish ','Attach','Kill']; -menus(enabled, #pinfo{status=exit}) -> - ['Attach']; -menus(disabled, #pinfo{status=exit}) -> - ['Step','Next','Continue','Finish ','Kill']; -menus(enabled, #pinfo{status=break}) -> - ['Step','Next','Continue','Finish ','Attach','Kill']; -menus(disabled, #pinfo{status=break}) -> - []; -menus(enabled, _PInfo) -> - ['Attach','Kill']; -menus(disabled, _PInfo) -> - ['Step','Next','Continue','Finish ']. - -shortcut(l) -> {always, 'Load Settings...'}; -shortcut(v) -> {always, 'Save Settings...'}; -shortcut(e) -> {always, 'Exit'}; - -shortcut(i) -> {always, 'Interpret...'}; - -shortcut(s) -> {if_enabled, 'Step'}; -shortcut(n) -> {if_enabled, 'Next'}; -shortcut(c) -> {if_enabled, 'Continue'}; -shortcut(f) -> {if_enabled, 'Finish '}; -shortcut(a) -> {if_enabled, 'Attach'}; - -shortcut(b) -> {always, 'Line Break...'}; -shortcut(d) -> {always, 'Delete All'}; - -shortcut(_) -> false. - -%% Enable/disable functionality depending on the state of the process -%% currently in Focus -gui_enable_functions(PInfo) -> - Enabled = menus(enabled, PInfo), - Disabled = menus(disabled, PInfo), - dbg_ui_mon_win:enable(Enabled, true), - dbg_ui_mon_win:enable(Disabled, false). - -%% Map values used by int to/from GUI names -map('First Call') -> init; % Auto attach -map('On Exit') -> exit; -map('On Break') -> break; -map(init) -> 'First Call'; -map(exit) -> 'On Exit'; -map(break) -> 'On Break'; - -map('Stack On, Tail') -> all; % Stack trace -map('Stack On, No Tail') -> no_tail; -map('Stack Off') -> false; -map(all) -> 'Stack On, Tail'; -map(true) -> 'Stack On, Tail'; -map(no_tail) -> 'Stack On, No Tail'; -map(false) -> 'Stack Off'. - - -%%==================================================================== -%% Debugger settings -%%==================================================================== - -load_settings(SFile, State) -> - case file:read_file(SFile) of - {ok, Binary} -> - case catch binary_to_term(Binary) of - {debugger_settings, Settings} -> - load_settings2(Settings, - State#state{sfile=SFile, - changed=false}); - _Error -> State - end; - {error, _Reason} -> State - end. - -load_settings2(Settings, State) -> - {TraceWin, AutoAttach, StackTrace, BackTrace, Files, Breaks} = - Settings, - - TraceWinAll = ['Button Area', 'Evaluator Area', 'Bindings Area', - 'Trace Area'], - lists:foreach(fun(Area) -> dbg_ui_mon_win:select(Area, true) end, - TraceWin), - lists:foreach(fun(Area) -> dbg_ui_mon_win:select(Area, false) end, - TraceWinAll--TraceWin), - - case AutoAttach of - false -> int:auto_attach(false); - {Flags, Function} -> int:auto_attach(Flags, Function) - end, - - int:stack_trace(StackTrace), - - dbg_ui_mon_win:show_option(State#state.win, back_trace, BackTrace), - - case State#state.mode of - local -> lists:foreach(fun(File) -> int:i(File) end, Files); - global -> lists:foreach(fun(File) -> int:ni(File) end, Files) - end, - lists:foreach(fun(Break) -> - {{Mod, Line}, [Status, Action, _, Cond]} = - Break, - int:break(Mod, Line), - if - Status =:= inactive -> - int:disable_break(Mod, Line); - true -> ignore - end, - if - Action/=enable -> - int:action_at_break(Mod,Line,Action); - true -> ignore - end, - case Cond of - CFunction when is_tuple(CFunction) -> - int:test_at_break(Mod,Line,CFunction); - null -> ignore - end - end, - Breaks), - - State#state{tracewin=TraceWin, backtrace=BackTrace}. - -save_settings(SFile, State) -> - Settings = {State#state.tracewin, - int:auto_attach(), - int:stack_trace(), - State#state.backtrace, - [int:file(Mod) || Mod <- int:interpreted()], - int:all_breaks()}, - Binary = term_to_binary({debugger_settings, Settings}), - case file:write_file(SFile, Binary) of - ok -> - State#state{sfile=SFile, changed=false}; - {error, _Reason} -> - State - end. - - -%%==================================================================== -%% Other internal functions -%%==================================================================== - -registered_name(Pid) -> - %% Yield in order to give Pid more time to register its name - timer:sleep(200), - - Node = node(Pid), - if - Node =:= node() -> - case erlang:process_info(Pid, registered_name) of - {registered_name, Name} -> Name; - _ -> undefined - end; - true -> - case rpc:call(Node,erlang,process_info, - [Pid,registered_name]) of - {registered_name, Name} -> Name; - _ -> undefined - end - end. - -trace_function(State) -> - {dbg_ui_trace, start, [State#state.tracewin,State#state.backtrace]}. diff --git a/lib/debugger/src/dbg_ui_mon_win.erl b/lib/debugger/src/dbg_ui_mon_win.erl deleted file mode 100644 index 8655c7697a..0000000000 --- a/lib/debugger/src/dbg_ui_mon_win.erl +++ /dev/null @@ -1,573 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2012. 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(dbg_ui_mon_win). --compile([{nowarn_deprecated_function,{gs,checkbutton,2}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,frame,2}}, - {nowarn_deprecated_function,{gs,grid,2}}, - {nowarn_deprecated_function,{gs,gridline,2}}, - {nowarn_deprecated_function,{gs,label,2}}, - {nowarn_deprecated_function,{gs,listbox,2}}, - {nowarn_deprecated_function,{gs,menu,2}}, - {nowarn_deprecated_function,{gs,menubar,2}}, - {nowarn_deprecated_function,{gs,menuitem,2}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,window,2}}]). - -%% External exports --export([init/0]). --export([create_win/3, get_window/1, - show_option/3, - enable/2, is_enabled/1, select/2, - add_module/3, delete_module/2, - add_process/6, update_process/4, clear_processes/1, - add_break/3, update_break/2, delete_break/2, - clear_breaks/1, clear_breaks/2, - handle_event/2 - ]). - --define(default_rows,50). - --record(moduleInfo, {module, menubtn}). --record(procInfo, {pid, row}). --record(breakInfo, {point, status, break}). --record(winInfo, {window, % gsobj() - grid, % gsobj() - row, % int() Last row in grid - - focus, % int() Selected row in grid - - modules=[], % [#moduleInfo{}] Known modules - processes=[], % [#procInfo{}] Known processes - breaks=[], % [#breakInfo{}] Known breakpoints - - listbox, % gsobj() Listinng known modules - - %% Auto attach buttons - fbutton, % gsobj() - bbutton, % gsobj() - ebutton, % gsobj() - selected=[], % ['First Call'|'On Break'|'On Exit'] - - slabel, % showing Stack Trace option - blabel % showing Back Trace Size - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -init() -> - dbg_ui_win:init(). - -%%-------------------------------------------------------------------- -%% create_win(GS, Title, Menus) -> #winInfo{} -%% GS = gsobj() -%% Title = string() -%% Menus = [menu()] See dbg_ui_win.erl -%%-------------------------------------------------------------------- - --define(PAD, 5). --define(Wf, 150). --define(Wg, 770). --define(W, 800). --define(H, 390). - -create_win(GS, Title, Menus) -> - Win = gs:window(GS, [{title, Title}, - {width, ?W}, {height, ?H}, - {configure,true}, {destroy,true}, - {keypress,true}, {motion,true}]), - - MenuBar = gs:menubar(Win, []), - dbg_ui_win:create_menus(MenuBar, Menus), - dbg_ui_winman:windows_menu(MenuBar), - - Font = dbg_ui_win:font(normal), - - Frame = gs:frame(Win, [{x, ?PAD}, {y, 30}, - {width, ?Wf}, {height, ?H}]), - Hlb = 200, - Listbox = gs:listbox(Frame, [{x, 0}, {y, 0}, - {width, ?Wf}, {height, Hlb}, - {data, listbox}, - {doubleclick, true}, - {items, []}]), - gs:label(Frame, [{x, 0}, {y, Hlb}, {width, ?Wf}, {height, 20}, - {align, w}, - {label, {text, "Auto Attach:"}}, {font, Font}]), - Fbtn = gs:checkbutton(Frame, [{x, 0}, {y, Hlb+20}, - {width, ?Wf}, {height, 20}, - {label, {text, 'First Call'}}, - {align, w}, {font, Font}, - {data, autoattach}]), - Bbtn = gs:checkbutton(Frame, [{x, 0}, {y, Hlb+40}, - {width, ?Wf}, {height, 20}, - {label, {text, 'On Break'}}, - {align, w}, {font, Font}, - {data, autoattach}]), - Ebtn = gs:checkbutton(Frame, [{x, 0}, {y, Hlb+60}, - {width, ?Wf}, {height, 20}, - {label, {text, 'On Exit'}}, - {align, w}, {font, Font}, - {data, autoattach}]), - SLabel = gs:label(Frame, [{x, 0}, {y, Hlb+80}, - {width, ?Wf}, {height, 40}, - {font, Font}, {align, w}]), - BLabel = gs:label(Frame, [{x, 0}, {y, Hlb+120}, - {width, ?Wf}, {height, 40}, - {font, Font}, {align, w}]), - - Grid = gs:grid(Win, [{x, 2*?PAD+?Wf}, {y, 30}, - {width, ?W-(2*?PAD+?Wf)}, {height, ?H-30}, - {bg, grey}, {fg, black}, - {vscroll, right}, {hscroll, bottom}, - calc_columnwidths(?Wg), - {rows, {1,?default_rows}}]), - gs:gridline(Grid, [{row, 1}, {bw, 5}, {fg, blue}, - {font, Font}, - {text, {1,"Pid"}}, {text, {2,"Initial Call"}}, - {text, {3,"Name"}}, {text, {4,"Status"}}, - {text, {5,"Information"}}]), - - gs:config(Win, {map, true}), - #winInfo{window=Win, grid=Grid, row=1, focus=0, - listbox=Listbox, - fbutton=Fbtn, bbutton=Bbtn, ebutton=Ebtn, - slabel=SLabel, blabel=BLabel}. - -%%-------------------------------------------------------------------- -%% get_window(WinInfo) -> Window -%% WinInfo = #winInfo{} -%% Window = gsobj() -%%-------------------------------------------------------------------- -get_window(WinInfo) -> - WinInfo#winInfo.window. - -%%-------------------------------------------------------------------- -%% show_option(WinInfo, Option, Value) -> void() -%% WinInfo = #winInfo{} -%% Option = auto_attach | stack_trace | back_trace -%% Value = [Flag] % Option==auto_attach -%% Flag = init | break | exit -%% | true | all | no_tail | false % Option==stack_trace -%% | int() % Option==back_trace -%%-------------------------------------------------------------------- -show_option(WinInfo, Option, Value) -> - case Option of - - auto_attach -> - lists:foreach(fun (Button) -> - gs:config(Button, {select, false}) - end, - option_buttons(WinInfo, [init, break, exit])), - lists:foreach(fun (Button) -> - gs:config(Button, {select, true}) - end, - option_buttons(WinInfo, Value)); - - stack_trace -> - Text = case Value of - all -> "Stack Trace:\n On (with tail)"; - true -> "Stack Trace:\n On (with tail)"; - no_tail -> "Stack Trace:\n On (no tail)"; - false -> "Stack Trace:\n Off" - end, - gs:config(WinInfo#winInfo.slabel, {label, {text, Text}}); - - back_trace -> - Text = "Back Trace Size:\n " ++ integer_to_list(Value), - gs:config(WinInfo#winInfo.blabel, {label, {text, Text}}) - end. - -option_buttons(WinInfo, [init|Flags]) -> - [WinInfo#winInfo.fbutton|option_buttons(WinInfo, Flags)]; -option_buttons(WinInfo, [break|Flags]) -> - [WinInfo#winInfo.bbutton|option_buttons(WinInfo, Flags)]; -option_buttons(WinInfo, [exit|Flags]) -> - [WinInfo#winInfo.ebutton|option_buttons(WinInfo, Flags)]; -option_buttons(_WinInfo, []) -> - []. - -%%-------------------------------------------------------------------- -%% enable([MenuItem], Bool) -%% is_enabled(MenuItem) -> Bool -%% MenuItem = atom() -%% Bool = boolean() -%%-------------------------------------------------------------------- -enable(MenuItems, Bool) -> - lists:foreach(fun(MenuItem) -> - gs:config(MenuItem, {enable, Bool}) - end, - MenuItems). - -is_enabled(MenuItem) -> - gs:read(MenuItem, enable). - -%%-------------------------------------------------------------------- -%% select(MenuItem, Bool) -%% MenuItem = atom() -%% Bool = boolean() -%%-------------------------------------------------------------------- -select(MenuItem, Bool) -> - dbg_ui_win:select(MenuItem, Bool). - -%%-------------------------------------------------------------------- -%% add_module(WinInfo, Name, Mod) -> WinInfo -%% WinInfo = #winInfo{} -%% Name = atom() -%% Mod = atom() -%%-------------------------------------------------------------------- -add_module(WinInfo, Menu, Mod) -> - Modules = WinInfo#winInfo.modules, - case lists:keymember(Mod, #moduleInfo.module, Modules) of - false -> - %% Create a menu for the module - Font = dbg_ui_win:font(normal), - MenuBtn = gs:menuitem(Menu, [{label, {text,Mod}}, - {font, Font}, - {itemtype, cascade}]), - SubMenu = gs:menu(MenuBtn, []), - gs:menuitem(SubMenu, [{label, {text,"View"}}, - {font, Font}, - {data, {module,Mod,view}}]), - gs:menuitem(SubMenu, [{label, {text,"Delete"}}, - {font, Font}, - {data, {module,Mod,delete}}]), - - %% Add the module to the listbox - gs:config(WinInfo#winInfo.listbox, {add, Mod}), - - ModInfo = #moduleInfo{module=Mod, menubtn=MenuBtn}, - WinInfo#winInfo{modules=[ModInfo | Modules]}; - true -> WinInfo - end. - -%%-------------------------------------------------------------------- -%% delete_module(WinInfo, Mod) -> WinInfo -%% WinInfo = #winInfo{} -%% Mod = atom() -%%-------------------------------------------------------------------- -delete_module(WinInfo, Mod) -> - {value, ModInfo} = lists:keysearch(Mod, #moduleInfo.module, - WinInfo#winInfo.modules), - gs:destroy(ModInfo#moduleInfo.menubtn), - delete_module(WinInfo#winInfo.listbox, atom_to_list(Mod), 0), - WinInfo#winInfo{modules=lists:keydelete(Mod, #moduleInfo.module, - WinInfo#winInfo.modules)}. - -delete_module(Listbox, ModS, Index) -> - case gs:read(Listbox, {get, Index}) of - ModS -> - gs:config(Listbox, {del, Index}); - _OtherModS -> - delete_module(Listbox, ModS, Index+1) - end. - -%%-------------------------------------------------------------------- -%% add_process(WinInfo, Pid, Name, Function, Status, Info) -> WinInfo -%% WinInfo = #winInfo{} -%% Pid = pid() -%% Name = undefined | atom() -%% Function = {Mod, Func, Args} -%% Status = idle | running | break | exit -%% Info = {} | term() -%%-------------------------------------------------------------------- -add_process(WinInfo, Pid, Name, {Mod,Func,Args}, Status, Info) -> - Grid = WinInfo#winInfo.grid, - Row = (WinInfo#winInfo.row)+1, - GridLine = case gs:read(Grid, {obj_at_row, Row}) of - undefined -> - if Row>?default_rows -> - gs:config(Grid,[{rows,{1,Row}}]); - true -> ok - end, - gs:gridline(Grid,[{row,Row}, {bw,5}, {fg,black}, - {font,dbg_ui_win:font(normal)}, - {click, true}, - {doubleclick, true}]); - GSObj -> - GSObj - end, - Name2 = case Name of undefined -> ""; _ -> Name end, - FuncS = io_lib:format("~w:~w/~w", [Mod, Func, length(Args)]), - Info2 = case Info of {} -> ""; _ -> Info end, - Options = [{text, {1,Pid}}, {text, {2,FuncS}}, {text, {3,Name2}}, - {text, {4,Status}}, {text, {5,Info2}}, - {data, {gridline, Pid}}], - gs:config(GridLine, Options), - - ProcInfo = #procInfo{pid=Pid, row=Row}, - WinInfo#winInfo{processes=[ProcInfo|WinInfo#winInfo.processes], - row=Row}. - -%%-------------------------------------------------------------------- -%% update_process(WinInfo, Pid, Status, Info) -%% WinInfo = #winInfo{} -%% Pid = pid() -%% Status = idle | running | break | exit -%% Info = {} | term() -%%-------------------------------------------------------------------- -update_process(WinInfo, Pid, Status, Info) -> - {value, ProcInfo} = lists:keysearch(Pid, #procInfo.pid, - WinInfo#winInfo.processes), - - Grid = WinInfo#winInfo.grid, - GridLine = gs:read(Grid, {obj_at_row, ProcInfo#procInfo.row}), - - Info2 = case Info of {} -> ""; _ -> Info end, - gs:config(GridLine, [{text, {4,Status}}, {text, {5,Info2}}]). - -%%-------------------------------------------------------------------- -%% clear_processes(WinInfo) -> WinInfo -%% WinInfo = #winInfo{} -%%-------------------------------------------------------------------- -clear_processes(WinInfo) -> - Grid = WinInfo#winInfo.grid, - Max = WinInfo#winInfo.row, - clear_processes(Grid, 2, Max), - gs:config(Grid,[{rows,{1,?default_rows}}]), - WinInfo#winInfo{row=1, focus=0, processes=[]}. - -clear_processes(Grid, Row, Max) when Row=<Max -> - GridLine = gs:read(Grid, {obj_at_row, Row}), - case gs:read(GridLine,{text,4}) of - "exit" -> - Pid = list_to_pid(gs:read(GridLine,{text,1})), - dbg_ui_winman:clear_process(dbg_ui_trace:title(Pid)); - _ -> - ok - end, - - Options = [{fg, black}, - {{text,1}, ""}, {{text,2},""}, {{text,3},""}, - {{text,4}, ""}, {{text,5},""}, - {data, []}], - gs:config(GridLine, Options), - clear_processes(Grid, Row+1, Max); -clear_processes(_Grid, Row, Max) when Row>Max -> - done. - -%%-------------------------------------------------------------------- -%% add_break(WinInfo, Name, {Point, Options}) -> WinInfo -%% WinInfo = #winInfo{} -%% Name = atom() -%% Point = {Mod, Line} -%% Options = [Status, Action, Mods, Cond] -%% Status = active | inactive -%% Action = enable | disable | delete -%% Mods = null (not used) -%% Cond = null | {Mod, Func} -%%-------------------------------------------------------------------- -add_break(WinInfo, Menu, {Point, Options}) -> - Break = dbg_ui_win:add_break(Menu, Point), - dbg_ui_win:update_break(Break, Options), - BreakInfo = #breakInfo{point=Point, break=Break}, - WinInfo#winInfo{breaks=[BreakInfo|WinInfo#winInfo.breaks]}. - -%%-------------------------------------------------------------------- -%% update_break(WinInfo, {Point, Options}) -%% WinInfo = #winInfo{} -%% Point = {Mod, Line} -%% Options = [Status, Action, Mods, Cond] -%% Status = active | inactive -%% Action = enable | disable | delete -%% Mods = null (not used) -%% Cond = null | {Mod, Func} -%%-------------------------------------------------------------------- -update_break(WinInfo, {Point, Options}) -> - {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, - WinInfo#winInfo.breaks), - dbg_ui_win:update_break(BreakInfo#breakInfo.break, Options). - -%%-------------------------------------------------------------------- -%% delete_break(WinInfo, Point) -> WinInfo -%% WinInfo = #winInfo{} -%% Point = {Mod, Line} -%%-------------------------------------------------------------------- -delete_break(WinInfo, Point) -> - {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, - WinInfo#winInfo.breaks), - dbg_ui_win:delete_break(BreakInfo#breakInfo.break), - WinInfo#winInfo{breaks=lists:keydelete(Point, #breakInfo.point, - WinInfo#winInfo.breaks)}. - -%%-------------------------------------------------------------------- -%% clear_breaks(WinInfo) -> WinInfo -%% clear_breaks(WinInfo, Mod) -> WinInfo -%% WinInfo = #winInfo{} -%%-------------------------------------------------------------------- -clear_breaks(WinInfo) -> - lists:foreach(fun(BreakInfo) -> - dbg_ui_win:delete_break(BreakInfo#breakInfo.break) - end, - WinInfo#winInfo.breaks), - WinInfo#winInfo{breaks=[]}. -clear_breaks(WinInfo, Mod) -> - Fun = - fun(BreakInfo) -> - case BreakInfo#breakInfo.point of - {Mod, _Line} -> - dbg_ui_win:delete_break(BreakInfo#breakInfo.break), - false; - _ -> true - end - end, - Breaks = lists:filter(Fun, WinInfo#winInfo.breaks), - WinInfo#winInfo{breaks=Breaks}. - -%%-------------------------------------------------------------------- -%% handle_event(GSEvent, WinInfo) -> Command -%% GSEvent = {gs, Id, Event, Data, Arg} -%% WinInfo = #winInfo{} -%% Command = ignore -%% | stopped -%% | {coords, {X,Y}} -%% -%% | {shortcut, Key} -%% | MenuItem | {Menu, [MenuItem]} -%% MenuItem = Menu = atom() -%% | {break, Point, What} -%% What = delete | {status, Status} | {trigger, Trigger} -%% | {module, Mod, What} -%% What = view | delete -%% -%% | {focus, Pid, WinInfo} -%% | default -%%-------------------------------------------------------------------- -%% Window events -handle_event({gs, _Id, configure, _Data, [W, H |_]}, WinInfo) -> - configure(WinInfo, {W, H}), - ignore; -handle_event({gs, _Id, destroy, _Data, _Arg}, _WinInfo) -> - stopped; -handle_event({gs, _Id, motion, _Data, [X,Y]}, WinInfo) -> - {LastX, LastY} = dbg_ui_win:motion(X, Y), - Win = WinInfo#winInfo.window, - {coords, {gs:read(Win, x)+LastX-5, gs:read(Win, y)+LastY-5}}; - -%% Menus and keyboard shortcuts -handle_event({gs, _Id, keypress, _Data, [Key,_,_,1]}, _WinInfo) when - Key/='Up', Key/='Down', Key/=p, Key/=n -> - {shortcut, Key}; -handle_event({gs, _Id, click, {dbg_ui_winman, Win}, _Arg}, _WinInfo) -> - dbg_ui_winman:raise(Win), - ignore; -handle_event({gs, _Id, click, {menuitem, Name}, _Arg}, _WinInfo) -> - Name; -handle_event({gs, _Id, click, {menu, Menu}, _Arg}, _WinInfo) -> - Names = dbg_ui_win:selected(Menu), - {Menu, Names}; -handle_event({gs, _Id, click, {break, Point, What}, _Arg}, _WinInfo) -> - {break, Point, What}; -handle_event({gs, _Id, click, {module, Mod, What}, _Arg}, _WinInfo) -> - {module, Mod, What}; - -%% Listbox -handle_event({gs, _Id, doubleclick, listbox, [_Index, ModS|_]}, _WI) -> - {module, list_to_atom(ModS), view}; - -%% Auto attach buttons -handle_event({gs, _Id, click, autoattach, _Arg}, WinInfo) -> - Names = lists:foldl(fun (Button, NamesAcc) -> - case gs:read(Button, select) of - true -> - {text, Name} = - gs:read(Button, label), - [list_to_atom(Name)|NamesAcc]; - false -> - NamesAcc - end - end, - [], - [WinInfo#winInfo.ebutton, - WinInfo#winInfo.bbutton, - WinInfo#winInfo.fbutton]), - {'Auto Attach', Names}; - -%% Process grid -handle_event({gs, _Id, keypress, _Data, [Key|_]}, WinInfo) when - Key =:= 'Up'; Key =:= 'Down' -> - Dir = if Key =:= 'Up' -> up; Key =:= 'Down' -> down end, - Row = move(WinInfo, Dir), - if Row>1 -> - WinInfo2 = highlight(WinInfo, Row), - #procInfo{pid=Pid} = - lists:keyfind(Row, #procInfo.row, WinInfo#winInfo.processes), - {focus, Pid, WinInfo2}; - true -> - ignore - end; -handle_event({gs, _Id, click, {gridline, Pid}, [_Col,Row|_]}, WinInfo) -> - WinInfo2 = highlight(WinInfo, Row), - {focus, Pid, WinInfo2}; -handle_event({gs, _Id, doubleclick, _Data, _Arg}, _WinInfo) -> - default; - -handle_event(_GSEvent, _WinInfo) -> - ignore. - -move(WinInfo, Dir) -> - Row = WinInfo#winInfo.focus, - Last = WinInfo#winInfo.row, - if - Dir =:= up, Row > 1 -> Row-1; - Dir =:= down, Row < Last -> Row+1; - true -> Row - end. - -highlight(WinInfo, Row) -> - Grid = WinInfo#winInfo.grid, - case WinInfo#winInfo.focus of - 0 -> ignore; - Focus -> - GridLine1 = gs:read(Grid, {obj_at_row, Focus}), - gs:config(GridLine1, {fg, black}) - end, - GridLine2 = gs:read(Grid, {obj_at_row, Row}), - gs:config(GridLine2, {fg, white}), - WinInfo#winInfo{focus=Row}. - -%%==================================================================== -%% Internal functions -%%==================================================================== - -configure(WinInfo, {W, H}) -> - Grid = WinInfo#winInfo.grid, - NewW = W - (2*?PAD+?Wf), - Dx = NewW - gs:read(Grid, width), - Dy = H-42 - gs:read(Grid, height), - if - (Dx+Dy) =/= 0 -> - gs:config(Grid, [{width, NewW}, {height, H-30}]), - Cols = calc_columnwidths(NewW), - gs:config(Grid, Cols); - true -> - ok - end. - -calc_columnwidths(Width) -> - W = if - Width =< ?Wg -> ?Wg; - true -> Width - end, - First = [round(X) || X <- [0.13*W, 0.27*W, 0.18*W, 0.18*W]], - Last = W - lists:sum(First) - 30, - {columnwidths, First++[Last]}. diff --git a/lib/debugger/src/dbg_ui_settings.erl b/lib/debugger/src/dbg_ui_settings.erl deleted file mode 100644 index fcfd67966f..0000000000 --- a/lib/debugger/src/dbg_ui_settings.erl +++ /dev/null @@ -1,162 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2011. 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(dbg_ui_settings). - --include_lib("kernel/include/file.hrl"). - -%% External exports --export([start/4]). - -%% Internal exports --export([init/6]). - -%% OTP-6011 What's denoted gs="Graphics system id" is now in fact -%% the object id of the monitor window. --record(state, {gs, % term() Graphics system id - win, % term() Settings dialog window data - monitor, % pid() Monitor pid - action % load | save - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(GS, Pos, Action, SFile) -%% GS = Graphics system id -%% Pos = {X,Y} -%% Action = load | save -%% SFile = default | string() -%%-------------------------------------------------------------------- -start(GS, Pos, Action, SFile) -> - Title = case Action of - load -> "Load Settings Dialog"; - save -> "Save Settings Dialog" - end, - case dbg_ui_winman:is_started(Title) of - true -> ignore; - false -> - spawn(?MODULE, init, [self(), GS, Pos, Title, Action, SFile]) - end. - -%%==================================================================== -%% Internal exports -%%==================================================================== - -init(Monitor, GS, Pos, Title, Action, SFile) -> - {SDir, SFileName} = - if - %% If settings are saved for the first time, and to - %% the default directory HOME/erlang.tools/debugger, - %% make sure the directory exists, or create it if - %% desired and possible - SFile==default -> {default_settings_dir(GS), "NoName.state"}; - true -> {filename:dirname(SFile), filename:basename(SFile)} - end, - - Filter = filename:join(SDir, "*.state"), - Extra = fun(_File) -> true end, - - %% Create window - Win = case Action of - load -> - dbg_ui_filedialog_win:create_win(GS, Title, Pos, normal, - Filter, Extra); - save -> - dbg_ui_filedialog_win:create_win(GS, Title, Pos, normal, - Filter, Extra, SFileName) - end, - Window = dbg_ui_filedialog_win:get_window(Win), - dbg_ui_winman:insert(Title, Window), - - State = #state{gs=GS, win=Win, monitor=Monitor, action=Action}, - loop(State). - - -%%==================================================================== -%% Main loop and message handling -%%==================================================================== - -loop(State) -> - receive - - %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> - Cmd = dbg_ui_filedialog_win:handle_event(GuiEvent, - State#state.win), - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the dbg_ui_winman process (Debugger window manager) - {dbg_ui_winman, update_windows_menu, _Data} -> - loop(State); - {dbg_ui_winman, destroy} -> - exit(normal) - end. - -gui_cmd(ignore, State) -> - State; -gui_cmd({stopped, _Dir}, _State) -> - exit(normal); -gui_cmd({win, Win}, State) -> - State#state{win=Win}; -gui_cmd({select, File}, State) -> - State#state.monitor ! {dbg_ui_settings, File, State#state.action}, - exit(normal). - - -%%==================================================================== -%% Internal functions -%%==================================================================== - -default_settings_dir(GS) -> - {ok, [[Home]]} = init:get_argument(home), - DefDir = filename:join([Home, ".erlang_tools", "debugger"]), - - case filelib:is_dir(DefDir) of - true -> DefDir; - false -> - {ok, CWD} = file:get_cwd(), - - Msg = ["Default directory", DefDir, "does not exist.", - "Click OK to create it or", - "Cancel to use other directory."], - case tool_utils:confirm(GS, Msg) of - ok -> - ToolsDir = filename:dirname(DefDir), - case filelib:is_dir(ToolsDir) of - true -> - case file:make_dir(DefDir) of - ok -> DefDir; - _Error -> CWD - end; - false -> - case file:make_dir(ToolsDir) of - ok -> - case file:make_dir(DefDir) of - ok -> DefDir; - _Error -> CWD - end; - _Error -> CWD - end - end; - cancel -> CWD - end - end. diff --git a/lib/debugger/src/dbg_ui_trace.erl b/lib/debugger/src/dbg_ui_trace.erl deleted file mode 100644 index 3e1fb2dcae..0000000000 --- a/lib/debugger/src/dbg_ui_trace.erl +++ /dev/null @@ -1,814 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-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(dbg_ui_trace). - -%% External exports --export([start/1, start/3]). --export([title/1]). - --define(TRACEWIN, ['Button Area', 'Evaluator Area', 'Bindings Area']). --define(BACKTRACE, 100). - --record(state, {gs, % term() Graphics system id - win, % term() Attach process window data - coords, % {X,Y} Mouse point position - - pid, % pid() Debugged process - meta, % pid() Meta process - status, % {Status,Mod,Line} | {exit,Where,Reason} - % Status = init | idle | break - % | wait_break | wait_running - % | running - % Where={Mod,Line} | null - - cm, % atom() | undefined Current module - cm_obsolete=false, % boolean() Curr mod needs reloading - - stack, % {Cur,Max} - - trace, % boolean() - stack_trace, % all | no_tail | false - backtrace % integer() #call frames to fetch - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(Pid) -%% start(Pid, TraceWin, BackTrace) -%% Pid = pid() -%% TraceWin = [WinArea] -%% WinArea = 'Button|Evaluator|Bindings|Trace Area' -%% Backtrace = integer() -%%-------------------------------------------------------------------- -start(Pid) -> % Used by debugger:quick/3 (no monitor) - start(Pid, ?TRACEWIN, ?BACKTRACE). -start(Pid, TraceWin, BackTrace) -> - case {whereis(dbg_wx_mon), whereis(dbg_ui_mon)} of - {undefined, undefined} -> - case which_gui() of - gs -> - start2(Pid, TraceWin, BackTrace); - wx -> - dbg_wx_trace:start(Pid, TraceWin, BackTrace) - end; - {undefined, Monitor} when is_pid(Monitor) -> - start2(Pid, TraceWin, BackTrace); - {Monitor, _} when is_pid(Monitor) -> - dbg_wx_trace:start(Pid, TraceWin, BackTrace) - end. - -start2(Pid, TraceWin, BackTrace) -> - %% Inform int about my existence and get the meta pid back - case int:attached(Pid) of - {ok, Meta} -> - init(Pid, Meta, TraceWin, BackTrace); - error -> - ignore - end. - -which_gui() -> - try - wx:new(), - wx:destroy(), - wx - catch _:_ -> - gs - end. - -%%-------------------------------------------------------------------- -%% title(Pid) -> string() -%% By exporting this function, dbg_ui_mon may check with dbg_ui_winman -%% if there already is an attach window for a given pid and thus avoid -%% spawning processes unnecessarily. -%%-------------------------------------------------------------------- -title(Pid) -> - "Attach Process " ++ pid_to_list(Pid). - - -%%==================================================================== -%% Main loop and message handling -%%==================================================================== - -init(Pid, Meta, TraceWin, BackTrace) -> - - %% Start necessary stuff - GS = dbg_ui_trace_win:init(), % Graphics system - - %% Create attach process window - Title = title(Pid), - Win = dbg_ui_trace_win:create_win(GS, Title, TraceWin, menus()), - Window = dbg_ui_trace_win:get_window(Win), - dbg_ui_winman:insert(Title, Window), - - %% Initial process state - State1 = #state{gs=GS, win=Win, coords={0,0}, pid=Pid, meta=Meta, - status={idle,null,null}, - stack={1,1}}, - - State2 = init_options(TraceWin, - int:stack_trace(), % Stack Trace - BackTrace, % Back trace size - State1), - - State3 = init_contents(int:all_breaks(), % Breakpoints - State2), - - int:meta(Meta, trace, State3#state.trace), - - gui_enable_updown(stack_trace, {1,1}), - gui_enable_btrace(false, false), - dbg_ui_trace_win:display(idle), - - loop(State3). - -init_options(TraceWin, StackTrace, BackTrace, State) -> - lists:foreach(fun(Area) -> dbg_ui_trace_win:select(Area, true) end, - TraceWin), - - Trace = lists:member('Trace Area', TraceWin), - - dbg_ui_trace_win:select(map(StackTrace), true), - - %% Backtrace size is (currently) not shown in window - - State#state{trace=Trace,stack_trace=StackTrace,backtrace=BackTrace}. - -init_contents(Breaks, State) -> - Win = - lists:foldl(fun(Break, Win) -> - dbg_ui_trace_win:add_break(Win, - 'Break',Break) - end, - State#state.win, - Breaks), - - State#state{win=Win}. - -loop(#state{meta=Meta} = State) -> - receive - - %% From the GUI main window - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> - Cmd = - dbg_ui_trace_win:handle_event(GuiEvent,State#state.win), - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the GUI help windows - {gui, Cmd} -> - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the interpreter - {int, Cmd} -> - State2 = int_cmd(Cmd, State), - loop(State2); - - %% From the meta process - {Meta, Cmd} -> - State2 = meta_cmd(Cmd, State), - loop(State2); - {NewMeta, {exit_at, Where, Reason, Cur}} -> - State2 = meta_cmd({exit_at, Where, Reason, Cur}, - State#state{meta=NewMeta}), - loop(State2); - - %% From the dbg_ui_edit process - {dbg_ui_edit, 'Backtrace:', BackTrace} -> - loop(State#state{backtrace=BackTrace}); - {dbg_ui_edit, Var, Val} -> - Cmd = atom_to_list(Var)++"="++io_lib:format("~p", [Val]), - State2 = gui_cmd({user_command, lists:flatten(Cmd)}, State), - loop(State2); - - %% From the dbg_ui_winman process (Debugger window manager) - {dbg_ui_winman, update_windows_menu, Data} -> - dbg_ui_winman:update_windows_menu(Data), - loop(State); - {dbg_ui_winman, destroy} -> - exit(stop) - end. - -%%--Commands from the GUI--------------------------------------------- - -gui_cmd(ignore, State) -> - State; -gui_cmd({win, Win}, State) -> - State#state{win=Win}; -gui_cmd(stopped, _State) -> - exit(stop); -gui_cmd({coords, Coords}, State) -> - State#state{coords=Coords}; - -gui_cmd({shortcut, Key}, State) -> - case shortcut(Key) of - {always, Cmd} -> gui_cmd(Cmd, State); - {if_enabled, Cmd} -> - case dbg_ui_trace_win:is_enabled(Cmd) of - true -> gui_cmd(Cmd, State); - false -> State - end; - false -> State - end; - -%% File menu -gui_cmd('Close', State) -> - gui_cmd(stopped, State); - -%% Edit menu -gui_cmd('Go To Line...', State) -> - %% Will result in message handled below: {gui, {gotoline, Line}} - dbg_ui_trace_win:helpwin(gotoline, State#state.win, - State#state.gs, State#state.coords), - State; -gui_cmd({gotoline, Line}, State) -> - Win = dbg_ui_trace_win:select_line(State#state.win, Line), - State#state{win=Win}; -gui_cmd('Search...', State) -> - dbg_ui_trace_win:helpwin(search, State#state.win, - State#state.gs, State#state.coords), - State; - -%% Process menu -gui_cmd('Step', State) -> - int:meta(State#state.meta, step), - State; -gui_cmd('Next', State) -> - int:meta(State#state.meta, next), - State; -gui_cmd('Continue', State) -> - int:meta(State#state.meta, continue), - {Status, Mod, Line} = State#state.status, - if - Status==wait_break -> - Win = dbg_ui_trace_win:unmark_line(State#state.win), - gui_enable_functions(wait_running), - State#state{win=Win, status={wait_running,Mod,Line}}; - true -> - dbg_ui_trace_win:enable(['Stop'], true), - dbg_ui_trace_win:enable(['Continue'], false), - State - end; -gui_cmd('Finish', State) -> - int:meta(State#state.meta, finish), - State; -gui_cmd('Skip', State) -> - int:meta(State#state.meta, skip), - State; -gui_cmd('Time Out', State) -> - int:meta(State#state.meta, timeout), - State; -gui_cmd('Stop', State) -> - int:meta(State#state.meta, stop), - {Status, Mod, Line} = State#state.status, - if - Status==wait_running -> - Win = dbg_ui_trace_win:mark_line(State#state.win, Line, - break), - gui_enable_functions(wait_break), - gui_enable_updown(State#state.stack_trace, - State#state.stack), - gui_enable_btrace(State#state.trace, - State#state.stack_trace), - dbg_ui_trace_win:display({wait, Mod, Line}), - State#state{win=Win, status={wait_break,Mod,Line}}; - true -> - dbg_ui_trace_win:enable(['Stop'], false), - dbg_ui_trace_win:enable(['Continue'], true), - State - end; -gui_cmd('Where', State) -> - {_Cur, Max} = State#state.stack, - Stack = {Max, Max}, - {_Status, Mod, Line} = State#state.status, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, break), - gui_update_bindings(State#state.meta), - gui_enable_updown(State#state.stack_trace, Stack), - dbg_ui_trace_win:display(State#state.status), - State#state{win=Win, cm=Mod, stack=Stack}; - -gui_cmd('Kill', State) -> - exit(State#state.pid, kill), - State; -gui_cmd('Messages', State) -> - case int:meta(State#state.meta, messages) of - [] -> - dbg_ui_trace_win:eval_output("< No Messages!\n", bold); - Messages -> - dbg_ui_trace_win:eval_output("< --- Current Messages ---\n", - bold), - lists:foldl( - fun(Msg, N) -> - Str1 = io_lib:format(" ~w:", [N]), - dbg_ui_trace_win:eval_output(Str1, bold), - Str2 = io_lib:format(" ~ts~n",[io_lib:print(Msg)]), - dbg_ui_trace_win:eval_output(Str2, normal), - N+1 - end, - 1, - Messages) - end, - State; -gui_cmd('Back Trace', State) -> - dbg_ui_trace_win:trace_output("\nBACK TRACE\n----------\n"), - lists:foreach( - fun({Le, {Mod,Func,Args}}) -> - Str = io_lib:format("~p > ~p:~p~p~n", - [Le, Mod, Func, Args]), - dbg_ui_trace_win:trace_output(Str); - ({Le, {Fun,Args}}) -> - Str = io_lib:format("~p > ~p~p~n", [Le, Fun, Args]), - dbg_ui_trace_win:trace_output(Str); - (_) -> ignore - end, - int:meta(State#state.meta, backtrace, State#state.backtrace)), - dbg_ui_trace_win:trace_output("\n"), - State; -gui_cmd('Up', State) -> - {Cur, Max} = State#state.stack, - case int:meta(State#state.meta, stack_frame, {up, Cur}) of - {New, {undefined,-1}, _Bs} -> % call from non-interpreted code - Stack = {New, Max}, - Win = dbg_ui_trace_win:show_no_code(State#state.win), - dbg_ui_trace_win:update_bindings([]), - gui_enable_updown(State#state.stack_trace, Stack), - dbg_ui_trace_win:display({New,null,null}), - State#state{win=Win, cm=null, stack=Stack}; - - {New, {Mod,Line}, Bs} -> - Stack = {New, Max}, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, - where), - dbg_ui_trace_win:update_bindings(Bs), - gui_enable_updown(State#state.stack_trace, Stack), - dbg_ui_trace_win:display({New,Mod,Line}), - State#state{win=Win, cm=Mod, stack=Stack}; - top -> - dbg_ui_trace_win:enable(['Up'], false), - State - end; -gui_cmd('Down', State) -> - {Cur, Max} = State#state.stack, - case int:meta(State#state.meta, stack_frame, {down, Cur}) of - {New, {undefined,-1}, _Bs} -> % call from non-interpreted code - Stack = {New, Max}, - Win = dbg_ui_trace_win:show_no_code(State#state.win), - dbg_ui_trace_win:update_bindings([]), - gui_enable_updown(State#state.stack_trace, Stack), - dbg_ui_trace_win:display({New,null,null}), - State#state{win=Win, cm=null, stack=Stack}; - - {New, {Mod,Line}, Bs} -> - Stack = {New, Max}, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, - where), - dbg_ui_trace_win:update_bindings(Bs), - gui_enable_updown(State#state.stack_trace, Stack), - dbg_ui_trace_win:display({New,Mod,Line}), - State#state{win=Win, cm=Mod, stack=Stack}; - - bottom -> - gui_cmd('Where', State) - end; - -%% Break menu -gui_cmd('Line Break...', State) -> - add_break(State#state.gs, State#state.coords, line, - State#state.cm, - dbg_ui_trace_win:selected_line(State#state.win)), - State; -gui_cmd('Conditional Break...', State) -> - add_break(State#state.gs, State#state.coords, conditional, - State#state.cm, - dbg_ui_trace_win:selected_line(State#state.win)), - State; -gui_cmd('Function Break...', State) -> - add_break(State#state.gs, State#state.coords, function, - State#state.cm, undefined), - State; -gui_cmd('Enable All', State) -> - Breaks = int:all_breaks(), - ThisMod = State#state.cm, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> - int:enable_break(Mod, Line); - (_Break) -> - ignore - end, - Breaks), - State; -gui_cmd('Disable All', State) -> - Breaks = int:all_breaks(), - ThisMod = State#state.cm, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> - int:disable_break(Mod, Line); - (_Break) -> - ignore - end, - Breaks), - State; -gui_cmd('Delete All', State) -> - int:no_break(State#state.cm), - State; -gui_cmd({break, {Mod, Line}, What}, State) -> - case What of - add -> int:break(Mod, Line); - delete -> int:delete_break(Mod, Line); - {status, inactive} -> int:disable_break(Mod, Line); - {status, active} -> int:enable_break(Mod, Line); - {trigger, Action} -> int:action_at_break(Mod, Line, Action) - end, - State; - -%% Options menu -gui_cmd({'Trace Window', TraceWin}, State) -> - Trace = lists:member('Trace Area', TraceWin), - int:meta(State#state.meta, trace, Trace), - Win = dbg_ui_trace_win:configure(State#state.win, TraceWin), - {Status,_,_} = State#state.status, - if - Status==break; Status==wait_break -> - gui_enable_btrace(Trace, State#state.stack_trace); - true -> ignore - end, - State#state{win=Win, trace=Trace}; -gui_cmd({'Stack Trace', [Name]}, State) -> - int:meta(State#state.meta, stack_trace, map(Name)), - {Status,_,_} = State#state.status, - if - Status==break; Status==wait_break -> - gui_enable_btrace(State#state.trace, map(Name)); - true -> ignore - end, - State; -gui_cmd('Back Trace Size...', State) -> - dbg_ui_edit:start(State#state.gs, State#state.coords, "Backtrace", - 'Backtrace:', {integer, State#state.backtrace}), - State; - -%% Help menu -gui_cmd('Debugger', State) -> - Window = dbg_ui_trace_win:get_window(State#state.win), - HelpFile = filename:join([code:lib_dir(debugger), - "doc", "html", "part_frame.html"]), - tool_utils:open_help(Window, HelpFile), - State; - -gui_cmd({user_command, Cmd}, State) -> - {Status, _Mod, _Line} = State#state.status, - if - Status==break; - Status==wait_break; - Status==wait_running -> - Cm = State#state.cm, - Arg = case State#state.stack of - {Cur, Max} when Cur<Max -> {Cm, Cmd, Cur}; - _Stack -> {Cm, Cmd} - end, - - %% Reply will be received as {Meta, {eval_rsp, Res}} - int:meta(State#state.meta, eval, Arg); - true -> - Str = "Commands not allowed", - dbg_ui_trace_win:eval_output([$<,Str,10], normal) - end, - State; - -gui_cmd({edit, {Var, Val}}, State) -> - dbg_ui_edit:start(State#state.gs, State#state.coords, - "Edit variable", Var, {term, Val}), - State. - -add_break(GS, Coords, Type, undefined, _Line) -> - dbg_ui_break:start(GS, Coords, Type); -add_break(GS, Coords, Type, Mod, undefined) -> - dbg_ui_break:start(GS, Coords, Type, Mod); -add_break(GS, Coords, Type, Mod, Line) -> - dbg_ui_break:start(GS, Coords, Type, Mod, Line). - -%%--Commands from the interpreter------------------------------------- - -int_cmd({interpret, Mod}, State) -> - if - Mod==State#state.cm -> - State#state{cm_obsolete=true}; - true -> - State - end; -int_cmd({no_interpret, Mod}, State) -> - if - Mod==State#state.cm -> - State#state{cm_obsolete=true}; - true -> - Win = dbg_ui_trace_win:remove_code(State#state.win, Mod), - State#state{win=Win} - end; - -int_cmd({new_break, Break}, State) -> - Win = dbg_ui_trace_win:add_break(State#state.win, 'Break', Break), - State#state{win=Win}; -int_cmd({delete_break, Point}, State) -> - Win = dbg_ui_trace_win:delete_break(State#state.win, Point), - State#state{win=Win}; -int_cmd({break_options, Break}, State) -> - Win = dbg_ui_trace_win:update_break(State#state.win, Break), - State#state{win=Win}; -int_cmd(no_break, State) -> - Win = dbg_ui_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; -int_cmd({no_break, Mod}, State) -> - Win = dbg_ui_trace_win:clear_breaks(State#state.win, Mod), - State#state{win=Win}. - -%%--Commands from the meta process------------------------------------ - -%% Message received when first attached to a living process -%% '_Trace' is a boolean indicating if the process is traced or not -- -%% ignore this as we already have ordered tracing or not depending on if -%% the Trace Area is shown or not. -meta_cmd({attached, Mod, Line, _Trace}, State) -> - Win = if - Mod/=undefined -> - gui_enable_functions(init), - gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, - break); - true -> State#state.win - end, - State#state{win=Win, status={init,Mod,Line}, cm=Mod}; - -%% Message received when returning to interpreted code -meta_cmd({re_entry, dbg_ieval, eval_fun}, State) -> - State; -meta_cmd({re_entry, Mod, _Func}, State) -> - Obs = State#state.cm_obsolete, - case State#state.cm of - Mod when Obs==true -> - Win = gui_load_module(State#state.win, Mod,State#state.pid), - State#state{win=Win, cm_obsolete=false}; - Mod -> State; - Cm -> - Win = gui_show_module(State#state.win, Mod, 0, - Cm, State#state.pid, break), - State#state{win=Win, cm=Mod} - end; - -%% Message received when attached to a terminated process -meta_cmd({exit_at, null, Reason, Cur}, State) -> - Stack = {Cur, Cur}, - gui_enable_functions(exit), - gui_enable_updown(false, Stack), - dbg_ui_trace_win:display({exit, null, Reason}), - State#state{status={exit,null,Reason}, stack=Stack}; -meta_cmd({exit_at, {Mod,Line}, Reason, Cur}, State) -> - Stack = {Cur+1, Cur+1}, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, break), - gui_enable_functions(exit), - gui_enable_updown(State#state.stack_trace, Stack), - gui_enable_btrace(State#state.trace, State#state.stack_trace), - gui_update_bindings(State#state.meta), - dbg_ui_trace_win:display({exit, {Mod,Line}, Reason}), - State#state{win=Win, cm=Mod,status={exit,{Mod,Line},Reason}, - stack=Stack}; - -meta_cmd({break_at, Mod, Line, Cur}, State) -> - Stack = {Cur,Cur}, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, break), - gui_enable_functions(break), - gui_enable_updown(State#state.stack_trace, Stack), - gui_enable_btrace(State#state.trace, State#state.stack_trace), - gui_update_bindings(State#state.meta), - dbg_ui_trace_win:display({break, Mod, Line}), - State#state{win=Win, cm=Mod, status={break,Mod,Line}, stack=Stack}; -meta_cmd({func_at, Mod, Line, Cur}, State) -> - Stack = {Cur,Cur}, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, where), - gui_enable_functions(idle), - dbg_ui_trace_win:display(idle), - State#state{win=Win, cm=Mod, status={idle,Mod,Line}, stack=Stack}; -meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}}=State) - when Status/=init, Status/=break -> - Stack = {Cur,Cur}, - gui_enable_functions(wait_running), - dbg_ui_trace_win:display({wait,Mod,Line}), - State#state{status={wait_running,Mod,Line}, stack=Stack}; -meta_cmd({wait_at, Mod, Line, Cur}, State) -> - Stack = {Cur,Cur}, - Win = gui_show_module(State#state.win, Mod, Line, - State#state.cm, State#state.pid, break), - gui_enable_functions(wait_break), - gui_enable_updown(State#state.stack_trace, Stack), - gui_enable_btrace(State#state.trace, State#state.stack_trace), - gui_update_bindings(State#state.meta), - dbg_ui_trace_win:display({wait, Mod, Line}), - State#state{win=Win, cm=Mod, status={wait_break,Mod,Line}, - stack=Stack}; -meta_cmd({wait_after_at, Mod, Line, Sp}, State) -> - meta_cmd({wait_at, Mod, Line, Sp}, State); -meta_cmd(running, State) -> - Win = dbg_ui_trace_win:unmark_line(State#state.win), - gui_enable_functions(running), - dbg_ui_trace_win:update_bindings([]), - dbg_ui_trace_win:display({running, State#state.cm}), - State#state{win=Win, status={running,null,null}}; - -meta_cmd(idle, State) -> - Win = dbg_ui_trace_win:show_no_code(State#state.win), - gui_enable_functions(idle), - dbg_ui_trace_win:update_bindings([]), - dbg_ui_trace_win:display(idle), - State#state{win=Win, status={idle,null,null}, cm=undefined}; - -%% Message about changed trace option can be ignored, the change must -%% have been ordered by this process. (In theory, the change could have -%% been ordered by another attached process. The Debugger, though, -%% allows max one attached process per debugged process). -meta_cmd({trace, _Bool}, State) -> - State; - -meta_cmd({stack_trace, Flag}, State) -> - dbg_ui_trace_win:select(map(Flag), true), - gui_enable_updown(Flag, State#state.stack), - {Status,_,_} = State#state.status, - if - Status==break; Status==wait_break -> - gui_enable_btrace(State#state.trace, Flag); - true -> ignore - end, - State#state{stack_trace=Flag}; - -meta_cmd({trace_output, Str}, State) -> - dbg_ui_trace_win:trace_output(Str), - State; - -%% Reply on a user command -meta_cmd({eval_rsp, Res}, State) -> - Str = io_lib:print(Res), - dbg_ui_trace_win:eval_output([$<,Str,10], normal), - State. - - -%%==================================================================== -%% GUI auxiliary functions -%%==================================================================== - -menus() -> - [{'File', [{'Close', no}]}, - {'Edit', [{'Go To Line...', no}, - {'Search...', no}]}, - {'Process', [{'Step', 0}, - {'Next', 0}, - {'Continue', 0}, - {'Finish', 0}, - {'Skip', no}, - {'Time Out', no}, - {'Stop', no}, - separator, - {'Kill', no}, - separator, - {'Messages', 0}, - {'Back Trace', no}, - separator, - {'Where', 0}, - {'Up', no}, - {'Down', no}]}, - {'Break', [{'Line Break...', 5}, - {'Conditional Break...', no}, - {'Function Break...', no}, - separator, - {'Enable All', no}, - {'Disable All', no}, - {'Delete All', 0}, - separator]}, - {'Options', [{'Trace Window', no, cascade, - [{'Button Area', no, check}, - {'Evaluator Area', no, check}, - {'Bindings Area', no, check}, - {'Trace Area', no, check}]}, - {'Stack Trace', no, cascade, - [{'Stack On, Tail', no, radio}, - {'Stack On, No Tail', no, radio}, - {'Stack Off', no, radio}]}, - {'Back Trace Size...', no}]}, - {'Help', [{'Debugger', no}]}]. - -%% enable(Status) -> [MenuItem] -%% Status = init % when first message from Meta has arrived -%% | idle | break | exit | wait_break | wait_running | running -enable(init) -> []; -enable(idle) -> ['Stop','Kill']; -enable(break) -> ['Step','Next','Continue','Finish','Skip', - 'Kill','Messages']; -enable(exit) -> []; -enable(wait_break) -> ['Continue','Time Out','Kill']; -enable(wait_running) -> ['Stop','Kill']; -enable(running) -> ['Stop','Kill']. - -all_buttons() -> - ['Step','Next','Continue','Finish','Skip','Time Out','Stop', - 'Kill','Messages','Back Trace','Where','Up','Down']. - -shortcut(s) -> {if_enabled, 'Step'}; -shortcut(n) -> {if_enabled, 'Next'}; -shortcut(c) -> {if_enabled, 'Continue'}; -shortcut(f) -> {if_enabled, 'Finish'}; -shortcut(m) -> {if_enabled, 'Messages'}; -shortcut(w) -> {if_enabled, 'Where'}; - -shortcut(b) -> {always, 'Line Break...'}; -shortcut(d) -> {always, 'Delete All'}; - -shortcut(_) -> false. - -map('Stack On, Tail') -> all; % Stack trace -map('Stack On, No Tail') -> no_tail; -map('Stack Off') -> false; -map(all) -> 'Stack On, Tail'; -map(true) -> 'Stack On, Tail'; -map(no_tail) -> 'Stack On, No Tail'; -map(false) -> 'Stack Off'. - - -%% gui_show_module(Win, Mod, Line, Cm, Pid, How) -> Win -%% gui_show_module(Win, {Mod,Line}, _Reason, Cm, Pid, How) -> Win -%% How = where | break -%% Show contents of a module in code area -gui_show_module(Win, {Mod,Line}, _Reason, Cm, Pid, How) -> - gui_show_module(Win, Mod, Line, Cm, Pid, How); -gui_show_module(Win, Mod, Line, Mod, _Pid, How) -> - dbg_ui_trace_win:mark_line(Win, Line, How); -gui_show_module(Win, Mod, Line, _Cm, Pid, How) -> - Win2 = case dbg_ui_trace_win:is_shown(Win, Mod) of - {true, Win3} -> Win3; - false -> gui_load_module(Win, Mod, Pid) - end, - dbg_ui_trace_win:mark_line(Win2, Line, How). - -gui_load_module(Win, Mod, Pid) -> - dbg_ui_trace_win:display({text, "Loading module..."}), - Contents = int:contents(Mod, Pid), - Win2 = dbg_ui_trace_win:show_code(Win, Mod, Contents), - dbg_ui_trace_win:display({text, ""}), - Win2. - -gui_update_bindings(Meta) -> - Bs = int:meta(Meta, bindings, nostack), - dbg_ui_trace_win:update_bindings(Bs). - -gui_enable_functions(Status) -> - Enable = enable(Status), - Disable = all_buttons() -- Enable, - dbg_ui_trace_win:enable(Disable, false), - dbg_ui_trace_win:enable(Enable, true). - -gui_enable_updown(Flag, Stack) -> - {Enable, Disable} = - if - Flag==false -> {[], ['Up', 'Down']}; - true -> - case Stack of - {1,1} -> {[], ['Up', 'Down']}; - {2,2} -> {[], ['Up', 'Down']}; - {Max,Max} -> {['Up'], ['Down']}; - {2,_Max} -> {['Down'], ['Up']}; - {_Cur,_Max} -> {['Up', 'Down'], []} - end - end, - dbg_ui_trace_win:enable(Enable, true), - dbg_ui_trace_win:enable(Disable, false), - if - Enable==[] -> dbg_ui_trace_win:enable(['Where'], false); - true -> dbg_ui_trace_win:enable(['Where'], true) - end. - -gui_enable_btrace(Trace, StackTrace) -> - Bool = if - Trace==false -> false; - StackTrace==false -> false; - true -> true - end, - dbg_ui_trace_win:enable(['Back Trace'], Bool). diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl deleted file mode 100644 index beb3fbd71e..0000000000 --- a/lib/debugger/src/dbg_ui_trace_win.erl +++ /dev/null @@ -1,1595 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2012. 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(dbg_ui_trace_win). --compile([{nowarn_deprecated_function,{gs,button,2}}, - {nowarn_deprecated_function,{gs,button,3}}, - {nowarn_deprecated_function,{gs,checkbutton,2}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,create,4}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,editor,2}}, - {nowarn_deprecated_function,{gs,editor,3}}, - {nowarn_deprecated_function,{gs,entry,2}}, - {nowarn_deprecated_function,{gs,entry,3}}, - {nowarn_deprecated_function,{gs,frame,3}}, - {nowarn_deprecated_function,{gs,grid,3}}, - {nowarn_deprecated_function,{gs,gridline,2}}, - {nowarn_deprecated_function,{gs,label,2}}, - {nowarn_deprecated_function,{gs,label,3}}, - {nowarn_deprecated_function,{gs,menubar,2}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,window,2}}, - {nowarn_deprecated_function,{gs,window,3}}]). - -%% External exports --export([init/0]). --export([create_win/4, get_window/1, - configure/2, - enable/2, is_enabled/1, select/2, - add_break/3, update_break/2, delete_break/2, - clear_breaks/1, clear_breaks/2, - display/1, % Help messages - is_shown/2, % Code area - show_code/3, show_no_code/1, remove_code/2, - mark_line/3, unmark_line/1, - select_line/2, selected_line/1, - eval_output/2, % Evaluator area - update_bindings/1, % Bindings area - trace_output/1, % Trace area - handle_event/2 - ]). --export([helpwin/4, % Help windows - helpwin/5]). - --record(breakInfo, {point, status, break}). --record(winInfo, {window, % gsobj() - size, % {W, H} - flags, % {F,F,F,F} F = open|close - - marked_line=0, % integer() Current line - selected_line=0, % integer() Selected line - - breaks=[], % [#breakInfo{}] Known breakpoints - - editor, % {Mod, Editor} Visible code editor - editors=[] % [{Mod,Editor}] Code editors - }). - - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% init() -> GS -%% GS = term() -%%-------------------------------------------------------------------- -init() -> - dbg_ui_win:init(). - -%%-------------------------------------------------------------------- -%% create_win(GS, Title, TraceWin, Menus) -> #winInfo{} -%% GS = gsobj() -%% Title = string() -%% TraceWin = [WinArea] -%% WinArea = 'Button|Evaluator|Bindings|Trace Area' -%% Menus = [menu()] See dbg_ui_win.erl -%%-------------------------------------------------------------------- -create_win(GS, Title, TraceWin, Menus) -> - Bu = flip(lists:member('Button Area', TraceWin)), - Ev = flip(lists:member('Evaluator Area', TraceWin)), - Bi = flip(lists:member('Bindings Area', TraceWin)), - Tr = flip(lists:member('Trace Area', TraceWin)), - - Win = gs:window(trace_window, GS, [{title, Title}, - {width, 550}, - {configure,true}, {destroy,true}, - {keypress,true}, {motion,true}]), - - MenuBar = gs:menubar(Win, []), - dbg_ui_win:create_menus(MenuBar, Menus), - dbg_ui_winman:windows_menu(MenuBar), - - FrameOpts = [{anchor,nw}, {relief,raised}, {bw,2}, {keypress,true}], - Editor = code_area(2, 25, FrameOpts, Win), - button_area(Bu, 2, 235, FrameOpts, Win), - eval_area({Ev,Bi}, 2, 265, FrameOpts, Win), - bind_area({Ev,Bi}, 300, 265, FrameOpts, Win), - trace_area(Tr, 2, 475, FrameOpts, Win), - - Flags = {Bu, Ev, Bi, Tr}, - resizebar(rb1(Flags), 'RB1', 2, 225, 710, 10, Win), - resizebar(rb2(Flags), 'RB2', 2, 465, 710, 10, Win), - resizebar(rb3(Flags), 'RB3', 290, 265, 10, 200, Win), - config_v(), - config_h(), - - gs:config(Win,{height, - 25 + - gs:read('CodeArea', height) + - gs:read('RB1', height) + - gs:read('ButtonArea', height) + - erlang:max(gs:read('EvalArea', height), - gs:read('BindArea', height)) + - gs:read('RB2', height) + - gs:read('TraceArea', height)}), - - gs:config(Win, {map, true}), - #winInfo{window=Win, size={gs:read(Win,width), gs:read(Win,height)}, - flags=Flags, - editor={'$top', Editor}, editors=[{'$top', Editor}]}. - -flip(true) -> open; -flip(false) -> close. - -%%-------------------------------------------------------------------- -%% get_window(WinInfo) -> Window -%% WinInfo = #winInfo{} -%% Window = gsobj() -%%-------------------------------------------------------------------- -get_window(WinInfo) -> - WinInfo#winInfo.window. - -%%-------------------------------------------------------------------- -%% configure(WinInfo, TraceWin) -> WinInfo -%% WinInfo = #winInfo{} -%% TraceWin = [WinArea] -%% WinArea = 'Button|Evaluator|Bindings|Trace Area' -%% Window areas should be opened or closed. -%%-------------------------------------------------------------------- -configure(WinInfo, TraceWin) -> - {Bu1, Ev1, Bi1, Tr1} = OldFlags = WinInfo#winInfo.flags, - Bu2 = flip(lists:member('Button Area', TraceWin)), - Ev2 = flip(lists:member('Evaluator Area', TraceWin)), - Bi2 = flip(lists:member('Bindings Area', TraceWin)), - Tr2 = flip(lists:member('Trace Area', TraceWin)), - NewFlags = {Bu2, Ev2, Bi2, Tr2}, - - Win = WinInfo#winInfo.window, - W = gs:read(Win, width), - H = gs:read(Win, height), - - H2 = if - Bu1 =:= close, Bu2 =:= open -> - resize_button_area(open, width, W-4), - gs:config('ButtonArea', {height, 30}), - H+30; - Bu1 =:= open, Bu2 =:= close -> - gs:config('ButtonArea', [{width, 0}, {height, 0}]), - H-30; - true -> H - end, - H3 = if - Ev1 =:= close, Ev2 =:= open, Bi1 =:= open -> - Wnew1 = round((W-10-4)/2), % W = window/2 - rb - pads - Hbi1 = gs:read('BindArea', height), % H = bind area h - resize_eval_area(open, width, Wnew1), - resize_eval_area(open, height, Hbi1), - gs:config('RB3', {width, 10}), - gs:config('RB3', {height, Hbi1}), - resize_bind_area(open, width, - Wnew1-gs:read('BindArea', width)), - H2; - Ev1 =:= close, Ev2 =:= open, Bi1 =:= close -> - resize_eval_area(open, width, W-4), - resize_eval_area(open, height, 200), - H2+200; - Ev1 =:= open, Ev2 =:= close, Bi1 =:= open -> - gs:config('EvalArea', [{width,0}, {height,0}]), - gs:config('RB3', [{width, 0}, {height, 0}]), - Wnew2 = W-4, - resize_bind_area(open, width, - Wnew2-gs:read('BindArea', width)), - H2; - Ev1 =:= open, Ev2 =:= close, Bi1 =:= close -> - Hs1 = gs:read('EvalArea', height), - gs:config('EvalArea', [{width, 0}, {height, 0}]), - H2-Hs1; - true -> H2 - end, - H4 = if - Bi1 =:= close, Bi2 =:= open, Ev2 =:= open -> - Wnew3 = round((W-10-4)/2), % W = window/2 - rb - pads - Hs2 = gs:read('EvalArea', height), % H = eval area h - resize_bind_area(open, width, Wnew3), - resize_bind_area(open, height, Hs2), - gs:config('RB3', [{width,10},{height,Hs2}]), - resize_eval_area(open, width, - Wnew3-gs:read('EvalArea', width)), - H3; - Bi1 =:= close, Bi2 =:= open, Ev2 =:= close -> - resize_bind_area(open, width, W-4), - resize_bind_area(open, height, 200), - H3+200; - Bi1 =:= open, Bi2 =:= close, Ev2 =:= open -> - gs:config('BindArea', [{width, 0}, {height, 0}]), - gs:config('RB3', [{width, 0}, {height, 0}]), - Wnew4 = W-4, - resize_eval_area(open, width, - Wnew4-gs:read('EvalArea', width)), - H3; - Bi1 =:= open, Bi2 =:= close, Ev2 =:= close -> - Hbi2 = gs:read('BindArea', height), - gs:config('BindArea', [{width, 0}, {height, 0}]), - H3-Hbi2; - true -> H3 - end, - H5 = if - Tr1 =:= close, Tr2 =:= open -> - resize_trace_area(open, width, W-4), - resize_trace_area(open, height, 200), - H4+200; - Tr1 =:= open, Tr2 =:= close -> - Hf = gs:read('TraceArea', height), - gs:config('TraceArea', [{width, 0}, {height, 0}]), - H4-Hf; - true -> H4 - end, - gs:config(Win, {height, H5}), - - RB1old = rb1(OldFlags), RB1new = rb1(NewFlags), - if - RB1old =:= close, RB1new =:= open -> - gs:config('RB1', [{width, W-4}, {height, 10}]), - gs:config(Win, {height, gs:read(Win, height)+10}); - RB1old =:= open, RB1new =:= close -> - gs:config('RB1', [{width, 0}, {height, 0}, lower]), - gs:config(Win, {height, gs:read(Win, height)-10}); - true -> ignore - end, - - RB2old = rb2(OldFlags), RB2new = rb2(NewFlags), - if - RB2old =:= close, RB2new =:= open -> - gs:config('RB2', [{width, W-4}, {height, 10}]), - gs:config(Win, {height,gs:read(Win, height)+10}); - RB2old =:= open, RB2new =:= close -> - gs:config('RB2', [{width, 0}, {height, 0}, lower]), - gs:config(Win, {height, gs:read(Win, height)-10}); - true -> ignore - end, - config_v(), - config_h(), - - flush_configure(), - - WinInfo#winInfo{size={gs:read(Win, width), gs:read(Win, height)}, - flags=NewFlags}. - -flush_configure() -> - receive - {gs, _Id, configure, _Data, _Arg} -> - flush_configure() - after 100 -> - true - end. - -%%-------------------------------------------------------------------- -%% enable([MenuItem], Bool) -%% is_enabled(MenuItem) -> Bool -%% MenuItem = atom() -%% Bool = boolean() -%%-------------------------------------------------------------------- -enable(MenuItems, Bool) -> - lists:foreach(fun(MenuItem) -> - gs:config(MenuItem, {enable, Bool}), - case is_button(MenuItem) of - {true, Button} -> - gs:config(Button, {enable, Bool}); - false -> ignore - end - end, - MenuItems). - -is_enabled(MenuItem) -> - gs:read(MenuItem, enable). - -%%-------------------------------------------------------------------- -%% select(MenuItem, Bool) -%% MenuItem = atom() -%% Bool = boolean() -%%-------------------------------------------------------------------- -select(MenuItem, Bool) -> - dbg_ui_win:select(MenuItem, Bool). - -%%-------------------------------------------------------------------- -%% add_break(WinInfo, Name, {Point, Options}) -> WinInfo -%% WinInfo = #winInfo{} -%% Name = atom() Menu name -%% Point = {Mod, Line} -%% Options = [Status, Action, Mods, Cond] -%% Status = active | inactive -%% Action = enable | disable | delete -%% Mods = null (not used) -%% Cond = null | {Mod, Func} -%%-------------------------------------------------------------------- -add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) -> - case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of - {Mod, Editor} -> - add_break_to_code(Editor, Line, Status); - false -> ignore - end, - add_break_to_menu(WinInfo, Menu, Break). - -add_break_to_code(Editor, Line, Status) -> - Color = if Status =:= active -> red; Status =:= inactive -> blue end, - config_editor(Editor, [{overwrite,{{Line,0},"-@- "}}, - {fg,{{{Line,0},{Line,lineend}}, Color}}]). - -add_break_to_menu(WinInfo, Menu, {Point, [Status|_Options]=Options}) -> - Break = dbg_ui_win:add_break(Menu, Point), - dbg_ui_win:update_break(Break, Options), - BreakInfo = #breakInfo{point=Point, status=Status, break=Break}, - WinInfo#winInfo{breaks=[BreakInfo|WinInfo#winInfo.breaks]}. - -%%-------------------------------------------------------------------- -%% update_break(WinInfo, {Point, Options}) -> WinInfo -%% WinInfo = #winInfo{} -%% Point = {Mod, Line} -%% Options = [Status, Action, Mods, Cond] -%% Status = active | inactive -%% Action = enable | disable | delete -%% Mods = null (not used) -%% Cond = null | {Mod, Func} -%%-------------------------------------------------------------------- -update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) -> - case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of - {Mod, Editor} -> - add_break_to_code(Editor, Line, Status); - false -> ignore - end, - update_break_in_menu(WinInfo, Break). - -update_break_in_menu(WinInfo, {Point, [Status|_Options]=Options}) -> - {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, - WinInfo#winInfo.breaks), - dbg_ui_win:update_break(BreakInfo#breakInfo.break, Options), - BreakInfo2 = BreakInfo#breakInfo{status=Status}, - WinInfo#winInfo{breaks=lists:keyreplace(Point, #breakInfo.point, - WinInfo#winInfo.breaks, - BreakInfo2)}. - -%%-------------------------------------------------------------------- -%% delete_break(WinInfo, Point) -> WinInfo -%% WinInfo = #winInfo{} -%% Point = {Mod, Line} -%%-------------------------------------------------------------------- -delete_break(WinInfo, {Mod,Line}=Point) -> - case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of - {Mod, Editor} -> delete_break_from_code(Editor, Line); - false -> ignore - end, - delete_break_from_menu(WinInfo, Point). - -delete_break_from_code(Editor, Line) -> - Prefix = string:substr(integer_to_list(Line)++": ", 1, 5), - config_editor(Editor, [{overwrite,{{Line,0},Prefix}}, - {fg,{{{Line,0},{Line,lineend}}, black}}]). - -delete_break_from_menu(WinInfo, Point) -> - {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, - WinInfo#winInfo.breaks), - dbg_ui_win:delete_break(BreakInfo#breakInfo.break), - WinInfo#winInfo{breaks=lists:keydelete(Point, #breakInfo.point, - WinInfo#winInfo.breaks)}. - -%%-------------------------------------------------------------------- -%% clear_breaks(WinInfo) -> WinInfo -%% clear_breaks(WinInfo, Mod) -> WinInfo -%% WinInfo = #winInfo{} -%%-------------------------------------------------------------------- -clear_breaks(WinInfo) -> - clear_breaks(WinInfo, all). -clear_breaks(WinInfo, Mod) -> - Remove = if - Mod =:= all -> WinInfo#winInfo.breaks; - true -> - lists:filter(fun(#breakInfo{point={Mod2,_L}}) -> - if - Mod2 =:= Mod -> true; - true -> false - end - end, - WinInfo#winInfo.breaks) - end, - lists:foreach(fun(#breakInfo{point=Point}) -> - delete_break(WinInfo, Point) - end, - Remove), - Remain = WinInfo#winInfo.breaks -- Remove, - WinInfo#winInfo{breaks=Remain}. - -%%-------------------------------------------------------------------- -%% display(Arg) -%% Arg = idle | {Status,Mod,Line} | {running,Mod} -%% | {exit,Where,Reason} | {text,Text} -%% Status = break | wait | Level -%% Level = int() -%% Mod = atom() -%% Line = integer() -%% Where = {Mod,Line} | null -%% Reason = term() -%% Text = string() -%%-------------------------------------------------------------------- -display(Arg) -> - Str = case Arg of - idle -> "State: uninterpreted"; - {exit, {Mod,Line}, Reason} -> - gs:config(trace_window, raise), - io_lib:format("State: EXITED [~w.erl/~w], Reason:~w", - [Mod, Line, Reason]); - {exit, null, Reason} -> - gs:config(trace_window, raise), - io_lib:format("State: EXITED [uninterpreted], " - "Reason:~w", [Reason]); - {Level, null, _Line} when is_integer(Level) -> - io_lib:format("*** Call level #~w " - "(in non-interpreted code)", - [Level]); - {Level, Mod, Line} when is_integer(Level) -> - io_lib:format("*** Call level #~w [~w.erl/~w]", - [Level, Mod, Line]); - {Status, Mod, Line} -> - What = case Status of - wait -> 'receive'; - _ -> Status - end, - io_lib:format("State: ~w [~w.erl/~w]", - [What, Mod, Line]); - {running, Mod} -> - io_lib:format("State: running [~w.erl]", [Mod]); - {text, Text} -> Text - end, - gs:config(info_window, {label,{text,lists:flatten(Str)}}). - -%%-------------------------------------------------------------------- -%% is_shown(WinInfo, Mod) -> {true, WinInfo} | false -%% show_code(WinInfo, Mod, Contents) -> WinInfo -%% show_no_code(WinInfo) -> WinInfo -%% remove_code(WinInfo, Mod) -> WinInfo -%% WinInfo = #winInfo{} -%% Mod = atom() -%% Contents = string() -%% Note: remove_code/2 should not be used for currently shown module. -%%-------------------------------------------------------------------- -is_shown(WinInfo, Mod) -> - case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of - {Mod, Editor} -> - gs:config(Editor, raise), - {true, WinInfo#winInfo{editor={Mod, Editor}}}; - false -> false - end. - -show_code(WinInfo, Mod, Contents) -> - Editors = WinInfo#winInfo.editors, - {Flag, Editor} = case lists:keyfind(Mod, 1, Editors) of - {Mod, Ed} -> {existing, Ed}; - false -> {new, code_editor()} - end, - %% Insert code and update breakpoints, if any - config_editor(Editor, [raise, clear]), - show_code(Editor, Contents), - lists:foreach(fun(BreakInfo) -> - case BreakInfo#breakInfo.point of - {Mod2, Line} when Mod2 =:= Mod -> - Status = BreakInfo#breakInfo.status, - add_break_to_code(Editor, Line,Status); - _Point -> ignore - end - end, - WinInfo#winInfo.breaks), - case Flag of - existing -> - WinInfo#winInfo{editor={Mod, Editor}}; - new -> - WinInfo#winInfo{editor={Mod, Editor}, - editors=[{Mod, Editor} | Editors]} - end. - -show_code(Editor, Text) when length(Text) > 1500 -> - %% Add some text at a time so that other processes may get scheduled - Str = string:sub_string(Text, 1, 1500), - config_editor(Editor, {insert,{'end', Str}}), - show_code(Editor, string:sub_string(Text, 1501)); -show_code(Editor, Text) -> - config_editor(Editor, {insert,{'end',Text}}). - -show_no_code(WinInfo) -> - {'$top', Editor} = lists:keyfind('$top', 1, WinInfo#winInfo.editors), - gs:config(Editor, raise), - WinInfo#winInfo{editor={'$top', Editor}}. - -remove_code(WinInfo, Mod) -> - Editors = WinInfo#winInfo.editors, - case lists:keyfind(Mod, 1, Editors) of - {Mod, Editor} -> - gs:destroy(Editor), - WinInfo#winInfo{editors=lists:keydelete(Mod, 1, Editors)}; - false -> - WinInfo - end. - -%%-------------------------------------------------------------------- -%% mark_line(WinInfo, Line, How) -> WinInfo -%% WinInfo = #winInfo{} -%% Line = integer() -%% How = break | where -%% Mark the code line where the process is executing. -%%-------------------------------------------------------------------- -mark_line(WinInfo, Line, How) -> - {_Mod, Editor} = WinInfo#winInfo.editor, - mark_line2(Editor, WinInfo#winInfo.marked_line, false), - mark_line2(Editor, Line, How), - if - Line =/= 0 -> config_editor(Editor, {vscrollpos, Line-5}); - true -> ignore - end, - WinInfo#winInfo{marked_line=Line}. - -unmark_line(WinInfo) -> - mark_line(WinInfo, 0, false). - -mark_line2(Editor, Line, How) -> - Prefix = case How of - break -> "-->"; - where -> ">>>"; - false -> " " - end, - Font = if - How =:= false -> dbg_ui_win:font(normal); - true -> dbg_ui_win:font(bold) - end, - config_editor(Editor, [{overwrite, {{Line,5}, Prefix}}, - {font_style, - {{{Line,0},{Line,lineend}}, Font}}]). - -%%-------------------------------------------------------------------- -%% select_line(WinInfo, Line) -> WinInfo -%% selected_line(WinInfo) -> undefined | Line -%% WinInfo = #winInfo{} -%% Line = integer() -%% Select/unselect a line (unselect if Line=0). -%%-------------------------------------------------------------------- -select_line(WinInfo, Line) -> - {_Mod, Editor} = WinInfo#winInfo.editor, - - %% Since 'Line' may be specified by the user in the 'Go To Line' - %% help window, it must be checked that it is correct - Size = gs:read(Editor, size), - if - Line =:= 0 -> - select_line(Editor, WinInfo#winInfo.selected_line, false), - WinInfo#winInfo{selected_line=0}; - Line < Size -> - select_line(Editor, Line, true), - config_editor(Editor, {vscrollpos, Line-5}), - WinInfo#winInfo{selected_line=Line}; - true -> - WinInfo - end. - -select_line(Editor, Line, true) -> - config_editor(Editor, {selection, {{Line,0}, {Line,lineend}}}); -select_line(Editor, _Line, false) -> - config_editor(Editor, {selection, {{1,0}, {1,0}}}). - -selected_line(WinInfo) -> - case WinInfo#winInfo.selected_line of - 0 -> undefined; - Line -> Line - end. - -%%-------------------------------------------------------------------- -%% eval_output(Str, Face) -%% Str = string() -%% Face = normal | bold -%%-------------------------------------------------------------------- -eval_output(Text, Face) -> - Y1 = gs:read('EvalEditor', size), - config_editor('EvalEditor', {insert,{'end',Text}}), - Y2 = gs:read('EvalEditor', size), - - Font = dbg_ui_win:font(Face), - config_editor('EvalEditor', - [{font_style, {{{Y1,0},{Y2,lineend}}, Font}}, - {vscrollpos,Y2}]). - -%%-------------------------------------------------------------------- -%% update_bindings(Bs) -%% Bs = [{Var,Val}] -%%-------------------------------------------------------------------- -update_bindings(Bs) -> - gs:config('BindGrid', {rows, {1,length(Bs)+1}}), - Font = dbg_ui_win:font(normal), - Last = - lists:foldl(fun({Var, Val}, Row) -> - Opts = [{text, {1,atom_to_list(Var)}}, - {text, {2,io_lib:format("~P", - [Val, 4])}}, - {doubleclick, true}, - {data, {binding,{Var,Val}}}], - case gs:read('BindGrid',{obj_at_row,Row}) of - undefined -> - gs:gridline('BindGrid', - [{row, Row}, - {height, 14}, - {font, Font} | Opts]); - GridLine -> - gs:config(GridLine, Opts) - end, - Row+1 - end, - 2, - Bs), - delete_gridlines(Last). - -delete_gridlines(Row) -> - case gs:read('BindGrid', {obj_at_row, Row}) of - undefined -> true; - GridLine -> - gs:destroy(GridLine), - delete_gridlines(Row+1) - end. - -%%-------------------------------------------------------------------- -%% trace_output(Str) -%% Str = string() -%%-------------------------------------------------------------------- -trace_output(Str) -> - Font = dbg_ui_win:font(normal), - config_editor('TraceEditor', - [{insert, {'end',Str}}, - {fg, {{{1,0},'end'},black}}, - {font_style, {{{1,0},'end'},Font}}]), - Max = gs:read('TraceEditor', size), - config_editor('TraceEditor', {vscrollpos, Max}). - -%%-------------------------------------------------------------------- -%% handle_event(GSEvent, WinInfo) -> Command -%% GSEvent = {gs, Id, Event, Data, Arg} -%% WinInfo = #winInfo{} -%% Command = ignore -%% | {win, WinInfo} -%% | stopped -%% | {coords, {X,Y}} -%% -%% | {shortcut, Key} -%% | MenuItem | {Menu, [MenuItem]} -%% MenuItem = Menu = atom() -%% | {break, Point, What} -%% What = add | delete | {status,Status} |{trigger,Trigger} -%% | {module, Mod, view} -%% -%% | {user_command, Cmd} -%% -%% | {edit, {Var, Val}} -%%-------------------------------------------------------------------- -%% Window events -handle_event({gs, _Id, configure, _Data, [W, H|_]}, WinInfo) -> - case WinInfo#winInfo.size of - {W, H} -> ignore; - _Size -> - configure(WinInfo, W, H), - {win, WinInfo#winInfo{size={W, H}}} - end; -handle_event({gs, _Id, destroy, _Data, _Arg}, _WinInfo) -> - stopped; -handle_event({gs, _Id, motion, _Data, [X,Y]}, WinInfo) -> - {LastX, LastY} = dbg_ui_win:motion(X, Y), - Win = WinInfo#winInfo.window, - {coords, {gs:read(Win, x)+LastX-5, gs:read(Win, y)+LastY-5}}; -handle_event({gs, RB, buttonpress, resizebar, _Arg}, WinInfo) -> - resize(WinInfo, RB), % Resize window contents - ignore; - -%% Menus, buttons and keyboard shortcuts -handle_event({gs, _Id, keypress, _Data, [Key,_,_,1]}, _WinInfo) -> - {shortcut, Key}; -handle_event({gs, _Id, click, {dbg_ui_winman, Win}, _Arg}, _WinInfo) -> - dbg_ui_winman:raise(Win), - ignore; -handle_event({gs, _Id, click, {menuitem, Name}, _Arg}, _WinInfo) -> - Name; -handle_event({gs, _Id, click, {menu, Menu}, _Arg}, _WinInfo) -> - Names = dbg_ui_win:selected(Menu), - {Menu, Names}; -handle_event({gs, _Id, click, {break, Point, What}, _Arg}, _WinInfo) -> - {break, Point, What}; -handle_event({gs, _Id, click, {module, Mod, view}, _Arg}, _WinInfo) -> - {module, Mod, view}; - -%% Code area -handle_event({gs, Editor, buttonpress, code_editor, _Arg}, WinInfo) -> - {Row, _Col} = gs:read(Editor, insertpos), - Again = receive - {gs, Editor, buttonpress, code_editor, _} -> - gs:read(Editor, insertpos) - after 500 -> - false - end, - case Again of - {Row, _} -> - {Mod, _Editor} = WinInfo#winInfo.editor, - Point = {Mod, Row}, - case lists:keymember(Point, #breakInfo.point, - WinInfo#winInfo.breaks) of - false -> {break, Point, add}; - true -> {break, Point, delete} - end; - {Row2, _} -> - select_line(Editor, Row2, true), - {win, WinInfo#winInfo{selected_line=Row2}}; - false -> - select_line(Editor, Row, true), - {win, WinInfo#winInfo{selected_line=Row}} - end; - -%% Button area -handle_event({gs, _Id, click, {button, Name}, _Arg}, _WinInfo) -> - Name; - -%% Evaluator area -handle_event({gs, 'EvalEntry', keypress, _Data, ['Return'|_]}, _WI) -> - Command = case gs:read('EvalEntry', text) of - [10] -> - eval_output("\n", normal), - ignore; - Cmd -> - eval_output([$>, Cmd, 10], normal), - {user_command, Cmd} - end, - gs:config('EvalEntry', [{text,""}, {focus,false}]), - Command; - -%% Bindings area -handle_event({gs, _Id, click, {binding, {Var, Val}}, _Arg}, _WinInfo) -> - Str = io_lib:format("< ~p = ~p~n", [Var, Val]), - eval_output(Str, bold), - ignore; -handle_event({gs, _Id, doubleclick, {binding, B}, _Arg}, _WinInfo) -> - {edit, B}; - -handle_event(_GSEvent, _WinInfo) -> - ignore. - - -%%==================================================================== -%% Internal functions -%%==================================================================== - -%%--Code Area--------------------------------------------------------- - -code_area(X, Y, FrameOpts, Win) -> - gs:frame('CodeArea', Win, - [{x,X}, {y,Y}, {width,546}, {height,400} | FrameOpts]), - gs:label(info_window, 'CodeArea', - [{label,{text,""}}, {font,dbg_ui_win:font(normal)}, - {x,5}, {y,10}, {width,406}, {height,15}, - {anchor,nw}, {align,w}]), - code_editor('CodeEditor', 536, 365). - -code_editor() -> - W = gs:read('CodeEditor', width), - H = gs:read('CodeEditor', height), - code_editor(null, W, H). - -code_editor(Name, W, H) -> - Editor = if - Name =:= null -> gs:editor('CodeArea', []); - true -> gs:editor(Name, 'CodeArea', []) - end, - gs:config(Editor, [{x,5}, {y,30}, {width,W}, {height,H}, - {keypress,false}, {buttonpress,true}, - {data,code_editor}]), - config_editor(Editor, [{vscroll,right}, {hscroll,bottom}]), - Font = dbg_ui_win:font(normal), - config_editor(Editor, [{wrap,none}, {fg,{{{1,0},'end'},black}}, - {font, Font}, - {font_style, {{{1,0},'end'},Font}}]), - Editor. - -resize_code_area(WinInfo, Key, Diff) -> - gs:config('CodeArea', {Key,gs:read('CodeArea', Key)+Diff}), - case Key of - width -> - gs:config(info_window, {Key,gs:read(info_window,Key)+Diff}); - height -> ignore - end, - - %% Resize all code editors - Value = gs:read('CodeEditor', Key)+Diff, - gs:config('CodeEditor', {Key,Value}), - Editors = WinInfo#winInfo.editors, - lists:foreach(fun({_Mod, Editor}) -> - gs:config(Editor, {Key,Value}) - end, - Editors). - -%%--Button Area------------------------------------------------------- - -buttons() -> - [{'Step','StepButton'}, {'Next','NextButton'}, - {'Continue','ContinueButton'}, {'Finish','FinishButton'}, - {'Where','WhereButton'}, {'Up','UpButton'}, {'Down','DownButton'}]. - -is_button(Name) -> - case lists:keyfind(Name, 1, buttons()) of - {Name, Button} -> {true, Button}; - false -> false - end. - -button_area(Bu, X, Y, FrameOpts, Win) -> - {W,H} = case Bu of - open -> {546,30}; - close -> {0,0} - end, - gs:frame('ButtonArea', Win, - [{x,X}, {y,Y}, {width,W}, {height,H} | FrameOpts]), - Font = dbg_ui_win:font(normal), - lists:foldl(fun({Name, Button}, Xb) -> - gs:button(Button, 'ButtonArea', - [{label, {text,Name}}, {font,Font}, - {x, Xb}, {y, 1}, - {width, 77}, {height, 24}, - {data, {button,Name}}]), - Xb+78 - end, - 1, - buttons()). - -resize_button_area(close, width, _Diff) -> - ignore; -resize_button_area(open, width, Diff) -> - gs:config('ButtonArea', {width, gs:read('ButtonArea', width)+Diff}). - -%%--Evaluator Area---------------------------------------------------- - -eval_area({Ev,Bi}, X, Y, FrameOpts, Win) -> - {W,H} = if - Ev =:= open -> {289,200}; - true -> {0,0} - end, - Font = dbg_ui_win:font(normal), - gs:frame('EvalArea', Win, - [{x,X}, {y,Y}, {width,W}, {height,H} | FrameOpts]), - gs:label('EvalArea', - [{label,{text,"Evaluator:"}}, {font, Font}, - {x,5}, {y,35}, {width,80}, {height,25}, - {anchor,sw}, {align,center}]), - gs:entry('EvalEntry', 'EvalArea', - [{font, Font}, - {x,80}, {y,35}, {width,185}, {height,25}, - {anchor,sw}, {keypress,true}]), - gs:editor('EvalEditor', 'EvalArea', - [{x,5}, {y,35}, {width, 280}, {height, 160}, - {keypress,false}, - {vscroll,right}, {hscroll,bottom}, - {wrap,none}, {fg,{{{1,0},'end'},black}}, - {font, Font}, - {font_style,{{{1,0},'end'},Font}}]), - gs:config('EvalEditor', {enable, false}), - if - Ev =:= open, Bi =:= close -> resize_eval_area(Ev, width, 257); - true -> ignore - end. - -resize_eval_area(close, _Key, _Diff) -> - ignore; -resize_eval_area(open, Key, Diff) -> - New = gs:read('EvalArea', Key)+Diff, - gs:config('EvalArea', {Key,New}), - case Key of - width -> - gs:config('EvalEntry', {width,New-104}), - gs:config('EvalEditor', {width,New-9}); - height -> - gs:config('EvalEditor', {height,New-40}) - end. - -%%--Bindings Area----------------------------------------------------- - -bind_area({Ev,Bi}, X, Y, FrameOpts, Win) -> - {W,H} = if - Bi =:= open -> {249,200}; - true -> {0,0} - end, - gs:frame('BindArea', Win, - [{x,X}, {y,Y}, {width,W}, {height,H} | FrameOpts]), - - Font = dbg_ui_win:font(bold), - gs:grid('BindGrid', 'BindArea', - [{x,2}, {y,2}, {height,193}, {width,241}, - {fg,black}, {vscroll,right}, {hscroll,bottom}, - {font,Font}, - calc_columnwidths(241), {rows, {1,50}}]), - gs:gridline('BindGrid', - [{row,1}, {height,14}, {fg,blue}, - {text,{1,"Name"}}, {text,{2,"Value"}}, {font,Font}]), - gs:config('BindGrid', {rows,{1,1}}), - if - Bi =:= open, Ev =:= close -> resize_bind_area(Bi, width, 297); - true -> ignore - end. - -resize_bind_area(close, _Key, _Diff) -> - ignore; -resize_bind_area(open, Key, Diff) -> - New = gs:read('BindArea', Key)+Diff, - gs:config('BindArea', {Key,New}), - case Key of - width -> - gs:config('BindGrid', {width,New-8}), - Cols = calc_columnwidths(New-8), - gs:config('BindGrid', Cols); - height -> - gs:config('BindGrid', {height,New-7}) - end. - -calc_columnwidths(Width) -> - if Width =< 291 -> - {columnwidths,[90,198]}; - true -> - S = (Width)/(90+198), - {columnwidths,[round(90*S),round(198*S)]} - end. - -%%--Trace Area-------------------------------------------------------- - -trace_area(Tr, X, Y, FrameOpts, Win) -> - {W,H} = case Tr of - open -> {546,200}; - close -> {0,0} - end, - gs:frame('TraceArea', Win, - [{x,X}, {y,Y}, {width,W}, {height,H} | FrameOpts]), - Editor = gs:editor('TraceEditor', 'TraceArea', - [{x,5}, {y,5}, {width,536}, {height,190}, - {keypress,false}]), - Font = dbg_ui_win:font(normal), - config_editor(Editor, - [{vscroll,right}, {hscroll,bottom}, - {wrap,none},{fg,{{{1,0},'end'},black}}, - {font, Font}, - {font_style,{{{1,0},'end'},Font}}]). - -resize_trace_area(close, _Key, _Diff) -> - ignore; -resize_trace_area(open, Key, Diff) -> - New = gs:read('TraceArea', Key)+Diff, - gs:config('TraceArea', {Key,New}), - gs:config('TraceEditor', {Key,New-10}). - -%%--Editors----------------------------------------------------------- - -config_editor(Editor, Opts) -> - gs:config(Editor, {enable,true}), - gs:config(Editor, Opts), - gs:config(Editor, {enable,false}). - -%%--Resize Bars------------------------------------------------------- -%% The resize bars are used to resize the areas within the window. - -%%-------------------------------------------------------------------- -%% resizebar(Flag, Name, X, Y, W, H, Obj) -> resizebar() -%% Flag = open | close -%% Name = atom() -%% X = Y = integer() Coordinates relative to Obj -%% W = H = integer() Width and height -%% Obj = gsobj() -%% Creates a 'resize bar', a frame object over which the cursor will -%% be of the 'resize' type. -%%-------------------------------------------------------------------- -resizebar(Flag, Name, X, Y, W, H, Obj) -> - {W2,H2} = case Flag of - open -> {W,H}; - close -> {0,0} - end, - gs:create(frame, Name, Obj, [{x,X}, {y,Y}, {width,W2}, {height,H2}, - {bw,2}, - {cursor,resize}, - {buttonpress,true}, {buttonrelease,true}, - {data,resizebar}]). - -rb1({_Bu,Ev,Bi,Tr}) -> - if - Ev =:= close, Bi =:= close, Tr =:= close -> close; - true -> open - end. - -rb2({_Bu,Ev,Bi,Tr}) -> - if - Tr =:= open -> - if - Ev =:= close, Bi =:= close -> close; - true -> open - end; - true -> close - end. - -rb3({_Bu,Ev,Bi,_Tr}) -> - if - Ev =:= open, Bi =:= open -> open; - true -> close - end. - -%%--Configuration----------------------------------------------------- -%% Resize the window as well as its contents - -%%-------------------------------------------------------------------- -%% config_v() -%% Reconfigure the window vertically. -%%-------------------------------------------------------------------- -config_v() -> - Y1 = 25+gs:read('CodeArea', height), - gs:config('RB1', {y,Y1}), - - Y2 = Y1+gs:read('RB1', height), - gs:config('ButtonArea', {y,Y2}), - - Y3 = Y2+gs:read('ButtonArea', height), - gs:config('EvalArea', {y,Y3}), - gs:config('RB3', {y,Y3}), - gs:config('BindArea', {y,Y3}), - - Y4 = Y3 + erlang:max(gs:read('EvalArea', height), - gs:read('BindArea', height)), - gs:config('RB2', {y,Y4}), - - Y5 = Y4 + gs:read('RB2', height), - gs:config('TraceArea', {y,Y5}). - -%%-------------------------------------------------------------------- -%% config_h() -%% Reconfigure the window horizontally. -%%-------------------------------------------------------------------- -config_h() -> - X1 = 2+gs:read('EvalArea', width), - gs:config('RB3', {x,X1}), - - X2 = X1+gs:read('RB3', width), - gs:config('BindArea', {x,X2}). - -%%-------------------------------------------------------------------- -%% configure(WinInfo, W, H) -%% The window has been resized, now its contents must be resized too. -%%-------------------------------------------------------------------- -configure(WinInfo, NewW, NewH) -> - {Bu,Ev,Bi,Tr} = Flags = WinInfo#winInfo.flags, - - OldW = gs:read('CodeArea', width)+4, - OldH = 25+gs:read('CodeArea', height)+ - gs:read('RB1', height)+ - gs:read('ButtonArea', height)+ - erlang:max(gs:read('EvalArea', height), gs:read('BindArea', height))+ - gs:read('RB2', height)+ - gs:read('TraceArea', height), - - %% Adjust width unless it is unchanged or less than minimum width - if - OldW =/= NewW -> - {Dcode,Deval,Dbind} = configure_widths(OldW,NewW,Flags), - resize_code_area(WinInfo, width, Dcode), - case rb1(Flags) of - open -> - gs:config('RB1', {width,gs:read('RB1',width)+Dcode}); - close -> ignore - end, - resize_button_area(Bu, width, Dcode), - resize_eval_area(Ev, width, Deval), - resize_bind_area(Bi, width, Dbind), - case rb2(Flags) of - open -> - gs:config('RB2', {width,gs:read('RB2',width)+Dcode}); - close -> ignore - end, - resize_trace_area(Tr, width, Dcode), - config_h(); - true -> ignore - end, - - %% Adjust height unless it is unchanged or less than minimum height - if - OldH =/= NewH -> - {Dcode2,Deval2,Dtrace} = configure_heights(OldH,NewH,Flags), - resize_code_area(WinInfo, height, Dcode2), - resize_eval_area(Ev, height, Deval2), - case rb3(Flags) of - open -> - gs:config('RB3', - {height,gs:read('RB3',height)+Deval2}); - close -> ignore - end, - resize_bind_area(Bi, height, Deval2), - resize_trace_area(Tr, height, Dtrace), - config_v(); - true -> ignore - end. - -%% Compute how much the width of each frame must be increased or -%% decreased in order to adjust to the new window width. -configure_widths(OldW, NewW, Flags) -> - {_Bu,Ev,Bi,_Tr} = Flags, - - %% Difference between old and new width, considering min window width - Diff = abs(erlang:max(OldW,330)-erlang:max(NewW,330)), - - %% Check how much the frames can be resized in reality - Limits = if - %% Window larger - NewW > OldW -> - if - Ev =:= open, Bi =:= open -> {0,Diff,Diff}; - Ev =:= open -> {0,Diff,0}; - Bi =:= open -> {0,0,Diff}; - true -> {Diff,0,0} - end; - - %% Window smaller; get difference between min size - %% and current size - OldW>NewW -> - if - Ev =:= open, Bi =:= open -> - {0, - gs:read('EvalArea',width)-204, - gs:read('BindArea',width)-112}; - Ev =:= open -> {0,Diff,0}; - Bi =:= open -> {0,0,Diff}; - true -> {Diff,0,0} - end - end, - - case Limits of - - %% No Shell or Bind frame, larger window - {T,0,0} when NewW > OldW -> {T,0,0}; - - %% No Shell or Bind frame, smaller window - {T,0,0} when OldW > NewW -> {-T,0,0}; - - %% Window larger; divide Diff among the frames and return result - {_,Sf,B} when NewW > OldW -> - {_,Sf2,B2} = divide([{0,0},{0,Sf},{0,B}],Diff), - {Sf2+B2,Sf2,B2}; - - %% Window smaller; divide Diff among the frames and return - %% the inverted result (the frames should shrink) - {_,Sf,B} when OldW>NewW -> - {_,Sf2,B2} = divide([{0,0},{0,Sf},{0,B}],Diff), - {-(Sf2+B2),-Sf2,-B2} - end. - -%% Compute how much the height of each frame must be increased or -%% decreased in order to adjust to the new window height. -configure_heights(OldH, NewH, Flags) -> - {_Bu,Ev,Bi,Tr} = Flags, - - %% Difference between old and new height, considering min win height - MinH = min_height(Flags), - Diff = abs(erlang:max(OldH,MinH)-erlang:max(NewH,MinH)), - - %% Check how much the frames can be resized in reality - {T,Sf,Ff} = if - %% Window larger - NewH > OldH -> - {Diff, - if - Ev =:= close, Bi =:= close -> 0; - true -> Diff - end, - if - Tr =:= open -> Diff; - true -> 0 - end}; - - %% Window smaller; get difference between min size - %% and current size - OldH > NewH -> - {gs:read('CodeArea',height)-100, - if - Ev =:= close, Bi =:= close -> 0; - true -> - if - Ev =:= open -> - gs:read('EvalArea',height)-100; - Bi =:= open -> - gs:read('BindArea',height)-100 - end - end, - if - Tr =:= open -> gs:read('TraceArea',height)-100; - true -> 0 - end} - end, - - if - %% Window larger; divide Diff among the frames and return result - NewH>OldH -> divide([{0,T},{0,Sf},{0,Ff}],Diff); - - %% Window smaller; divide Diff among the frames and return - %% the inverted result (the frames should shrink) - OldH>NewH -> - {T2,Sf2,Ff2} = divide([{0,T},{0,Sf},{0,Ff}],Diff), - {-T2,-Sf2,-Ff2} - end. - -%% Compute minimum window height -min_height(Flags) -> - {Bu,S,Bi,F} = Flags, - H1 = 25 + 100 + 2, % Upper pad + Trace frame + lower pad - H2 = H1 + bu(Bu) + s_bi(S,Bi) + f(F), - H3 = case rb1(Flags) of - open -> H2+10; - close -> H2 - end, - H4 = case rb2(Flags) of - open -> H3+10; - close -> H3 - end, - H4. - -bu(close) -> 0; -bu(open) -> 30. - -s_bi(close,close) -> 0; -s_bi(_,_) -> 100. - -f(close) -> 0; -f(open) -> 100. - -%% Try to distribute Diff as evenly as possible between E1, E2 and E3. -divide([{T,T},{S,S},{F,F}], _Diff) -> - {T,S,F}; -divide(L, Diff) -> - [{T,Tmax},{S,Smax},{F,Fmax}] = L, - - %% Count how many elements in L can still be filled - Rem = remaining(L), - - %% Divide Diff by Rem - D = Diff div Rem, - - if - %% All of Diff has been distributed - D =:= 0 -> {T,S,F}; - true -> - %% For each element, try to add as much as possible of D - {NewT,Dt} = divide2(D,T,Tmax), - {NewS,Ds} = divide2(D,S,Smax), - {NewF,Df} = divide2(D,F,Fmax), - - %% Recur with a list of elements with new current values - %% and decreased Diff - divide([{NewT,Tmax},{NewS,Smax},{NewF,Fmax}], - (Diff rem Rem)+Dt+Ds+Df) - end. - -%% Count the number of 'non-filled' elements in L, ie where Curr<Max. -remaining([]) -> 0; -remaining([{Max,Max}|T]) -> remaining(T); -remaining([_H|T]) -> 1 + remaining(T). - -divide2(_Diff, Max, Max) -> {Max,0}; -divide2(Diff, Curr, Max) -> - New = Curr+Diff, - if - New>Max -> {Max,New-Max}; - true -> {New,0} - end. - -%%--Resizing using resize bars---------------------------------------- -%% Motions event will move the ResizeBar accordingly in Win, when -%% the mouse button is released, the window is reconfigured. - -resize(WinInfo, ResizeBar) -> - - %% Get window dimensions - W = gs:read(WinInfo#winInfo.window, width), - H = gs:read(WinInfo#winInfo.window, height), - - %% Call resize loop with min and max for the resize bars derived - %% from the window dimensions - resizeloop(WinInfo, ResizeBar, null, - rblimits('RB1',W,H), - rblimits('RB2',W,H), - rblimits('RB3',W,H)). - -resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}) -> - receive - {gs,_,motion,_,[_,Y]} when RB =:= 'RB1', Y > Min1, Y < Max1 -> - gs:config('RB1', {y,Y}), - resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,_} when RB =:= 'RB1' -> - resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - - {gs,_,motion,_,[_,Y]} when RB =:= 'RB2', Y > Min2, Y < Max2 -> - gs:config('RB2', {y,Y}), - resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,_} when RB =:= 'RB2' -> - resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - - {gs,_,motion,_,[X,_]} when RB =:= 'RB3', X > Min3, X < Max3 -> - gs:config('RB3', {x,X}), - resizeloop(WI, RB, X, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,_} when RB =:= 'RB3' -> - resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - - {gs,_,buttonrelease,_,_} -> - resize_win(WI, RB, Prev) - end. - -resize_win(_WinInfo, _RB, null) -> ignore; -resize_win(WinInfo, 'RB1', Y) -> - {_Bu,S,Bi,F} = Flags = WinInfo#winInfo.flags, - H = gs:read('CodeArea', height), - Diff = H-(Y-25), - - %% Resize Code, Evaluator and Binding areas - resize_code_area(WinInfo, height, -Diff), - if - S =:= close, Bi =:= close, F =:= open -> - resize_trace_area(open, height, Diff); - true -> - resize_eval_area(S, height, Diff), - resize_bind_area(Bi, height, Diff) - end, - - %% Resize vertical resize bar - case rb3(Flags) of - open -> gs:config('RB3', {height,gs:read('RB3',height)+Diff}); - close -> ignore - end, - - %% Adjust the frames y coordinates - config_v(); -resize_win(WinInfo, 'RB2', Y) -> - {_Bu,S,Bi,F} = Flags = WinInfo#winInfo.flags, - Prev = gs:read('TraceArea',y), - Diff = Prev-(Y+10), - - %% Resize Trace, Evaluator and Binding areas - resize_trace_area(F, height, Diff), - resize_eval_area(S, height, -Diff), - resize_bind_area(Bi, height, -Diff), - - %% Resize vertical resize bar - case rb3(Flags) of - open -> gs:config('RB3', {height,gs:read('RB3',height)-Diff}); - close -> ignore - end, - - %% Adjust the frames y coordinates - config_v(); - -resize_win(WinInfo, 'RB3', X) -> - {_Bu,S,Bi,_F} = WinInfo#winInfo.flags, - Prev = gs:read('BindArea', x), - Diff = Prev-(X+10), - - %% Resize Binding and Trace areas - resize_bind_area(Bi, width, Diff), - resize_eval_area(S, width, -Diff), - - %% Adjust the frames x coordinates - config_h(). - -%% Given the window dimensions, return the limits for a resize bar. -rblimits('RB1',_W,H) -> - - %% Code frame should not have height <100 - Min = 126, - - %% Max is decided by a minimum distance to 'RB2' - %% unless 'RB2' is invisible and 'CodeArea' is visible - %% (=> EvalFrame and BindFrame invisible) in which case - %% TraceFrame should not have height <100 - RB2 = gs:read('RB2',height), - FF = gs:read('TraceArea',height), - Max = case RB2 of - 0 when FF =/= 0 -> - H-112; - _ -> - Y = gs:read('RB2',y), - erlang:max(Min,Y-140) - end, - - {Min,Max}; -rblimits('RB2',_W,H) -> - %% TraceFrame should not have height < 100 - Max = H-112, - %% Min is decided by a minimum distance to 'RB1' - Y = gs:read('RB1',y), - Min = erlang:min(Max,Y+140), - {Min,Max}; - -rblimits('RB3',W,_H) -> - %% Neither CodeArea nor BindArea should occupy - %% less than 1/3 of the total window width and EvalFrame should - %% be at least 289 pixels wide - {erlang:max(round(W/3),289),round(2*W/3)}. - - -%%==================================================================== -%% 'Go To Line' and 'Search' help windows -%%==================================================================== - -helpwin(gotoline, WinInfo, GS, Coords) -> - spawn_link(?MODULE, helpwin, [gotoline, WinInfo, GS, Coords,self()]); -helpwin(search, WinInfo, GS, Coords) -> - spawn_link(?MODULE, helpwin, [search, WinInfo, GS, Coords, self()]). - -helpwin(Type, WinInfo, GS, Coords, AttPid) -> - {_Mod, Editor} = WinInfo#winInfo.editor, - Data = case Type of - gotoline -> null; - search -> - {{1, 0}, false} - end, - Win = helpwin(Type, GS, Coords), - helpwin_loop(Type, AttPid, Editor, Data, Win). - -helpwin_loop(Type, AttPid, Editor, Data, Win) -> - receive - {gs, _Id, destroy, _Data, _Arg} -> - helpwin_stop(Type, AttPid, Editor, Data), - true; - - {gs, _Id, keypress, _Data, ['Return'|_]} -> - gs:config(btn(Win), flash), - Data2 = helpwin_action(Type, default, - AttPid, Editor, Data, Win), - helpwin_loop(Type, AttPid, Editor, Data2, Win); - {gs, _Id, keypress, _Data, _Arg} -> - helpwin_loop(Type, AttPid, Editor, Data, Win); - - {gs, _Id, click, _Data, ["Clear"]} -> - gs:config(ent(Win), {delete, {0,last}}), - Data2 = helpwin_clear(Type, AttPid, Editor, Data, Win), - helpwin_loop(Type, AttPid, Editor, Data2, Win); - {gs, _Id, click, _Data, ["Close"]} -> - helpwin_stop(Type, AttPid, Editor, Data), - true; - {gs, _Id, click, Action, _Arg} -> - Data2 = - helpwin_action(Type, Action, AttPid, Editor, Data, Win), - helpwin_loop(Type, AttPid, Editor, Data2, Win) - end. - -helpwin_stop(gotoline, _AttPid, _Editor, _Data) -> - ignore; -helpwin_stop(search, _AttPid, Editor, {Pos, _CS}) -> - unmark_string(Editor, Pos). - -helpwin_clear(gotoline, _AttPid, _Editor, Data, _Win) -> - Data; -helpwin_clear(search, _AttPid, Editor, {Pos, CS}, Win) -> - unmark_string(Editor, Pos), - gs:config(lbl(Win), {label, {text,""}}), - {{1, 0}, CS}. - -helpwin_action(gotoline, default, AttPid, _Editor, Data, Win) -> - case string:strip(gs:read(ent(Win), text)) of - "" -> ignore; - Str -> - case catch list_to_integer(Str) of - {'EXIT', _Reason} -> ignore; - Line -> AttPid ! {gui, {gotoline, Line}} - end - end, - Data; -helpwin_action(search, case_sensitive, _AttPid, _Ed, {Pos, CS}, _Win) -> - Bool = if CS =:= true -> false; CS =:= false -> true end, - {Pos, Bool}; -helpwin_action(search, default, _AttPid, Editor, {Pos, CS}, Win) -> - gs:config(lbl(Win), {label, {text, ""}}), - unmark_string(Editor, Pos), - case gs:read(ent(Win), text) of - "" -> {Pos, CS}; - Str -> - gs:config(lbl(Win), {label, {text,"Searching..."}}), - Str2 = lowercase(CS, Str), - case search(Str2, Editor, gs:read(Editor, size), Pos, CS) of - {Row, Col} -> - gs:config(lbl(Win), {label, {text,""}}), - mark_string(Editor, {Row, Col}, Str), - {{Row, Col}, CS}; - not_found -> - gs:config(lbl(Win), {label, {text,"Not found"}}), - {Pos, CS} - end - end. - -search(_Str, _Editor, Max, {Row, _Col}, _CS) when Row>Max -> - not_found; -search(Str, Editor, Max, {Row, Col}, CS) -> - SearchIn = lowercase(CS, gs:read(Editor, - {get,{{Row,Col+1},{Row,lineend}}})), - case string:str(SearchIn, Str) of - 0 -> search(Str, Editor, Max, {Row+1, 0}, CS); - N -> {Row, Col+N} - end. - -lowercase(true, Str) -> Str; -lowercase(false, Str) -> - [if Char >= $A, Char =< $Z -> Char+32; - true -> Char - end || Char <- Str]. - -mark_string(Editor, {Row, Col}, Str) -> - Between = {{Row,Col}, {Row,Col+length(Str)}}, - Font = dbg_ui_win:font(bold), - gs:config(Editor, [{vscrollpos, Row-5}, - {font_style, {Between, Font}}, - {fg, {Between, red}}]). - -unmark_string(Editor, {Row, Col}) -> - Between = {{Row,Col}, {Row,lineend}}, - Font = dbg_ui_win:font(normal), - gs:config(Editor, [{vscrollpos, Row-5}, - {font_style, {Between, Font}}, - {fg, {Between, black}}]). - -helpwin(Type, GS, {X, Y}) -> - W = 200, Pad = 10, Wbtn = 50, - - Title = case Type of search -> "Search"; gotoline -> "Go To Line" end, - Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y}, {width, W}, - {destroy, true}]), - - Ent = gs:entry(Win, [{x, Pad}, {y, Pad}, {width, W-2*Pad}, - {keypress, true}]), - Hent = gs:read(Ent, height), - - Font = dbg_ui_win:font(normal), - - {Ybtn, Lbl} = - case Type of - search -> - Ycb = Pad+Hent, - gs:checkbutton(Win, [{label, {text, "Case Sensitive"}}, - {font, Font}, - {align, w}, - {x, Pad}, {y, Ycb}, - {width, W-2*Pad}, {height, 15}, - {data, case_sensitive}]), - Ylbl = Ycb+15, - {Ylbl+Hent+Pad, - gs:label(Win, [{x, Pad}, {y, Ylbl}, - {width, W-2*Pad}, {height, Hent}])}; - gotoline -> {Pad+Hent+Pad, null} - end, - - BtnLbl = case Type of search -> "Search"; gotoline -> "Go" end, - Btn = gs:button(Win, [{label, {text, BtnLbl}}, {font, Font}, - {x, W/2-3/2*Wbtn-Pad}, {y, Ybtn}, - {width, Wbtn}, {height, Hent}, - {data, default}]), - gs:button(Win, [{label, {text, "Clear"}}, {font, Font}, - {x, W/2-1/2*Wbtn}, {y, Ybtn}, - {width, Wbtn}, {height, Hent}]), - gs:button(Win, [{label, {text, "Close"}}, {font, Font}, - {x, W/2+1/2*Wbtn+Pad}, {y, Ybtn}, - {width, Wbtn}, {height, Hent}]), - - H = Ybtn+Hent+Pad, - gs:config(Win, [{height, H}, {map, true}]), - {Ent, Lbl, Btn}. - -ent(Win) -> element(1, Win). -lbl(Win) -> element(2, Win). -btn(Win) -> element(3, Win). diff --git a/lib/debugger/src/dbg_ui_view.erl b/lib/debugger/src/dbg_ui_view.erl deleted file mode 100644 index be998f22ff..0000000000 --- a/lib/debugger/src/dbg_ui_view.erl +++ /dev/null @@ -1,255 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2011. 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(dbg_ui_view). - -%% External exports --export([start/2]). - --record(state, {gs, % term() Graphics system id - win, % term() Attach process window data - coords, % {X,Y} Mouse point position - mod % atom() Module - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start(GS, Mod) -%% Mod = atom() -%%-------------------------------------------------------------------- -start(GS, Mod) -> - Title = "View Module " ++ atom_to_list(Mod), - case dbg_ui_winman:is_started(Title) of - true -> ignore; - false -> spawn(fun () -> init(GS, Mod, Title) end) - end. - --spec stop() -> no_return(). -stop() -> - exit(stop). - -%%==================================================================== -%% Main loop and message handling -%%==================================================================== - -init(GS, Mod, Title) -> - %% Subscribe to messages from the interpreter - int:subscribe(), - - %% Create attach process window - Win1 = dbg_ui_trace_win:create_win(GS, Title, ['Code Area'], menus()), - Window = dbg_ui_trace_win:get_window(Win1), - dbg_ui_winman:insert(Title, Window), - - Win2 = gui_load_module(Win1, Mod), - Win3 = - lists:foldl(fun(Break, Win) -> - dbg_ui_trace_win:add_break(Win, 'Break', Break) - end, - Win2, - int:all_breaks(Mod)), - - loop(#state{gs=GS, win=Win3, coords={0,0}, mod=Mod}). - -loop(State) -> - receive - - %% From the GUI main window - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> - Cmd = dbg_ui_trace_win:handle_event(GuiEvent, State#state.win), - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the GUI help windows - {gui, Cmd} -> - State2 = gui_cmd(Cmd, State), - loop(State2); - - %% From the interpreter - {int, Cmd} -> - State2 = int_cmd(Cmd, State), - loop(State2); - - %% From the dbg_ui_winman process (Debugger window manager) - {dbg_ui_winman, update_windows_menu, Data} -> - dbg_ui_winman:update_windows_menu(Data), - loop(State); - {dbg_ui_winman, destroy} -> - stop(); - - %% Help window termination -- ignore - {'EXIT', _Pid, _Reason} -> - loop(State) - end. - -%%--Commands from the GUI--------------------------------------------- - -gui_cmd(ignore, State) -> - State; -gui_cmd({win, Win}, State) -> - State#state{win=Win}; -gui_cmd(stopped, _State) -> - stop(); -gui_cmd({coords, Coords}, State) -> - State#state{coords=Coords}; - -gui_cmd({shortcut, Key}, State) -> - case shortcut(Key) of - false -> State; - Cmd -> gui_cmd(Cmd, State) - end; - -%% File menu -gui_cmd('Close', _State) -> - stop(); - -%% Edit menu -gui_cmd('Go To Line...', State) -> - %% Will result in message handled below: {gui, {gotoline, Line}} - dbg_ui_trace_win:helpwin(gotoline, State#state.win, - State#state.gs, State#state.coords), - State; -gui_cmd({gotoline, Line}, State) -> - Win = dbg_ui_trace_win:select_line(State#state.win, Line), - State#state{win=Win}; -gui_cmd('Search...', State) -> - dbg_ui_trace_win:helpwin(search, State#state.win, - State#state.gs, State#state.coords), - State; - -%% Break menu -gui_cmd('Line Break...', State) -> - add_break(State#state.gs, State#state.coords, line, - State#state.mod, - dbg_ui_trace_win:selected_line(State#state.win)), - State; -gui_cmd('Conditional Break...', State) -> - add_break(State#state.gs, State#state.coords, conditional, - State#state.mod, - dbg_ui_trace_win:selected_line(State#state.win)), - State; -gui_cmd('Function Break...', State) -> - add_break(State#state.gs, State#state.coords, function, - State#state.mod, undefined), - State; -gui_cmd('Enable All', State) -> - Breaks = int:all_breaks(), - ThisMod = State#state.mod, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> - int:enable_break(Mod, Line); - (_Break) -> - ignore - end, - Breaks), - State; -gui_cmd('Disable All', State) -> - Breaks = int:all_breaks(), - ThisMod = State#state.mod, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> - int:disable_break(Mod, Line); - (_Break) -> - ignore - end, - Breaks), - State; -gui_cmd('Delete All', State) -> - int:no_break(State#state.mod), - State; -gui_cmd({break, {Mod, Line}, What}, State) -> - case What of - add -> int:break(Mod, Line); - delete -> int:delete_break(Mod, Line); - {status, inactive} -> int:disable_break(Mod, Line); - {status, active} -> int:enable_break(Mod, Line); - {trigger, Action} -> int:action_at_break(Mod, Line, Action) - end, - State; - -%% Help menu -gui_cmd('Debugger', State) -> - Window = dbg_ui_trace_win:get_window(State#state.win), - HelpFile = filename:join([code:lib_dir(debugger), - "doc", "html", "part_frame.html"]), - tool_utils:open_help(Window, HelpFile), - State. - -add_break(GS, Coords, Type, undefined, _Line) -> - dbg_ui_break:start(GS, Coords, Type); -add_break(GS, Coords, Type, Mod, undefined) -> - dbg_ui_break:start(GS, Coords, Type, Mod); -add_break(GS, Coords, Type, Mod, Line) -> - dbg_ui_break:start(GS, Coords, Type, Mod, Line). - -%%--Commands from the interpreter------------------------------------- - -int_cmd({new_break, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) -> - Win = dbg_ui_trace_win:add_break(State#state.win, 'Break', Break), - State#state{win=Win}; -int_cmd({delete_break, {Mod,_Line}=Point}, #state{mod=Mod}=State) -> - Win = dbg_ui_trace_win:delete_break(State#state.win, Point), - State#state{win=Win}; -int_cmd({break_options, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) -> - Win = dbg_ui_trace_win:update_break(State#state.win, Break), - State#state{win=Win}; -int_cmd(no_break, State) -> - Win = dbg_ui_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; -int_cmd({no_break, _Mod}, State) -> - Win = dbg_ui_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; -int_cmd(_, State) -> - State. - - -%%==================================================================== -%% GUI auxiliary functions -%%==================================================================== - -menus() -> - [{'File', [{'Close', 0}]}, - {'Edit', [{'Go To Line...', 0}, - {'Search...', 0}]}, - {'Break', [{'Line Break...', 5}, - {'Conditional Break...', 13}, - {'Function Break...', 0}, - separator, - {'Enable All', no}, - {'Disable All', no}, - {'Delete All', 0}, - separator]}, - {'Help', [{'Debugger', no}]}]. - -shortcut(c) -> 'Close'; -shortcut(g) -> 'Go To Line...'; -shortcut(s) -> 'Search...'; -shortcut(b) -> 'Line Break...'; -shortcut(r) -> 'Conditional Break...'; -shortcut(f) -> 'Function Break...'; -shortcut(d) -> 'Delete All'; - -shortcut(_) -> false. - -gui_load_module(Win, Mod) -> - dbg_ui_trace_win:display({text, "Loading module..."}), - Contents = int:contents(Mod, any), - Win2 = dbg_ui_trace_win:show_code(Win, Mod, Contents), - dbg_ui_trace_win:display({text, ""}), - Win2. diff --git a/lib/debugger/src/dbg_ui_win.erl b/lib/debugger/src/dbg_ui_win.erl deleted file mode 100644 index 1c7a80a1c4..0000000000 --- a/lib/debugger/src/dbg_ui_win.erl +++ /dev/null @@ -1,281 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2012. 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(dbg_ui_win). --compile([{nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,menu,2}}, - {nowarn_deprecated_function,{gs,menu,3}}, - {nowarn_deprecated_function,{gs,menubutton,2}}, - {nowarn_deprecated_function,{gs,menuitem,2}}, - {nowarn_deprecated_function,{gs,menuitem,3}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,start,1}}]). - -%% External exports --export([init/0, - font/1, min_size/3, min_size/4, - create_menus/2, select/2, selected/1, - add_break/2, update_break/2, delete_break/1, - motion/2 - ]). - --record(break, {mb, smi, emi, dimi, demi}). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% init() -> GS -%% GS = term() -%%-------------------------------------------------------------------- -init() -> - gs:start([{kernel, true}]). - -%%-------------------------------------------------------------------- -%% font(Style) -> Font -%% Style = normal | bold -%% Select a suitable font. Defaults to {screen,12} and, if it does not -%% exist, {courier,12}. -%%-------------------------------------------------------------------- -font(Style) -> - GS = init(), - Style2 = if - Style =:= normal -> []; - true -> [Style] - end, - case gs:read(GS, {choose_font, {screen,Style2,12}}) of - Font when element(1, Font) =:= screen -> - Font; - _ -> - gs:read(GS, {choose_font, {courier,Style2,12}}) - end. - -%%-------------------------------------------------------------------- -%% min_size(Strings, MinW, MinH) -> {W, H} -%% min_size(Font, Strings, MinW, MinH) -> {W, H} -%% Font = GS font - defaults to dbg_ui_win:font(normal) -%% Strings = [string()] -%% MinW = MinH = int() -%% W = H = int() -%%-------------------------------------------------------------------- -min_size(Strings, MinW, MinH) -> - min_size(font(normal), Strings, MinW, MinH). - -min_size(Font, Strings, MinW, MinH) -> - GS = init(), - min_size(GS, Font, Strings, MinW, MinH). - -min_size(GS, Font, [String|Strings], MinW, MinH) -> - {W, H} = gs:read(GS, {font_wh, {Font, String}}), - min_size(GS, Font, Strings, erlang:max(MinW, W), erlang:max(MinH, H)); -min_size(_GS, _Font, [], W, H) -> - {W, H}. - -%%-------------------------------------------------------------------- -%% create_menus(MenuBar, [Menu]) -%% MenuBar = gsobj() -%% Menu = {Name, [Item]} -%% Name = atom() -%% Item = {Name, N} | {Name, N, Type} | {Name, N, cascade, [Item]} -%% | separator -%% N = no | integer() -%% Type = check | radio -%% Create the specified menus and menuitems. -%% -%% Normal menuitems are specified as {Name, N}. Generates the event: -%% {gs, _Id, click, {menuitem, Name}, _Arg} -%% -%% Check and radio menuitems are specified as {Name, N, check|radio}. -%% They are assumed to be children to a cascade menuitem! (And all children -%% to one cascade menuitem are assumed to be either check OR radio -%% menuitems)! -%% Selecting a check/radio menuitem generates the event: -%% {gs, _Id, click, {menu, Menu}, _Arg} -%% where Menu is the name of the parent, the cascade menuitem. -%% Use selected(Menu) to retrieve which check/radio menuitems are -%% selected. -%%-------------------------------------------------------------------- -create_menus(MenuBar, [{Title, Items}|Menus]) -> - Title2 = " "++(atom_to_list(Title))++" ", - MenuBtn = gs:menubutton(MenuBar, [{label, {text,Title2}}, - {font, font(normal)}]), - case Title of - 'Help' -> gs:config(MenuBtn, {side, right}); - _ -> ignore - end, - Menu = gs:menu(Title, MenuBtn, []), - create_items(Menu, Items, Title), - create_menus(MenuBar, Menus); -create_menus(_MenuBar, []) -> - done. - -create_items(Menu, [Item|Items], Group) -> - create_item(Menu, Item, Group), - create_items(Menu, Items, Group); -create_items(_Menu, [], _Group) -> - done. - -create_item(Menu, {Name, _N, cascade, Items}, _Group) -> - MenuBtn = gs:menuitem(Menu, [{label, {text,Name}}, - {font, font(normal)}, - {itemtype, cascade}]), - SubMenu = gs:menu(Name, MenuBtn, []), - create_items(SubMenu, Items, Name); -create_item(Menu, separator, _Group) -> - gs:menuitem(Menu, [{itemtype, separator}]); -create_item(Menu, MenuItem, Group) -> - Options = case MenuItem of - {Name, N} -> - [{data, {menuitem,Name}}]; - {Name, N, check} -> - [{itemtype, check}, {data, {menu, Group}}]; - {Name, N, radio} -> - [{itemtype, radio}, {data, {menu, Group}}, - {group, group(Group)}] - end, - gs:menuitem(Name, Menu, [{label, {text,Name}}, - {font, font(normal)} | Options]), - if - is_integer(N) -> gs:config(Name, {underline, N}); - true -> ignore - end. - -%% When grouping radio buttons, the group id must be an atom unique for -%% each window. -group(Group) -> - list_to_atom(atom_to_list(Group)++pid_to_list(self())). - -%%-------------------------------------------------------------------- -%% select(MenuItem, Bool) -%% MenuItem = atom() -%% Bool = boolean() -%%-------------------------------------------------------------------- -select(MenuItem, Bool) -> - gs:config(MenuItem, {select, Bool}). - -%%-------------------------------------------------------------------- -%% selected(Menu) -> [Name] -%% Menu = Name = atom() -%%-------------------------------------------------------------------- -selected(Menu) -> - Children = gs:read(Menu, children), - Selected = [gs:read(Child, select) || Child <- Children], - lists:map(fun(Child) -> - {text, Name} = gs:read(Child, label), - list_to_atom(Name) - end, - Selected). - -%%-------------------------------------------------------------------- -%% add_break(Name, Point) -> #break{} -%% Name = atom() -%% Point = {Mod, Line} -%% The break will generate the following events: -%% {gs, _Id, click, {break, Point, Event}, _Arg} -%% Event = delete | {trigger, Action} | {status, Status} -%% Action = enable | disable | delete -%% Status = active | inactive -%%-------------------------------------------------------------------- -add_break(Menu, Point) -> - Font = font(normal), - - %% Create a name for the breakpoint - {Mod, Line} = Point, - Label = io_lib:format("~w ~5w", [Mod, Line]), - - %% Create a menu for the breakpoint - MenuBtn = gs:menuitem(Menu, [{label, {text,Label}}, {font, Font}, - {itemtype, cascade}]), - SubMenu = gs:menu(MenuBtn, []), - SMI = gs:menuitem(SubMenu, [{data, {break,Point,null}}]), - gs:menuitem(SubMenu, [{label, {text,"Delete"}}, {font, Font}, - {data, {break,Point,delete}}]), - TriggerMenuBtn = gs:menuitem(SubMenu, - [{label,{text,"Trigger Action"}}, - {font, Font}, - {itemtype, cascade}]), - TriggerMenu = gs:menu(TriggerMenuBtn, []), - Group = element(3, erlang:now()), - EMI = gs:menuitem(TriggerMenu, [{label, {text,"Enable"}}, - {font, Font}, - {itemtype, radio}, {group, Group}, - {data, - {break,Point,{trigger,enable}}}]), - DiMI = gs:menuitem(TriggerMenu, [{label, {text,"Disable"}}, - {font, Font}, - {itemtype, radio}, {group, Group}, - {data, - {break,Point,{trigger,disable}}}]), - DeMI = gs:menuitem(TriggerMenu, [{label, {text,"Delete"}}, - {font, Font}, - {itemtype, radio}, {group, Group}, - {data, - {break,Point,{trigger,delete}}}]), - - #break{mb=MenuBtn, smi=SMI, emi=EMI, dimi=DiMI, demi=DeMI}. - -%%-------------------------------------------------------------------- -%% update_break(Break, Options) -%% Break = #break{} -%% Options = [Status, Action, Mods, Cond] -%% Status = active | inactive -%% Action = enable | disable | delete -%% Mods = null (not used) -%% Cond = null | {Mod, Func} -%%-------------------------------------------------------------------- -update_break(Break, Options) -> - [Status, Trigger|_] = Options, - {break, Point, _Status} = gs:read(Break#break.smi, data), - - {Label, Data} = case Status of - active -> - {"Disable", {break,Point,{status,inactive}}}; - inactive -> - {"Enable", {break,Point,{status,active}}} - end, - gs:config(Break#break.smi, [{label, {text,Label}}, - {font, font(normal)}, - {data, Data}]), - - TriggerMI = case Trigger of - enable -> Break#break.emi; - disable -> Break#break.dimi; - delete -> Break#break.demi - end, - gs:config(TriggerMI, {select, true}). - -%%-------------------------------------------------------------------- -%% delete_break(Break) -%% Break = #break{} -%%-------------------------------------------------------------------- -delete_break(Break) -> - gs:destroy(Break#break.mb). - -%%-------------------------------------------------------------------- -%% motion(X, Y) -> {X, Y} -%% X = Y = integer() -%%-------------------------------------------------------------------- -motion(X, Y) -> - receive - {gs, _Id, motion, _Data, [NX,NY]} -> - motion(NX, NY) - after 0 -> - {X, Y} - end. diff --git a/lib/debugger/src/dbg_ui_winman.erl b/lib/debugger/src/dbg_ui_winman.erl deleted file mode 100644 index b5433a202b..0000000000 --- a/lib/debugger/src/dbg_ui_winman.erl +++ /dev/null @@ -1,182 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2012. 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(dbg_ui_winman). --behaviour(gen_server). --compile([{nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,menu,3}}, - {nowarn_deprecated_function,{gs,menubutton,3}}, - {nowarn_deprecated_function,{gs,menuitem,3}}]). - -%% External exports --export([start/0]). --export([insert/2, is_started/1, - clear_process/1, - raise/1, - windows_menu/1, update_windows_menu/1]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --record(win, {owner, % pid() - title, % string() - win % gsobj() - }). - --record(state, {wins=[] % [#win{}] - }). - -%%==================================================================== -%% External exports -%%==================================================================== - -%%-------------------------------------------------------------------- -%% start() -%%-------------------------------------------------------------------- -start() -> - gen_server:start({local,?MODULE}, ?MODULE, [], []). - -%%-------------------------------------------------------------------- -%% insert(Title, Win) -%% Title = string() -%% Win = gsobj() -%%-------------------------------------------------------------------- -insert(Title, Win) -> - gen_server:cast(?MODULE, {insert, self(), Title, Win}). - -%%-------------------------------------------------------------------- -%% is_started(Title) -> true | false -%% Title = string() -%%-------------------------------------------------------------------- -is_started(Title) -> - case gen_server:call(?MODULE, {is_started, Title}, infinity) of - {true, Win} -> - raise(Win), - true; - false -> - false - end. - -%%-------------------------------------------------------------------- -%% clear_process(Title) -%% Title = string() -%%-------------------------------------------------------------------- -clear_process(Title) -> - gen_server:cast(?MODULE, {clear_process, Title}). - -%%-------------------------------------------------------------------- -%% raise(Win) -%% Win = gsobj() -%%-------------------------------------------------------------------- -raise(Win) -> - gs:config(Win, [raise, {iconify, false}, {setfocus, true}]). - -%%-------------------------------------------------------------------- -%% windows_menu(MenuBar) -%% MenuBar = gsobj() -%%-------------------------------------------------------------------- -windows_menu(MenuBar) -> - gs:menubutton('WindowsMenuBtn', MenuBar, - [{label,{text," Windows "}}, - {font, dbg_ui_win:font(normal)}]), - gs:menu('WindowsMenu', 'WindowsMenuBtn', []). - -%%-------------------------------------------------------------------- -%% update_windows_menu(Data) -%% Data = {New, Old} -%% New = Old = list() -%%-------------------------------------------------------------------- -update_windows_menu([MonInfo|Infos]) -> - gs:destroy('WindowsMenu'), - gs:menu('WindowsMenu', 'WindowsMenuBtn', []), - menuitem(MonInfo), - gs:menuitem(separator, 'WindowsMenu', [{itemtype, separator}]), - lists:foreach(fun(Info) -> menuitem(Info) end, Infos). - -menuitem({Title, Win}) -> - gs:menuitem(Title, 'WindowsMenu', [{label, {text,Title}}, - {font, dbg_ui_win:font(normal)}, - {data, {dbg_ui_winman,Win}}]). - - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -init(_Arg) -> - process_flag(trap_exit, true), - {ok, #state{}}. - -handle_call({is_started, Title}, _From, State) -> - Reply = case lists:keyfind(Title, #win.title, State#state.wins) of - false -> false; - Win -> {true, Win#win.win} - end, - {reply, Reply, State}. - -handle_cast({insert, Pid, Title, Win}, State) -> - link(Pid), - Wins = State#state.wins ++ [#win{owner=Pid, title=Title, win=Win}], - inform_all(Wins), - {noreply, State#state{wins=Wins}}; - -handle_cast({clear_process, Title}, State) -> - OldWins = State#state.wins, - Wins = case lists:keyfind(Title, #win.title, OldWins) of - #win{owner=Pid} -> - Msg = {dbg_ui_winman, destroy}, - Pid ! Msg, - lists:keydelete(Title, #win.title, OldWins); - false -> - OldWins - end, - {noreply, State#state{wins=Wins}}. - -handle_info({'EXIT', Pid, _Reason}, State) -> - [Mon | _Wins] = State#state.wins, - if - Pid =:= Mon#win.owner -> {stop, normal, State}; - true -> - Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins), - inform_all(Wins2), - {noreply, State#state{wins=Wins2}} - end. - -terminate(_Reason, State) -> - delete_all(State#state.wins), - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - -%%==================================================================== -%% Internal functions -%%==================================================================== - -inform_all(Wins) -> - Infos = lists:map(fun(#win{title=Title, win=Win}) -> {Title, Win} end, - Wins), - Msg = {dbg_ui_winman, update_windows_menu, Infos}, - lists:foreach(fun(#win{owner=Pid}) -> Pid ! Msg end, Wins). - -delete_all(Wins) -> - Msg = {dbg_ui_winman, destroy}, - lists:foreach(fun(#win{owner=Pid}) -> Pid ! Msg end, Wins). diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl index a617f3e1e7..0071b27027 100644 --- a/lib/debugger/src/dbg_wx_mon_win.erl +++ b/lib/debugger/src/dbg_wx_mon_win.erl @@ -43,7 +43,7 @@ -record(moduleInfo, {module, menubtn}). -record(procInfo, {pid, row}). -record(breakInfo, {point, status, break}). --record(break, {mb, smi, emi, dimi, demi}). %% BUGBUG defined in dbg_ui_win +-record(break, {mb, smi, emi, dimi, demi}). -record(winInfo, {window, % gsobj() grid, % gsobj() row, % int() Last row in grid @@ -76,13 +76,6 @@ init() -> dbg_wx_win:init(). -%%-------------------------------------------------------------------- -%% create_win(GS, Title, Menus) -> #winInfo{} -%% GS = gsobj() -%% Title = string() -%% Menus = [menu()] See dbg_ui_win.erl -%%-------------------------------------------------------------------- - -define(GRID,1000). -define(PAD, 5). diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index 7108b5a79a..1ac796bb4c 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -71,21 +71,10 @@ start(Pid, TraceWin, BackTrace) -> start(Pid, TraceWin, BackTrace, ?STRINGS). start(Pid, TraceWin, BackTrace, Strings) -> - case {whereis(dbg_wx_mon), whereis(dbg_ui_mon)} of - {undefined, undefined} -> - case which_gui() of - gs -> - dbg_ui_trace:start(Pid, TraceWin, BackTrace); - wx -> - Parent = wx:new(), - Env = wx:get_env(), - start(Pid, Env, Parent, TraceWin, BackTrace, Strings) - end; - {undefined, Monitor} when is_pid(Monitor) -> - dbg_ui_trace:start(Pid, TraceWin, BackTrace); - {Monitor, _} when is_pid(Monitor) -> + case whereis(dbg_wx_mon) of + Monitor when is_pid(Monitor) -> Monitor ! {?MODULE, self(), get_env}, - receive + receive {env, Monitor, Env, Parent} -> start(Pid, Env, Parent, TraceWin, BackTrace, Strings) end @@ -110,15 +99,6 @@ start(Pid, Env, Parent, TraceWin, BackTrace, Strings) -> ignore end. -which_gui() -> - try - wx:new(), - wx:destroy(), - wx - catch _:_ -> - gs - end. - %%-------------------------------------------------------------------- %% title(Pid) -> string() %% By exporting this function, dbg_wx_mon may check with dbg_wx_winman diff --git a/lib/debugger/src/debugger.app.src b/lib/debugger/src/debugger.app.src index 807054c983..84fb98c94e 100644 --- a/lib/debugger/src/debugger.app.src +++ b/lib/debugger/src/debugger.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -27,20 +27,6 @@ dbg_iload, dbg_iserver, dbg_istk, - dbg_ui_break, - dbg_ui_break_win, - dbg_ui_edit, - dbg_ui_edit_win, - dbg_ui_filedialog_win, - dbg_ui_interpret, - dbg_ui_mon, - dbg_ui_mon_win, - dbg_ui_settings, - dbg_ui_trace, - dbg_ui_trace_win, - dbg_ui_view, - dbg_ui_win, - dbg_ui_winman, dbg_wx_break, dbg_wx_break_win, dbg_wx_code, @@ -59,5 +45,5 @@ i, int ]}, - {registered, [dbg_iserver, dbg_ui_mon, dbg_ui_winman]}, - {applications, [kernel, stdlib, gs]}]}. + {registered, [dbg_iserver, dbg_wx_mon, dbg_wx_winman]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/debugger/src/debugger.erl b/lib/debugger/src/debugger.erl index b97091ee6b..8a2ac28df5 100644 --- a/lib/debugger/src/debugger.erl +++ b/lib/debugger/src/debugger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -32,44 +32,44 @@ %% -------- %% Interface module. %% -%% dbg_ui_winman +%% dbg_wx_winman %% ------------- %% Window manager, keeping track of open windows and Debugger %% processes. %% -%% dbg_ui_mon, dbg_ui_mon_win +%% dbg_wx_mon, dbg_wx_mon_win %% -------------------------- %% Monitor window, main window of Debugger, displaying information %% about interpreted modules and debugged processes. %% -%% dbg_ui_trace, dbg_ui_trace_win +%% dbg_wx_trace, dbg_wx_trace_win %% ------------------------------ %% Attach process window, showing the code executed by a debugged %% process and providing a GUI for stepping, inspecting variables etc. %% -%% dbg_ui_break, dbg_ui_break_win +%% dbg_wx_break, dbg_wx_break_win %% ------------------------------ %% Help window for creating new breakpoints. %% -%% dbg_ui_edit, dbg_ui_edit_win +%% dbg_wx_edit, dbg_wx_edit_win %% -------------------------------------- %% Help window for editing terms, used for setting backtrace size %% (i.e. how many stack frames to display in the attach process window) %% and changing variable values. %% -%% dbg_ui_interpret, dbg_ui_filedialog_win +%% dbg_wx_interpret, dbg_wx_filedialog_win %% -------------------------------------- %% Help window for selecting modules to interpret. %% -%% dbg_ui_settings, dbg_ui_filedialog_win +%% dbg_wx_settings, dbg_wx_filedialog_win %% -------------------------------------- %% Help window for saving and loading Debugger settings. %% -%% dbg_ui_view +%% dbg_wx_view %% ----------- -%% Help window for viewing interpreted modules (uses dbg_ui_trace_win). +%% Help window for viewing interpreted modules (uses dbg_wx_trace_win). %% -%% dbg_ui_win +%% dbg_wx_win %% ---------- %% GUI specific functionality used by more than one window type. %% @@ -78,7 +78,7 @@ start() -> start(global, default, default). start(Mode) when Mode==local; Mode==global -> start(Mode, default, default); -start(Gui) when Gui==gs; Gui==wx -> +start(Gui) when Gui==wx -> start(global, default, Gui); start(SFile) when is_list(SFile), is_integer(hd(SFile)) -> start(global, SFile, default). @@ -86,8 +86,6 @@ start(SFile) when is_list(SFile), is_integer(hd(SFile)) -> start(Mode, SFile) -> start(Mode, SFile, default). -start(Mode, SFile, gs) -> - dbg_ui_mon:start(Mode, SFile); start(Mode, SFile, wx) -> dbg_wx_mon:start(Mode, SFile); start(Mode, SFile, default) -> @@ -95,7 +93,7 @@ start(Mode, SFile, default) -> start(Mode, SFile, Gui). stop() -> - dbg_ui_mon:stop(). + dbg_wx_mon:stop(). quick(M, F, A) -> int:i(M), @@ -104,15 +102,7 @@ quick(M, F, A) -> auto_attach(Flags) -> case which_gui() of - gs -> int:auto_attach(Flags, {dbg_ui_trace, start, []}); wx -> int:auto_attach(Flags, {dbg_wx_trace, start, []}) end. -which_gui() -> - try - wx:new(), - wx:destroy(), - wx - catch _:_ -> - gs - end. +which_gui() -> wx. diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl index bdd671cff1..2755db64b8 100644 --- a/lib/debugger/src/int.erl +++ b/lib/debugger/src/int.erl @@ -630,14 +630,13 @@ find_beam(Mod, Src) -> File = filename:join(SrcDir, BeamFile), case is_file(File) of true -> File; - false -> find_beam_1(Mod, BeamFile, SrcDir) + false -> find_beam_1(BeamFile, SrcDir) end. -find_beam_1(Mod, BeamFile, SrcDir) -> +find_beam_1(BeamFile, SrcDir) -> RootDir = filename:dirname(SrcDir), EbinDir = filename:join(RootDir, "ebin"), CodePath = [EbinDir | code:get_path()], - BeamFile = atom_to_list(Mod) ++ code:objfile_extension(), lists:foldl(fun(_, Beam) when is_list(Beam) -> Beam; (Dir, error) -> File = filename:join(Dir, BeamFile), diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile index 5aa290c61b..9a0ce14ecd 100644 --- a/lib/debugger/test/Makefile +++ b/lib/debugger/test/Makefile @@ -44,6 +44,7 @@ MODULES= \ fun_SUITE \ lc_SUITE \ line_number_SUITE \ + map_SUITE \ record_SUITE \ trycatch_SUITE \ test_lib \ diff --git a/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl index 2e9c9570b5..ba60ed6fb3 100644 --- a/lib/debugger/test/erl_eval_SUITE.erl +++ b/lib/debugger/test/erl_eval_SUITE.erl @@ -39,7 +39,8 @@ otp_8133/1, funs/1, try_catch/1, - eval_expr_5/1]). + eval_expr_5/1, + eep37/1]). %% %% Define to run outside of test server @@ -79,7 +80,7 @@ all() -> pattern_expr, match_bin, guard_3, guard_4, lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, otp_6787, otp_6977, otp_7550, - otp_8133, funs, try_catch, eval_expr_5]. + otp_8133, funs, try_catch, eval_expr_5, eep37]. groups() -> []. @@ -1324,6 +1325,27 @@ eval_expr_5(Config) when is_list(Config) -> ok end. +eep37(Config) when is_list(Config) -> + check(fun () -> (fun _(X) -> X end)(42) end, + "(fun _(X) -> X end)(42).", + 42), + check(fun () -> (fun _Id(X) -> X end)(42) end, + "(fun _Id(X) -> X end)(42).", 42), + check(fun () -> is_function((fun Self() -> Self end)(), 0) end, + "is_function((fun Self() -> Self end)(), 0).", + true), + check(fun () -> + F = fun Fact(N) when N > 0 -> + N * Fact(N - 1); + Fact(0) -> + 1 + end, + F(6) + end, + "(fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end)(6).", + 720), + ok. + %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), diff --git a/lib/debugger/test/fun_SUITE.erl b/lib/debugger/test/fun_SUITE.erl index a06cdc7165..8425f973e6 100644 --- a/lib/debugger/test/fun_SUITE.erl +++ b/lib/debugger/test/fun_SUITE.erl @@ -24,7 +24,7 @@ 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,external/1]). + ext_badarity/1,otp_6061/1,external/1,eep37/1]). %% Internal exports. -export([nothing/0,call_me/1]). @@ -48,7 +48,7 @@ end_per_group(_GroupName, Config) -> cases() -> [good_call, bad_apply, bad_fun_call, badarity, - ext_badarity, otp_6061, external]. + ext_badarity, otp_6061, external, eep37]. init_per_testcase(_Case, Config) -> test_lib:interpret(?MODULE), @@ -288,5 +288,18 @@ external(Config) when is_list(Config) -> call_me(I) -> {ok,I}. +eep37(Config) when is_list(Config) -> + F = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end, + Add = fun _(N) -> N + 1 end, + UnusedName = fun BlackAdder(N) -> N + 42 end, + 720 = F(6), + 10 = Add(9), + 50 = UnusedName(8), + [1,1,2,6,24,120] = lists:map(F, lists:seq(0, 5)), + {'EXIT',{{badarity,_},_}} = (catch lists:map(fun G() -> G() end, [1])), + {'EXIT',{{badarity,_},_}} = (catch F()), + + ok. + id(I) -> I. diff --git a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl index c5c6a56363..ab485fd350 100644 --- a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl +++ b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl @@ -236,4 +236,8 @@ otp_8310() -> (catch {a, [X || X <- a]}), {'EXIT',{{bad_generator,b},_}} = (catch {a, << <<X>> || << X >> <= b >>}), + true = begin (X1 = true) andalso X1, X1 end, + false = begin (X2 = false) andalso X2, X2 end, + true = begin (X3 = true) orelse X3, X3 end, + false = begin (X4 = false) orelse X4, X4 end, ok. diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl new file mode 100644 index 0000000000..e9f4ea1fad --- /dev/null +++ b/lib/debugger/test/map_SUITE.erl @@ -0,0 +1,1002 @@ +%% %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(map_SUITE). + +%% Copied from map_SUITE in erts. + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2 + ]). + +-export([ + t_build_and_match_literals/1, + t_update_literals/1,t_match_and_update_literals/1, + t_update_map_expressions/1, + t_update_assoc/1,t_update_exact/1, + t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, + t_guard_receive/1, t_guard_fun/1, + t_list_comprehension/1, + t_map_sort_literals/1, + %t_size/1, + t_map_size/1, + + %% Specific Map BIFs + t_bif_map_get/1, + t_bif_map_find/1, + t_bif_map_is_key/1, + t_bif_map_keys/1, + t_bif_map_merge/1, + t_bif_map_new/1, + t_bif_map_put/1, + t_bif_map_remove/1, + t_bif_map_update/1, + t_bif_map_values/1, + t_bif_map_to_list/1, + t_bif_map_from_list/1, + + %% erlang + t_erlang_hash/1, + t_map_encode_decode/1, + + %% maps module not bifs + t_maps_fold/1, + t_maps_map/1, + t_maps_size/1, + t_maps_without/1, + + %% misc + t_pdict/1, + t_ets/1, + t_dets/1 + ]). + +-include_lib("stdlib/include/ms_transform.hrl"). + +suite() -> []. + +all() -> [ + t_build_and_match_literals, + t_update_literals, t_match_and_update_literals, + t_update_map_expressions, + t_update_assoc,t_update_exact, + t_guard_bifs, t_guard_sequence, t_guard_update, + t_guard_receive,t_guard_fun, t_list_comprehension, + t_map_sort_literals, + + %% Specific Map BIFs + t_bif_map_get,t_bif_map_find,t_bif_map_is_key, + t_bif_map_keys, t_bif_map_merge, t_bif_map_new, + t_bif_map_put, + t_bif_map_remove, t_bif_map_update, + t_bif_map_values, + t_bif_map_to_list, t_bif_map_from_list, + + %% erlang + t_erlang_hash, t_map_encode_decode, + t_map_size, + + %% maps module + t_maps_fold, t_maps_map, + t_maps_size, t_maps_without, + + + %% Other functions + t_pdict, + t_ets + ]. + +groups() -> []. + +init_per_suite(Config) -> + test_lib:interpret(?MODULE), + Config. + +end_per_suite(_Config) -> ok. + +init_per_group(_GroupName, Config) -> Config. +end_per_group(_GroupName, Config) -> Config. + +%% tests + +t_build_and_match_literals(Config) when is_list(Config) -> + #{} = id(#{}), + #{1:=a} = id(#{1=>a}), + #{1:=a,2:=b} = id(#{1=>a,2=>b}), + #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}), + #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}), + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}), + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), + + #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}), + + #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =) + + #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} = + id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}), + + M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}, + M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} = + id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), + + %% error case + %V = 32, + %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), + ok. + + +%% Tests size(Map). +%% not implemented, perhaps it shouldn't be either + +%t_size(Config) when is_list(Config) -> +% 0 = size(#{}), +% 1 = size(#{a=>1}), +% 1 = size(#{a=>#{a=>1}}), +% 2 = size(#{a=>1, b=>2}), +% 3 = size(#{a=>1, b=>2, b=>"3"}), +% ok. + +t_map_size(Config) when is_list(Config) -> + 0 = map_size(id(#{})), + 1 = map_size(id(#{a=>1})), + 1 = map_size(id(#{a=>"wat"})), + 2 = map_size(id(#{a=>1, b=>2})), + 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})), + + true = map_is_size(#{a=>1}, 1), + true = map_is_size(#{a=>1, a=>2}, 1), + M = #{ "a" => 1, "b" => 2}, + true = map_is_size(M, 2), + false = map_is_size(M, 3), + true = map_is_size(M#{ "a" => 2}, 2), + false = map_is_size(M#{ "c" => 2}, 2), + + %% Error cases. + {'EXIT',{badarg,_}} = (catch map_size([])), + {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{badarg,_}} = (catch map_size(1)), + ok. + +map_is_size(M,N) when map_size(M) =:= N -> true; +map_is_size(_,_) -> false. + +% test map updates without matching +t_update_literals(Config) when is_list(Config) -> + Map = #{x=>1,y=>2,z=>3,q=>4}, + #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ + {"a","1"},{"b","2"},{"c","3"},{"d","4"} + ]), + ok. + +loop_update_literals_x_q(Map, []) -> Map; +loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> + loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs). + +% test map updates with matching +t_match_and_update_literals(Config) when is_list(Config) -> + Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, + #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ + {1,2},{3,4},{5,6},{7,8} + ]), + M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}), + M1 = id(#{}), + M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + M0 = M2, + + #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, + ok. + +loop_match_and_update_literals_x_q(Map, []) -> Map; +loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> + loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). + + +t_update_map_expressions(Config) when is_list(Config) -> + M = maps:new(), + #{ a := 1 } = M#{a => 1}, + + #{ b := 2 } = (maps:new())#{ b => 2 }, + + #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 }, + #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 }, + + %% Error cases, FIXME: should be 'badmap'? + {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + ok. + + +t_update_assoc(Config) when is_list(Config) -> + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + + M2 = M0#{3.0=>new}, + #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0:=wrong,3.0=>new}, + + %% Errors cases. + BadMap = id(badmap), + {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + + ok. + +t_update_exact(Config) when is_list(Config) -> + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{1:=42,2:=100,4:=[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]}, + + M2 = M0#{3.0:=new}, + #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0=>wrong,3.0:=new}, + %% M2 = M0#{3=>wrong,3.0:=new}, %% FIXME + + %% Errors cases. + {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + + ok. + +t_guard_bifs(Config) when is_list(Config) -> + true = map_guard_head(#{a=>1}), + false = map_guard_head([]), + true = map_guard_body(#{a=>1}), + false = map_guard_body({}), + true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }), + false = map_guard_pattern("list"), + ok. + +map_guard_head(M) when is_map(M) -> true; +map_guard_head(_) -> false. + +map_guard_body(M) -> is_map(M). + +map_guard_pattern(#{}) -> true; +map_guard_pattern(_) -> false. + +t_guard_sequence(Config) when is_list(Config) -> + {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}), + {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}), + {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}), + {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}), + {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}), + + {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})), + {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})), + {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), + {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), + {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), + + %% error case + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), + ok. + +map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}. + +map_guard_sequence_2(#{ a:=3 }=M) -> {1, M}; +map_guard_sequence_2(#{ a:=4 }=M) -> {2, M}; +map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M}; +map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M}; +map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}. + + +t_guard_update(Config) when is_list(Config) -> + error = map_guard_update(#{},#{}), + first = map_guard_update(#{}, #{x=>first}), + second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), + ok. + +map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first; +map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; +map_guard_update(_, _) -> error. + +t_guard_receive(Config) when is_list(Config) -> + M0 = #{ id => 0 }, + Pid = spawn_link(fun() -> guard_receive_loop() end), + Big = 36893488147419103229, + B1 = <<"some text">>, + B2 = <<"was appended">>, + B3 = <<B1/binary, B2/binary>>, + + #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), + #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), + #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), + #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), + #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), + #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), + #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), + + + %% update old maps and check id update + #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), + #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}), + + %% cleanup + done = call(Pid, done), + ok. + +call(Pid, M) -> + Pid ! {self(), M}, receive {Pid, Res} -> Res end. + +guard_receive_loop() -> + receive + {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) -> + Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}}, + guard_receive_loop(); + {Pid, #{ id:=Id, op:=add, in:={X,Y}}} -> + Pid ! {self(), #{ id=>Id+1, res=>X+Y}}, + guard_receive_loop(); + {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X-Y}}, + guard_receive_loop(); + {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X div Y}}, + guard_receive_loop(); + {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, + guard_receive_loop(); + {Pid, done} -> + Pid ! {self(), done}; + {Pid, Other} -> + Pid ! {error, Other}, + guard_receive_loop() + end. + + +t_list_comprehension(Config) when is_list(Config) -> + [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], + ok. + +t_guard_fun(Config) when is_list(Config) -> + F1 = fun + (#{s:=v,v:=V}) -> {v,V}; + (#{s:=t,v:={V,V}}) -> {t,V}; + (#{s:=l,v:=[V,V]}) -> {l,V} + end, + + F2 = fun + (#{s:=T,v:={V,V}}) -> {T,V}; + (#{s:=T,v:=[V,V]}) -> {T,V}; + (#{s:=T,v:=V}) -> {T,V} + end, + V = <<"hi">>, + + {v,V} = F1(#{s=>v,v=>V}), + {t,V} = F1(#{s=>t,v=>{V,V}}), + {l,V} = F1(#{s=>l,v=>[V,V]}), + + {v,V} = F2(#{s=>v,v=>V}), + {t,V} = F2(#{s=>t,v=>{V,V}}), + {l,V} = F2(#{s=>l,v=>[V,V]}), + + %% error case + {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), + ok. + + +t_map_sort_literals(Config) when is_list(Config) -> + % test relation + + %% size order + true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}), + true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}), + false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}), + + %% key order + true = #{ a => 1 } < id(#{ b => 1}), + false = #{ b => 1 } < id(#{ a => 1}), + true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}), + true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}), + true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}), + true = #{ "a" => 1 } < id(#{ <<"a">> => 1}), + false = #{ <<"a">> => 1 } < id(#{ "a" => 1}), + false = #{ 1 => 1 } < id(#{ 1.0 => 1}), + false = #{ 1.0 => 1 } < id(#{ 1 => 1}), + + %% value order + true = #{ a => 1 } < id(#{ a => 2}), + false = #{ a => 2 } < id(#{ a => 1}), + false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}), + true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}), + + true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}), + + %% lists:sort + + SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}], + [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), + [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs), + [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)), + + ok. + +%% BIFs +t_bif_map_get(Config) when is_list(Config) -> + + 1 = maps:get(a, #{ a=> 1}), + 2 = maps:get(b, #{ a=> 1, b => 2}), + "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}), + "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})), + ok. + +t_bif_map_find(Config) when is_list(Config) -> + + {ok, 1} = maps:find(a, #{ a=> 1}), + {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = maps:find(1, #{ 1 => "int"}), + {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), + + {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + error = maps:find(a,#{}), + error = maps:find(a,#{b=>1, c=>2}), + error = maps:find(1.0, #{ 1 => "int"}), + error = maps:find(1, #{ 1.0 => "float"}), + error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + + + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))), + ok. + + +t_bif_map_is_key(Config) when is_list(Config) -> + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + + true = maps:is_key("hi", M1), + true = maps:is_key(int, M1), + true = maps:is_key(<<"key">>, M1), + true = maps:is_key(4, M1), + + false = maps:is_key(5, M1), + false = maps:is_key(<<"key2">>, M1), + false = maps:is_key("h", M1), + false = maps:is_key("hello", M1), + false = maps:is_key(atom, M1), + false = maps:is_key(any, id(#{})), + + false = maps:is_key("hi", maps:remove("hi", M1)), + true = maps:is_key("hi", M1), + true = maps:is_key(1, maps:put(1, "number", M1)), + false = maps:is_key(1.0, maps:put(1, "number", M1)), + + %% error case + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))), + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))), + ok. + +t_bif_map_keys(Config) when is_list(Config) -> + [] = maps:keys(#{}), + + [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + + % values in key order: [4,int,"hi",<<"key">>] + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + [4,int,"hi",<<"key">>] = maps:keys(M1), + + %% error case + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), + ok. + +t_bif_map_new(Config) when is_list(Config) -> + #{} = maps:new(), + 0 = erlang:map_size(maps:new()), + ok. + +t_bif_map_merge(Config) when is_list(Config) -> + 0 = erlang:map_size(maps:merge(#{},#{})), + + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}), + + M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer }, + + #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3, + {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0), + + #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3, + {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1), + + %% error case + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), + + ok. + + +t_bif_map_put(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}), + + ["hi"] = maps:keys(M1), + ["hello"] = maps:values(M1), + + M2 = #{ int := 3 } = maps:put(int, 3, M1), + + [int,"hi"] = maps:keys(M2), + [3,"hello"] = maps:values(M2), + + M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2), + + [int,"hi",<<"key">>] = maps:keys(M3), + [3,"hello",<<"value">>] = maps:values(M3), + + M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3), + + [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4), + [wat,3,"hello",<<"value">>] = maps:values(M4), + + M0 = #{ 4 := number } = M5 = maps:put(4, number, M4), + + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), + [number,wat,3,"hello",<<"value">>] = maps:values(M5), + + M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5), + + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6), + [number,wat,3,"hello",<<"other value">>] = maps:values(M6), + + %% error case + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)), + ok. + +t_bif_map_remove(Config) when is_list(Config) -> + 0 = erlang:map_size(maps:remove(some_key, #{})), + + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = maps:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = maps:keys(M1), + [number,wat,3,<<"value">>] = maps:values(M1), + + M2 = maps:remove(int, M1), + [4,18446744073709551629,<<"key">>] = maps:keys(M2), + [number,wat,<<"value">>] = maps:values(M2), + + M3 = maps:remove(<<"key">>, M2), + [4,18446744073709551629] = maps:keys(M3), + [number,wat] = maps:values(M3), + + M4 = maps:remove(18446744073709551629, M3), + [4] = maps:keys(M4), + [number] = maps:values(M4), + + M5 = maps:remove(4, M4), + [] = maps:keys(M5), + [] = maps:values(M5), + + M0 = maps:remove(5,M0), + M0 = maps:remove("hi there",M0), + + #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), + + %% error case + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), + ok. + +t_bif_map_update(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0), + + #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>, + 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0), + + %% error case + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)), + + ok. + + + +t_bif_map_values(Config) when is_list(Config) -> + + [] = maps:values(#{}), + + [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + + % values in key order: [4,int,"hi",<<"key">>] + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, + [number,3,"hello2",<<"value2">>] = maps:values(M2), + [number,3,"hello",<<"value">>] = maps:values(M1), + + %% error case + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), + ok. + +t_erlang_hash(Config) when is_list(Config) -> + + ok = t_bif_erlang_phash2(), + ok = t_bif_erlang_phash(), + ok = t_bif_erlang_hash(), + + ok. + +t_bif_erlang_phash2() -> + + 39679005 = erlang:phash2(#{}), + 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 14363616 = erlang:phash2(#{ 1 => a }), + 51612236 = erlang:phash2(#{ a => 1 }), + + 37468437 = erlang:phash2(#{{} => <<>>}), + 44049159 = erlang:phash2(#{<<>> => {}}), + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = maps:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 118679416 = erlang:phash2(M0), + 51612236 = erlang:phash2(M1), + 118679416 = erlang:phash2(M2), + ok. + +t_bif_erlang_phash() -> + Sz = 1 bsl 32, + 268440612 = erlang:phash(#{},Sz), + 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 1394238263 = erlang:phash(#{ 1 => a },Sz), + 4066388227 = erlang:phash(#{ a => 1 },Sz), + + 1578050717 = erlang:phash(#{{} => <<>>},Sz), + 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = maps:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 3590546636 = erlang:phash(M0,Sz), + 4066388227 = erlang:phash(M1,Sz), + 3590546636 = erlang:phash(M2,Sz), + ok. + +t_bif_erlang_hash() -> + Sz = 1 bsl 27 - 1, + 5158 = erlang:hash(#{},Sz), + 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 126071654 = erlang:hash(#{ 1 => a },Sz), + 126426236 = erlang:hash(#{ a => 1 },Sz), + + 101655720 = erlang:hash(#{{} => <<>>},Sz), + 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = maps:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 38260486 = erlang:hash(M0,Sz), + 126426236 = erlang:hash(M1,Sz), + 38260486 = erlang:hash(M2,Sz), + ok. + + +t_map_encode_decode(Config) when is_list(Config) -> + <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), + Pairs = [ + {a,b},{"key","values"},{<<"key">>,<<"value">>}, + {1,b},{[atom,1],{<<"wat">>,1,2,3}}, + {aa,"values"}, + {1 bsl 64 + (1 bsl 50 - 1), sc1}, + {99, sc2}, + {1 bsl 65 + (1 bsl 51 - 1), sc3}, + {88, sc4}, + {1 bsl 66 + (1 bsl 52 - 1), sc5}, + {77, sc6}, + {1 bsl 67 + (1 bsl 53 - 1), sc3}, + {75, sc6}, {-10,sc8}, + {<<>>, sc9}, {3.14158, sc10}, + {[3.14158], sc11}, {more_atoms, sc12}, + {{more_tuples}, sc13}, {self(), sc14}, + {{},{}},{[],[]} + ], + ok = map_encode_decode_and_match(Pairs,[],#{}), + + %% check sorting + + %% literally #{ b=>2, a=>1 } in the internal order + #{ a:=1, b:=2 } = + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>), + + + %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order + #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3, + 107,0,2,104,105, % "hi" :: list() + 100,0,1,97, % a :: atom() + 100,0,1,98, % b :: atom() + 107,0,5,118,97,108,117,101, % "value" :: list() + 97,33, % 33 :: integer() + 97,55 % 55 :: integer() + >>), + + + %% error cases + %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> + %% which is: #{ a=>1, b=>1 } + + %% uniqueness violation + %% literally #{ a=>1, "hi"=>"value", a=>2 } + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)), + + %% bad size (too large) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)), + + %% bad size (too small) .. should fail just truncate it .. weird. + %% possibly change external format so truncated will be #{a:=1} + #{ a:=b } = + erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>), + + ok. + +map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> + M1 = maps:put(K,V,M0), + B0 = erlang:term_to_binary(M1), + Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + %% sort Ks and Vs according to term spec, then match it + ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), + %% decode and match it + M1 = erlang:binary_to_term(B0), + map_encode_decode_and_match(Pairs,Ls,M1); +map_encode_decode_and_match([],_,_) -> ok. + +match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) -> + match_encoded_map(Encoded,Items); +match_encoded_map(_,_,_) -> no_match_size. + +match_encoded_map(<<>>,[]) -> ok; +match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> + Size = erlang:byte_size(Item), + <<EncodedTerm:Size/binary, Bin1/binary>> = Bin, + EncodedTerm = Item, %% Asssert + match_encoded_map(Bin1,Items). + + +t_bif_map_to_list(Config) when is_list(Config) -> + [] = maps:to_list(#{}), + [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}), + + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5, + <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}), + + %% error cases + {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))), + ok. + + +t_bif_map_from_list(Config) when is_list(Config) -> + #{} = maps:from_list([]), + A = maps:from_list([]), + 0 = erlang:map_size(A), + + #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]), + #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]), + #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]), + + #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]), + + #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = + maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + + #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = + maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), + + %% error cases + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), + ok. + +%% Maps module, not BIFs +t_maps_fold(_Config) -> + Vs = lists:seq(1,100), + M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), + + %% fold + 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M), + + ok. + +t_maps_map(_Config) -> + Vs = lists:seq(1,100), + M1 = maps:from_list([{I,I}||I<-Vs]), + M2 = maps:from_list([{I,{token,I}}||I<-Vs]), + + M2 = maps:map(fun(_K,V) -> {token,V} end, M1), + ok. + +t_maps_size(_Config) -> + Vs = lists:seq(1,100), + lists:foldl(fun(I,M) -> + M1 = maps:put(I,I,M), + I = maps:size(M1), + M1 + end, #{}, Vs), + ok. + + +t_maps_without(_Config) -> + Ki = [11,22,33,44,55,66,77,88,99], + M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]), + M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]), + M1 = maps:without([{k,I}||I <- Ki],M0), + ok. + + +%% MISC +t_pdict(_Config) -> + + put(#{ a => b, b => a},#{ c => d}), + put(get(#{ a => b, b => a}),1), + 1 = get(#{ c => d}), + #{ c := d } = get(#{ a => b, b => a}). + +t_ets(_Config) -> + + Tid = ets:new(map_table,[]), + + [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)], + + + [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }), + + %% Test equal + [3,4] = lists:sort( + ets:select(Tid,[{{'$1','$2'}, + [{'or',{'==','$1',#{ 3 => -3 }}, + {'==','$1',#{ 4 => -4 }}}], + ['$2']}])), + %% Test match + [30,50] = lists:sort( + ets:select(Tid, + [{{#{ 30 => -30}, '$1'},[],['$1']}, + {{#{ 50 => -50}, '$1'},[],['$1']}] + )), + + ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}), + + %% Test equal with map of different size + [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), + + %% Test match with map of different size + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), + + %%% Test match with don't care value + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), + + %% Test is_map bif + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), + ets:insert(Tid,{not_a_map,2}), + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), + ets:insert(Tid,{{nope,a,tuple},2}), + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), + + %% Test map_size bif + [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}], + [{map_size,'$1'}]}]), + + true = ets:delete(Tid,#{50 => -50}), + [] = ets:lookup(Tid,#{50 => -50}), + + ets:delete(Tid), + ok. + +t_dets(_Config) -> + ok. + +getmsg(_Tracer) -> + receive V -> V after 100 -> timeout end. + +trace_collector(Msg,Parent) -> + io:format("~p~n",[Msg]), + Parent ! Msg, + Parent. + +%% Use this function to avoid compile-time evaluation of an expression. +id(I) -> I. |