cerl_messagean.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,100 行 · 第 1/3 页

ERL
1,100
字号
%% =====================================================================%% Message analysis of Core Erlang programs.%%%% Copyright (C) 2002 Richard Carlsson%%%% This library is free software; you can redistribute it and/or modify%% it under the terms of the GNU Lesser General Public License as%% published by the Free Software Foundation; either version 2 of the%% License, or (at your option) any later version.%%%% This library is distributed in the hope that it will be useful, but%% WITHOUT ANY WARRANTY; without even the implied warranty of%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU%% Lesser General Public License for more details.%%%% You should have received a copy of the GNU Lesser General Public%% License along with this library; if not, write to the Free Software%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307%% USA%%%% Author contact: richardc@it.uu.se%%%% $Id$%%%% =====================================================================%% TODO: might need a "top" (`any') element for any-length value lists.-module(cerl_messagean).-export([annotate/1]).-import(cerl, [alias_pat/1, alias_var/1, ann_c_var/2, ann_c_fun/3,	       apply_args/1, apply_op/1, atom_val/1, bitstr_size/1,	       bitstr_val/1, binary_segments/1, c_letrec/2,	       ann_c_tuple/2, c_nil/0, call_args/1, call_module/1,	       call_name/1, case_arg/1, case_clauses/1, catch_body/1,	       clause_body/1, clause_guard/1, clause_pats/1, cons_hd/1,	       cons_tl/1, fun_body/1, fun_vars/1, get_ann/1, int_val/1,	       is_c_atom/1, is_c_int/1, let_arg/1, let_body/1,	       let_vars/1, letrec_body/1, letrec_defs/1, module_defs/1,	       module_defs/1, module_exports/1, pat_vars/1,	       primop_args/1, primop_name/1, receive_action/1,	       receive_clauses/1, receive_timeout/1, seq_arg/1,	       seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,	       try_evars/1, try_handler/1, tuple_es/1, type/1,	       values_es/1]).-import(cerl_trees, [get_label/1]).-define(DEF_LIMIT, 4).%-export([test/1, test1/1, ttest/1]).% ttest(F) ->%     {T, _} = cerl_trees:label(user_default:read(F)),%     {Time0, _} = erlang:statistics(runtime),%     analyze(T),%     {Time1, _} = erlang:statistics(runtime),%     Time1 - Time0.% test(F) ->%     {T, _} = cerl_trees:label(user_default:read(F)),%     {Time0, _} = erlang:statistics(runtime),%     {Esc, _Vars} = analyze(T),%     {Time1, _} = erlang:statistics(runtime),%     io:fwrite("messages: ~p.\n", [Esc]),%     Set = sets:from_list(Esc),%     H = fun (Node, Ctxt, Cont) ->% 		Doc = case get_ann(Node) of% 			  [{label, L} | _] ->% 			      B = sets:is_element(L, Set),% 			      bf(Node, Ctxt, Cont, B);% 			  _ ->% 			      bf(Node, Ctxt, Cont, false)% 		      end,% 		case type(Node) of% 		    cons -> color(Doc);% 		    tuple -> color(Doc);% 		    _ -> Doc% 		end% 	end,%     {ok, FD} = file:open("out.html",[write]),%     Txt = cerl_prettypr:format(T, [{hook, H},{user,false}]),%     io:put_chars(FD, "<pre>\n"),%     io:put_chars(FD, html(Txt)),%     io:put_chars(FD, "</pre>\n"),%     file:close(FD),%     {ok, Time1 - Time0}.% test1(F) ->%     {T, _} = cerl_trees:label(user_default:read(F)),%     {Time0, _} = erlang:statistics(runtime),%     {T1, Esc, Vars} = annotate(T),%     {Time1, _} = erlang:statistics(runtime),%     io:fwrite("messages: ~p.\n", [Esc]),% %%%     io:fwrite("vars: ~p.\n", [[X || X <- dict:to_list(Vars)]]),%     T2 = hhl_transform:transform(T1, Vars),%     Set = sets:from_list(Esc),%     H = fun (Node, Ctxt, Cont) ->% 		case get_ann(Node) of% 		    [{label, L} | _] ->% 			B = sets:is_element(L, Set),% 			bf(Node, Ctxt, Cont, B);% 		    _ ->% 			bf(Node, Ctxt, Cont, false)% 		end% 	end,%     {ok, FD} = file:open("out.html",[write]),%     Txt = cerl_prettypr:format(T2, [{hook, H},{user,false}]),%     io:put_chars(FD, "<pre>\n"),%     io:put_chars(FD, html(Txt)),%     io:put_chars(FD, "</pre>\n"),%     file:close(FD),%     {ok, Time1 - Time0}.% html(Cs) ->%     html(Cs, []).% html([$#, $< | Cs], As) ->%     html_1(Cs, [$< | As]);% html([$< | Cs], As) ->%     html(Cs, ";tl&" ++ As);% html([$> | Cs], As) ->%     html(Cs, ";tg&" ++ As);% html([$& | Cs], As) ->%     html(Cs, ";pma&" ++ As);% html([C | Cs], As) ->%     html(Cs, [C | As]);% html([], As) ->%     lists:reverse(As).% html_1([$> | Cs], As) ->%     html(Cs, [$> | As]);% html_1([C | Cs], As) ->%     html_1(Cs, [C | As]).% bf(Node, Ctxt, Cont, B) ->%     B0 = cerl_prettypr:get_ctxt_user(Ctxt),%     if B /= B0 ->% 	    Ctxt1 = cerl_prettypr:set_ctxt_user(Ctxt, B),% 	    Doc = Cont(Node, Ctxt1),% 	    case B of% 		true ->% 		    Start = "<b>",% 		    End = "</b>";% 		false ->% 		    Start = "</b>",% 		    End = "<b>"% 	    end,% 	    markup(Doc, Start, End);%        true ->% 	    Cont(Node, Ctxt)%     end.% color(Doc) ->% %    Doc.%     markup(Doc, "<font color=blue>", "</font>").% markup(Doc, Start, End) ->%     prettypr:beside(%       prettypr:null_text([$# | Start]),%       prettypr:beside(Doc,% 		      prettypr:null_text([$# | End]))).%% =====================================================================%% annotate(Tree) -> {Tree1, Escapes}%%%%	    Tree = coreErlang()%%%%	Analyzes `Tree' (see `analyze') and appends a term 'escapes', to%%	the annotation list of each constructor expression node and of%%	`Tree', corresponding to the escape information derived by the%%	analysis. Any previous such annotations are removed from `Tree'.%%	`Tree1' is the modified tree; for details on `OutList',%%	`Outputs' , `Dependencies', `Escapes' and `Parents', see%%	`analyze'.%%%%	Note: `Tree' must be annotated with labels in order to use this%%	function; see `analyze' for details.annotate(T) ->    {Esc0, Vars} = analyze(T),    Esc = sets:from_list(Esc0),    F = fun (T) ->		case type(T) of		    literal -> T;%%% 		    var ->%%% 			L = get_label(T),%%% 			T1 = ann_escape(T, L, Esc),%%% 			X = dict:fetch(L, Vars),%%% 			set_ann(T1, append_ann({s,X}, get_ann(T1)));		    _ ->			L = get_label(T),			ann_escape(T, L, Esc)		end	end,    {cerl_trees:map(F, T), Esc0, Vars}.ann_escape(T, L, Esc) ->    case sets:is_element(L, Esc) of	true ->	    set_ann(T, append_ann(escapes, get_ann(T)));	false ->	    T    end.append_ann(Tag, [X | Xs]) ->    if is_tuple(X), size(X) >= 1, element(1, X) =:= Tag -> 	    append_ann(Tag, Xs);       true ->	    [X | append_ann(Tag, Xs)]    end;append_ann(Tag, []) ->    [Tag].%% =====================================================================%% analyze(Tree) -> Escapes%%%%	    Tree = coreErlang()%%	    Escapes = ordset(Label)%%	    Label = integer() | external | top%%%%	Analyzes a module or an expression represented by `Tree'.%%%%	`Escapes' is the set of labels of constructor expressions in%%	`Tree' such that the created values may be accessed from outside%%	`Tree'.%%%%	Note: `Tree' must be annotated with labels (as done by the%%	function `cerl_trees:label/1') in order to use this function.%%	The label annotation `{label, L}' (where L should be an integer)%%	must be the first element of the annotation list of each node in%%	the tree. Instances of variables bound in `Tree' which denote%%	the same variable must have the same label; apart from this,%%	labels should be unique. Constant literals do not need to be%%	labeled.-record(state, {vars, out, dep, work, funs, k}).%% Note: We assume that all remote calls and primops return a single%% value.%% The analysis determines which objects (identified by the%% corresponding "cons-point" labels in the code) are likely to be%% passed in a message. (If so, we say that they "escape".) It is always%% safe to assume either case, because the send operation will assure%% that things are copied if necessary. This analysis tries to%% anticipate that copying will be done.%%%% Rules:%%   1) An object passed as message argument (or part of such an%%   argument) to a known send-operation, will probably be a message.%%   2) A received value is always a message (safe).%%   3) The external function can return any object (unsafe).%%   4) A function called from the external function can receive any%%   object (unsafe) as argument.%%   5) Unknown functions/operations can return any object (unsafe).%% We wrap the given syntax tree T in a fun-expression labeled `top',%% which is initially in the set of escaped labels. `top' will be%% visited at least once.%%%% We create a separate function labeled `external', defined as:%% "'external'/1 = fun () -> Any", which will represent any and all%% functions outside T, and which returns the 'unsafe' value.analyze(T) ->    analyze(T, ?DEF_LIMIT).analyze(T, Limit) ->    {_, _, Esc, Dep, _Par} = cerl_closurean:analyze(T),%%%     io:fwrite("dependencies: ~w.\n", [dict:to_list(Dep)]),    analyze(T, Limit, Dep, Esc).analyze(T, Limit, Dep0, Esc0) ->    %% Note that we use different name spaces for variable labels and    %% function/call site labels, so we can reuse some names here. We    %% assume that the labeling of T only uses integers, not atoms.    Any = ann_c_var([{label, any}], 'Any'),    External = ann_c_var([{label, external}], {external, 1}),    ExtFun = ann_c_fun([{label, external}], [], Any),%%%     io:fwrite("external fun:\n~s.\n",%%%   	      [cerl_prettypr:format(ExtFun, [noann, {paper, 80}])]),    Top = ann_c_var([{label, top}], {top, 0}),    TopFun = ann_c_fun([{label, top}], [], T),    %% The "start fun" just makes the initialisation easier. It is not    %% itself in the call graph.    StartFun =  ann_c_fun([{label, start}], [],			  c_letrec([{External, ExtFun}, {Top, TopFun}],				   c_nil())),%%%     io:fwrite("start fun:\n~s.\n",%%%  	      [cerl_prettypr:format(StartFun, [{paper, 80}])]),    %% Initialise the Any and Escape variables. Gather a database of all    %% fun-expressions in T and initialise their outputs and parameter    %% variables. All escaping functions can receive any values as    %% inputs. Bind all module- and letrec-defined variables to their    %% corresponding labels.    Esc = sets:from_list(Esc0),    Unsafe = unsafe(),    Empty = empty(),    Funs0 = dict:new(),    Vars0 = dict:store(escape, empty(), 		       dict:store(any, Unsafe, dict:new())),    Out0 = dict:new(),    F = fun (T, S = {Fs, Vs, Os}) ->		case type(T) of		    'fun' ->			L = get_label(T),			As = fun_vars(T),			X = case sets:is_element(L, Esc) of				true -> Unsafe;				false -> Empty			    end,			{dict:store(L, T, Fs),			 bind_vars_single(As, X, Vs),			 dict:store(L, none, Os)};		    letrec ->			{Fs, bind_defs(letrec_defs(T), Vs), Os};		    module ->			{Fs, bind_defs(module_defs(T), Vs), Os};		    _ ->			S		end	end,    {Funs, Vars, Out} = cerl_trees:fold(F, {Funs0, Vars0, Out0},					StartFun),    %% Add the dependency for the loop in 'external':    Dep = add_dep(loop, external, Dep0),    %% Enter the fixpoint iteration at the StartFun.    St = loop(StartFun, start, #state{vars = Vars,				      out = Out,				      dep = Dep,				      work = init_work(),				      funs = Funs,				      k = Limit}),    Ms = labels(dict:fetch(escape, St#state.vars)),    {Ms, St#state.vars}.loop(T, L, St0) ->%%%     io:fwrite("analyzing: ~w.\n",[L]),%%%     io:fwrite("work: ~w.\n", [St0#state.work]),    Xs0 = dict:fetch(L, St0#state.out),    {Xs1, St1} = visit(fun_body(T), L, St0),    Xs = limit(Xs1, St1#state.k),    {W, M} = case equal(Xs0, Xs) of		 true ->		     {St1#state.work, St1#state.out};		 false ->%%%       		     io:fwrite("out (~w) changed: ~w <- ~w.\n",%%%       			       [L, Xs, Xs0]),		     M1 = dict:store(L, Xs, St1#state.out),		     case dict:find(L, St1#state.dep) of			 {ok, S} ->			     {add_work(set__to_list(S), St1#state.work),			      M1};			 error ->			     {St1#state.work, M1}		     end	     end,    St2 = St1#state{out = M},

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?