diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/debugger/src/dbg_iload.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/debugger/src/dbg_iload.erl')
-rw-r--r-- | lib/debugger/src/dbg_iload.erl | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl new file mode 100644 index 0000000000..1216338006 --- /dev/null +++ b/lib/debugger/src/dbg_iload.erl @@ -0,0 +1,669 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-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_iload). + +%% External exports +-export([load_mod/4]). + +%% Internal exports +-export([load_mod1/4]). + +%%==================================================================== +%% External exports +%%==================================================================== + +%%-------------------------------------------------------------------- +%% load_mod(Mod, File, Binary, Db) -> {ok, Mod} +%% Mod = atom() +%% File = string() Source file (including path) +%% Binary = binary() +%% Db = ETS identifier +%% Load a new module into the database. +%% +%% We want the loading of a module to be syncronous so no other +%% process tries to interpret code in a module not being completely +%% loaded. This is achieved as this function is called from +%% dbg_iserver. We are suspended until the module has been loaded. +%%-------------------------------------------------------------------- +load_mod(Mod, File, Binary, Db) -> + Flag = process_flag(trap_exit, true), + Pid = spawn_link(?MODULE, load_mod1, [Mod, File, Binary, Db]), + receive + {'EXIT', Pid, What} -> + process_flag(trap_exit, Flag), + What + end. + +%%==================================================================== +%% Internal exports +%%==================================================================== + +load_mod1(Mod, File, Binary, Db) -> + store_module(Mod, File, Binary, Db), + exit({ok, Mod}). + + +%%==================================================================== +%% Internal functions +%%==================================================================== + +store_module(Mod, File, Binary, Db) -> + {interpreter_module, Exp, Abst, Src, MD5} = binary_to_term(Binary), + Forms = case abstr(Abst) of + {abstract_v1,Forms0} -> Forms0; + {abstract_v2,Forms0} -> Forms0; + {raw_abstract_v1,Code0} -> + Code = interpret_file_attribute(Code0), + {_,_,Forms0,_} = sys_pre_expand:module(Code, []), + Forms0 + end, + dbg_idb:insert(Db, mod_file, File), + dbg_idb:insert(Db, exports, Exp), + dbg_idb:insert(Db, defs, []), + + put(vcount, 0), + put(fun_count, 0), + put(funs, []), + put(mod_md5, MD5), + Attr = store_forms(Forms, Mod, Db, Exp, []), + erase(mod_md5), + erase(current_function), +% store_funs(Db, Mod), + erase(vcount), + erase(funs), + erase(fun_count), + + dbg_idb:insert(Db, attributes, Attr), + NewBinary = store_mod_line_no(Mod, Db, binary_to_list(Src)), + dbg_idb:insert(Db, mod_bin, NewBinary), + dbg_idb:insert(Db, mod_raw, <<Src/binary,0:8>>), %% Add eos + dbg_idb:insert(Db, module, Mod). +%% Adjust line numbers using the file/2 attribute. +%% Also take the absolute value of line numbers. +%% This simple fix will make the marker point at the correct line +%% (assuming the file attributes are correct) in the source; it will +%% not point at code in included files. +interpret_file_attribute(Code) -> + epp:interpret_file_attribute(Code). + + +abstr(Bin) when is_binary(Bin) -> binary_to_term(Bin); +abstr(Term) -> Term. + +% store_funs(Db, Mod) -> +% store_funs_1(get(funs), Db, Mod). + +% store_funs_1([{Name,Index,Uniq,_,_,Arity,Cs}|Fs], Db, Mod) -> +% dbg_idb:insert(Db, {Mod,Name,Arity,false}, Cs), +% dbg_idb:insert(Db, {'fun',Mod,Index,Uniq}, {Name,Arity,Cs}), +% store_funs_1(Fs, Db, Mod); +% store_funs_1([], _, _) -> ok. + +store_forms([{function,_,module_info,0,_}|Fs], Mod, Db, Exp, Attr) -> + Cs = [{clause,0,[],[], [{module_info_0,0,Mod}]}], + dbg_idb:insert(Db, {Mod,module_info,0,true}, Cs), + store_forms(Fs, Mod, Db, Exp, Attr); +store_forms([{function,_,module_info,1,_}|Fs], Mod, Db, Exp, Attr) -> + Cs = [{clause,0,[{var,0,'What'}],[], [{module_info_1,0,Mod,[{var,0,'What'}]}]}], + dbg_idb:insert(Db, {Mod,module_info,1,true}, Cs), + store_forms(Fs, Mod, Db, Exp, Attr); +store_forms([{function,_,Name,Arity,Cs0}|Fs], Mod, Db, Exp, Attr) -> + FA = {Name,Arity}, + put(current_function, FA), + Cs = clauses(Cs0), + Exported = lists:member(FA, Exp), + dbg_idb:insert(Db, {Mod,Name,Arity,Exported}, Cs), + store_forms(Fs, Mod, Db, Exp, Attr); +store_forms([{attribute,_,Name,Val}|Fs], Mod, Db, Exp, Attr) -> + store_forms(Fs, Mod, Db, Exp, [{Name,Val}|Attr]); +store_forms([F|_], _Mod, _Db, _Exp, _Attr) -> + exit({unknown_form,F}); +store_forms([], _, _, _, Attr) -> + lists:reverse(Attr). + +store_mod_line_no(Mod, Db, Contents) -> + store_mod_line_no(Mod, Db, Contents, 1, 0, []). + +store_mod_line_no(_, _, [], _, _, NewCont) -> + list_to_binary(lists:reverse(NewCont)); +store_mod_line_no(Mod, Db, Contents, LineNo, Pos, NewCont) when is_integer(LineNo) -> + {ContTail,Pos1,NewCont1} = store_line(Mod, Db, Contents, LineNo, Pos, NewCont), + store_mod_line_no(Mod, Db, ContTail, LineNo+1, Pos1, NewCont1). + +store_line(_, Db, Contents, LineNo, Pos, NewCont) -> + {ContHead,ContTail,PosNL} = get_nl(Contents,Pos+8,[]), + dbg_idb:insert(Db,LineNo,{Pos+8,PosNL}), + {ContTail,PosNL+1,[make_lineno(LineNo, 8, ContHead)|NewCont]}. + +make_lineno(N, P, Acc) -> + S = integer_to_list(N), + S ++ [$:|spaces(P-length(S)-1, Acc)]. + +spaces(P, Acc) when P > 0 -> + spaces(P-1, [$\s|Acc]); +spaces(_, Acc) -> Acc. + +get_nl([10|T],Pos,Head) -> {lists:reverse([10|Head]),T,Pos}; +get_nl([H|T],Pos,Head) -> + get_nl(T,Pos+1,[H|Head]); +get_nl([],Pos,Head) -> {lists:reverse(Head),[],Pos}. + +%%% Rewrite the abstract syntax tree to that it will be easier (== faster) +%%% to interpret. + +clauses([C0|Cs]) -> + C1 = clause(C0), + [C1|clauses(Cs)]; +clauses([]) -> []. + +clause({clause,Line,H0,G0,B0}) -> + H1 = head(H0), + G1 = guard(G0), + B1 = exprs(B0), + {clause,Line,H1,G1,B1}. + +head(Ps) -> patterns(Ps). + +%% These patterns are processed "sequentially" for purposes of variable +%% definition etc. + +patterns([P0|Ps]) -> + P1 = pattern(P0), + [P1|patterns(Ps)]; +patterns([]) -> []. + +%% N.B. Only valid patterns are included here. + +pattern({var,Line,V}) -> {var,Line,V}; +pattern({char,Line,I}) -> {value,Line,I}; +pattern({integer,Line,I}) -> {value,Line,I}; +pattern({match,Line,Pat1,Pat2}) -> + {match,Line,pattern(Pat1),pattern(Pat2)}; +pattern({float,Line,F}) -> {value,Line,F}; +pattern({atom,Line,A}) -> {value,Line,A}; +pattern({string,Line,S}) -> {value,Line,S}; +pattern({nil,Line}) -> {value,Line,[]}; +pattern({cons,Line,H0,T0}) -> + H1 = pattern(H0), + T1 = pattern(T0), + {cons,Line,H1,T1}; +pattern({tuple,Line,Ps0}) -> + Ps1 = pattern_list(Ps0), + {tuple,Line,Ps1}; +pattern({op,_,'-',{integer,Line,I}}) -> + {value,Line,-I}; +pattern({op,_,'+',{integer,Line,I}}) -> + {value,Line,I}; +pattern({op,_,'-',{char,Line,I}}) -> + {value,Line,-I}; +pattern({op,_,'+',{char,Line,I}}) -> + {value,Line,I}; +pattern({op,_,'-',{float,Line,I}}) -> + {value,Line,-I}; +pattern({op,_,'+',{float,Line,I}}) -> + {value,Line,I}; +pattern({bin,Line,Grp}) -> + Grp1 = pattern_list(Grp), + {bin,Line,Grp1}; +pattern({bin_element,Line,Expr,Size,Type}) -> + Expr1 = pattern(Expr), + Size1 = expr(Size), + {bin_element,Line,Expr1,Size1,Type}. + +%% These patterns are processed "in parallel" for purposes of variable +%% definition etc. + +pattern_list([P0|Ps]) -> + P1 = pattern(P0), + [P1|pattern_list(Ps)]; +pattern_list([]) -> []. + +guard([G0|Gs]) -> + G1 = and_guard(G0), + [G1|guard(Gs)]; +guard([]) -> []. + +and_guard([{atom,_,true}|Gs]) -> + and_guard(Gs); +and_guard([G0|Gs]) -> + G1 = guard_test(G0), + [G1|and_guard(Gs)]; +and_guard([]) -> []. + +guard_test({call,Line,{remote,_,{atom,_,erlang},{atom,_,F}},As0}) -> + As = gexpr_list(As0), + case map_guard_bif(F, length(As0)) of + {ok,Name} -> + {safe_bif,Line,erlang,Name,As}; + error -> + {safe_bif,Line,erlang,F,As} + end; +guard_test({op,Line,Op,L0}) -> + true = erl_internal:arith_op(Op, 1) orelse %Assertion. + erl_internal:bool_op(Op, 1), + L1 = gexpr(L0), + {safe_bif,Line,erlang,Op,[L1]}; +guard_test({op,Line,Op,L0,R0}) when Op =:= 'andalso'; Op =:= 'orelse' -> + L1 = gexpr(L0), + R1 = gexpr(R0), %They see the same variables + {Op,Line,L1,R1}; +guard_test({op,Line,Op,L0,R0}) -> + true = erl_internal:comp_op(Op, 2) orelse %Assertion. + erl_internal:bool_op(Op, 2) orelse + erl_internal:arith_op(Op, 2), + L1 = gexpr(L0), + R1 = gexpr(R0), %They see the same variables + {safe_bif,Line,erlang,Op,[L1,R1]}; +guard_test({integer,_,_}=I) -> I; +guard_test({char,_,_}=C) -> C; +guard_test({float,_,_}=F) -> F; +guard_test({atom,_,_}=A) -> A; +guard_test({nil,_}=N) -> N; +guard_test({var,_,_}=V) ->V. % Boolean var + +map_guard_bif(integer, 1) -> {ok,is_integer}; +map_guard_bif(float, 1) -> {ok,is_float}; +map_guard_bif(number, 1) -> {ok,is_number}; +map_guard_bif(atom, 1) -> {ok,is_atom}; +map_guard_bif(list, 1) -> {ok,is_list}; +map_guard_bif(tuple, 1) -> {ok,is_tuple}; +map_guard_bif(pid, 1) -> {ok,is_pid}; +map_guard_bif(reference, 1) -> {ok,is_reference}; +map_guard_bif(port, 1) -> {ok,is_port}; +map_guard_bif(binary, 1) -> {ok,is_binary}; +map_guard_bif(function, 1) -> {ok,is_function}; +map_guard_bif(_, _) -> error. + +gexpr({var,Line,V}) -> {var,Line,V}; +gexpr({integer,Line,I}) -> {value,Line,I}; +gexpr({char,Line,I}) -> {value,Line,I}; +gexpr({float,Line,F}) -> {value,Line,F}; +gexpr({atom,Line,A}) -> {value,Line,A}; +gexpr({string,Line,S}) -> {value,Line,S}; +gexpr({nil,Line}) -> {value,Line,[]}; +gexpr({cons,Line,H0,T0}) -> + case {gexpr(H0),gexpr(T0)} of + {{value,Line,H1},{value,Line,T1}} -> {value,Line,[H1|T1]}; + {H1,T1} -> {cons,Line,H1,T1} + end; +gexpr({tuple,Line,Es0}) -> + Es1 = gexpr_list(Es0), + {tuple,Line,Es1}; +gexpr({bin,Line,Flds0}) -> + Flds = gexpr_list(Flds0), + {bin,Line,Flds}; +gexpr({bin_element,Line,Expr0,Size0,Type}) -> + Expr = gexpr(Expr0), + Size = gexpr(Size0), + {bin_element,Line,Expr,Size,Type}; +%%% The previous passes have added the module name 'erlang' to +%%% all BIF calls, even in guards. +gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}) -> + {dbg, Line, self, []}; +gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,F}},As0}) -> + As = gexpr_list(As0), + {safe_bif,Line,erlang,F,As}; +gexpr({op,Line,Op,A0}) -> + erl_internal:arith_op(Op, 1), + A1 = gexpr(A0), + {safe_bif,Line,erlang,Op,[A1]}; +gexpr({op,Line,Op,L0,R0}) when Op =:= 'andalso'; Op =:= 'orelse' -> + L1 = gexpr(L0), + R1 = gexpr(R0), %They see the same variables + {Op,Line,L1,R1}; +gexpr({op,Line,Op,L0,R0}) -> + true = erl_internal:arith_op(Op, 2) orelse erl_internal:comp_op(Op, 2) + orelse erl_internal:bool_op(Op, 2), + L1 = gexpr(L0), + R1 = gexpr(R0), %They see the same variables + {safe_bif,Line,erlang,Op,[L1,R1]}. + +%% These expressions are processed "in parallel" for purposes of variable +%% definition etc. + +gexpr_list([E0|Es]) -> + E1 = gexpr(E0), + [E1|gexpr_list(Es)]; +gexpr_list([]) -> []. + +%% These expressions are processed "sequentially" for purposes of variable +%% definition etc. + +exprs([E0|Es]) -> + E1 = expr(E0), + [E1|exprs(Es)]; +exprs([]) -> []. + +expr({var,Line,V}) -> {var,Line,V}; +expr({integer,Line,I}) -> {value,Line,I}; +expr({char,Line,I}) -> {value,Line,I}; +expr({float,Line,F}) -> {value,Line,F}; +expr({atom,Line,A}) -> {value,Line,A}; +expr({string,Line,S}) -> {value,Line,S}; +expr({nil,Line}) -> {value,Line,[]}; +expr({cons,Line,H0,T0}) -> + case {expr(H0),expr(T0)} of + {{value,Line,H1},{value,Line,T1}} -> {value,Line,[H1|T1]}; + {H1,T1} -> {cons,Line,H1,T1} + end; +expr({tuple,Line,Es0}) -> + Es1 = expr_list(Es0), + {tuple,Line,Es1}; +expr({block,Line,Es0}) -> + %% Unfold block into a sequence. + Es1 = exprs(Es0), + {block,Line,Es1}; +expr({'if',Line,Cs0}) -> + Cs1 = icr_clauses(Cs0), + {'if',Line,Cs1}; +expr({'case',Line,E0,Cs0}) -> + E1 = expr(E0), + Cs1 = icr_clauses(Cs0), + {'case',Line,E1,Cs1}; +expr({'receive',Line,Cs0}) -> + Cs1 = icr_clauses(Cs0), + {'receive',Line,Cs1}; +expr({'receive',Line,Cs0,To0,ToEs0}) -> + To1 = expr(To0), + ToEs1 = exprs(ToEs0), + Cs1 = icr_clauses(Cs0), + {'receive',Line,Cs1,To1,ToEs1}; +expr({'fun',Line,{clauses,Cs0},{_,_,Name}}) when is_atom(Name) -> + %% New R10B-2 format (abstract_v2). + Cs = fun_clauses(Cs0), + {make_fun,Line,Name,Cs}; +expr({'fun',Line,{clauses,Cs0},{_,_,_,_,Name}}) when is_atom(Name) -> + %% New R8 format (abstract_v2). + Cs = fun_clauses(Cs0), + {make_fun,Line,Name,Cs}; +expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}) -> + %% New R8 format (abstract_v2). + As = new_vars(A, Line), + Cs = [{clause,Line,As,[],[{local_call,Line,F,As}]}], + {make_fun,Line,Name,Cs}; +expr({'fun',_,{clauses,_},{_OldUniq,_Hvss,_Free}}) -> + %% Old format (abstract_v1). + exit({?MODULE,old_funs}); +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}) -> + {dbg,Line,self,[]}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}) -> + {dbg,Line,get_stacktrace,[]}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,throw}},[_]=As}) -> + {dbg,Line,throw,expr_list(As)}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,error}},[_]=As}) -> + {dbg,Line,error,expr_list(As)}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,fault}},[_]=As}) -> + {dbg,Line,fault,expr_list(As)}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) -> + {dbg,Line,exit,expr_list(As)}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},As0}) + when length(As0) == 3 -> + As = expr_list(As0), + {apply,Line,As}; +expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) -> + As = expr_list(As0), + case erlang:is_builtin(Mod, Func, length(As)) of + false -> + {call_remote,Line,Mod,Func,As}; + true -> + case bif_type(Mod, Func) of + safe -> {safe_bif,Line,Mod,Func,As}; + spawn -> {spawn_bif,Line,Mod,Func,As}; + unsafe ->{bif,Line,Mod,Func,As} + end + end; +expr({call,Line,{remote,_,Mod0,Func0},As0}) -> + %% New R8 format (abstract_v2). + Mod = expr(Mod0), + Func = expr(Func0), + As = consify(expr_list(As0)), + {apply,Line,[Mod,Func,As]}; +expr({call,Line,{atom,_,Func},As0}) -> + As = expr_list(As0), + {local_call,Line,Func,As}; +expr({call,Line,Fun0,As0}) -> + Fun = expr(Fun0), + As = expr_list(As0), + {apply_fun,Line,Fun,As}; +expr({'catch',Line,E0}) -> + %% No new variables added. + E1 = expr(E0), + {'catch',Line,E1}; +expr({'try',Line,Es0,CaseCs0,CatchCs0,As0}) -> + %% No new variables added. + Es = expr_list(Es0), + CaseCs = icr_clauses(CaseCs0), + CatchCs = icr_clauses(CatchCs0), + As = expr_list(As0), + {'try',Line,Es,CaseCs,CatchCs,As}; +expr({'query', Line, E0}) -> + E = expr(E0), + {'query', Line, E}; +expr({lc,Line,E0,Gs0}) -> %R8. + Gs = lists:map(fun ({generate,L,P0,Qs}) -> + {generate,L,expr(P0),expr(Qs)}; + ({b_generate,L,P0,Qs}) -> %R12. + {b_generate,L,expr(P0),expr(Qs)}; + (Expr) -> + case is_guard_test(Expr) of + true -> {guard,[[guard_test(Expr)]]}; + false -> expr(Expr) + end + end, Gs0), + {lc,Line,expr(E0),Gs}; +expr({bc,Line,E0,Gs0}) -> %R12. + Gs = lists:map(fun ({generate,L,P0,Qs}) -> + {generate,L,expr(P0),expr(Qs)}; + ({b_generate,L,P0,Qs}) -> %R12. + {b_generate,L,expr(P0),expr(Qs)}; + (Expr) -> + case is_guard_test(Expr) of + true -> {guard,[[guard_test(Expr)]]}; + false -> expr(Expr) + end + end, Gs0), + {bc,Line,expr(E0),Gs}; +expr({match,Line,P0,E0}) -> + E1 = expr(E0), + P1 = pattern(P0), + {match,Line,P1,E1}; +expr({op,Line,Op,A0}) -> + A1 = expr(A0), + {op,Line,Op,[A1]}; +expr({op,Line,'++',L0,R0}) -> + L1 = expr(L0), + R1 = expr(R0), %They see the same variables + {op,Line,append,[L1,R1]}; +expr({op,Line,'--',L0,R0}) -> + L1 = expr(L0), + R1 = expr(R0), %They see the same variables + {op,Line,subtract,[L1,R1]}; +expr({op,Line,'!',L0,R0}) -> + L1 = expr(L0), + R1 = expr(R0), %They see the same variables + {send,Line,L1,R1}; +expr({op,Line,Op,L0,R0}) when Op =:= 'andalso'; Op =:= 'orelse' -> + L1 = expr(L0), + R1 = expr(R0), %They see the same variables + {Op,Line,L1,R1}; +expr({op,Line,Op,L0,R0}) -> + L1 = expr(L0), + R1 = expr(R0), %They see the same variables + {op,Line,Op,[L1,R1]}; +expr({bin,Line,Grp}) -> + Grp1 = expr_list(Grp), + {bin,Line,Grp1}; +expr({bin_element,Line,Expr,Size,Type}) -> + Expr1 = expr(Expr), + Size1 = expr(Size), + {bin_element,Line,Expr1,Size1,Type}; +expr(Other) -> + exit({?MODULE,{unknown_expr,Other}}). + +%% is_guard_test(Expression) -> true | false. +%% Test if a general expression is a guard test. Cannot use erl_lint +%% here as sys_pre_expand has transformed source. + +is_guard_test({op,_,Op,L,R}) -> + case erl_internal:comp_op(Op, 2) of + true -> is_gexpr_list([L,R]); + false -> false + end; +is_guard_test({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) -> + case erl_internal:type_test(Test, length(As)) of + true -> is_gexpr_list(As); + false -> false + end; +is_guard_test({atom,_,true}) -> true; +is_guard_test(_) -> false. + +is_gexpr({var,_,_}) -> true; +is_gexpr({atom,_,_}) -> true; +is_gexpr({integer,_,_}) -> true; +is_gexpr({char,_,_}) -> true; +is_gexpr({float,_,_}) -> true; +is_gexpr({string,_,_}) -> true; +is_gexpr({nil,_}) -> true; +is_gexpr({cons,_,H,T}) -> is_gexpr_list([H,T]); +is_gexpr({tuple,_,Es}) -> is_gexpr_list(Es); +is_gexpr({call,_,{remote,_,{atom,_,erlang},{atom,_,F}},As}) -> + Ar = length(As), + case erl_internal:guard_bif(F, Ar) of + true -> is_gexpr_list(As); + false -> + case erl_internal:arith_op(F, Ar) of + true -> is_gexpr_list(As); + false -> false + end + end; +is_gexpr({op,_,Op,A}) -> + case erl_internal:arith_op(Op, 1) of + true -> is_gexpr(A); + false -> false + end; +is_gexpr({op,_,Op,A1,A2}) -> + case erl_internal:arith_op(Op, 2) of + true -> is_gexpr_list([A1,A2]); + false -> false + end; +is_gexpr(_) -> false. + +is_gexpr_list(Es) -> lists:all(fun (E) -> is_gexpr(E) end, Es). + +consify([A|As]) -> + {cons,0,A,consify(As)}; +consify([]) -> {value,0,[]}. + + +%% -type expr_list([Expression]) -> [Expression]. +%% These expressions are processed "in parallel" for purposes of variable +%% definition etc. + +expr_list([E0|Es]) -> + E1 = expr(E0), + [E1|expr_list(Es)]; +expr_list([]) -> []. + +icr_clauses([C0|Cs]) -> + C1 = clause(C0), + [C1|icr_clauses(Cs)]; +icr_clauses([]) -> []. + +fun_clauses([{clause,L,H,G,B}|Cs]) -> + [{clause,L,head(H),guard(G),exprs(B)}|fun_clauses(Cs)]; +fun_clauses([]) -> []. + +%% new_var_name() -> VarName. + +new_var_name() -> + C = get(vcount), + put(vcount, C+1), + list_to_atom("%" ++ integer_to_list(C)). + +%% new_vars(Count, Line) -> [Var]. +%% Make Count new variables. + +new_vars(N, L) -> new_vars(N, L, []). + +new_vars(N, L, Vs) when N > 0 -> + V = {var,L,new_var_name()}, + new_vars(N-1, L, [V|Vs]); +new_vars(0, _, Vs) -> Vs. + +bif_type(erlang, Name) -> bif_type(Name); +bif_type(_, _) -> unsafe. + +bif_type(register) -> safe; +bif_type(unregister) -> safe; +bif_type(whereis) -> safe; +bif_type(registered) -> safe; +bif_type(abs) -> safe; +bif_type(float) -> safe; +bif_type(trunc) -> safe; +bif_type(round) -> safe; +bif_type(math) -> safe; +bif_type(node) -> safe; +bif_type(length) -> safe; +bif_type(hd) -> safe; +bif_type(tl) -> safe; +bif_type(size) -> safe; +bif_type(element) -> safe; +bif_type(setelement) -> safe; +bif_type(atom_to_list) -> safe; +bif_type(list_to_atom) -> safe; +bif_type(integer_to_list) -> safe; +bif_type(list_to_integer) -> safe; +bif_type(float_to_list) -> safe; +bif_type(list_to_float) -> safe; +bif_type(tuple_to_list) -> safe; +bif_type(list_to_tuple) -> safe; +bif_type(make_ref) -> safe; +bif_type(time) -> safe; +bif_type(date) -> safe; +bif_type(processes) -> safe; +bif_type(process_info) -> safe; +bif_type(load_module) -> safe; +bif_type(delete_module) -> safe; +bif_type(halt) -> safe; +bif_type(check_process_code) -> safe; +bif_type(purge_module) -> safe; +bif_type(pid_to_list) -> safe; +bif_type(list_to_pid) -> safe; +bif_type(module_loaded) -> safe; +bif_type(binary_to_term) -> safe; +bif_type(term_to_binary) -> safe; +bif_type(alive) -> safe; +bif_type(notalive) -> safe; +bif_type(nodes) -> safe; +bif_type(is_alive) -> safe; +bif_type(disconnect_node) -> safe; +bif_type(binary_to_list) -> safe; +bif_type(list_to_binary) -> safe; +bif_type(split_binary) -> safe; +bif_type(concat_binary) -> safe; +bif_type(term_to_atom) -> safe; +bif_type(hash) -> safe; +bif_type(pre_loaded) -> safe; +bif_type(info) -> safe; +bif_type(set_cookie) -> safe; +bif_type(get_cookie) -> safe; +bif_type(spawn) -> spawn; +bif_type(spawn_link) -> spawn; +bif_type(spawn_opt) -> spawn; +bif_type(_) -> unsafe. |