dialyzer_dep.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 553 行 · 第 1/2 页
ERL
553 行
%% ``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 via the world wide web 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.%% %% Copyright 2006, Tobias Lindahl and Kostis Sagonas%% %% $Id$%%%%%-------------------------------------------------------------------%%% File : dialyzer_dep.erl%%% Author : Tobias Lindahl <tobiasl@it.uu.se>%%%%%% Description: A pretty limited but efficient escape/dependency%%% analysis of Core Erlang.%%%%%% Created : 28 Oct 2005 by Tobias Lindahl <tobiasl@it.uu.se>%%%--------------------------------------------------------------------module(dialyzer_dep).-define(NO_UNUSED, true).-export([analyze/1]).-ifndef(NO_UNUSED).-export([test/1]).-endif.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% analyze(CoreTree) -> {Deps, Esc, Calls}.%% %% Deps = a dict mapping labels of functions to a ordset of functions it calls.%%%% Esc = a set (ordsets) of the labels of escaping functions. A function%% is considered to escape if the control escapes a function,%% i.e., this analysis is not module-local but rather%% function-local.%%%% Calls = a dict mapping apply:s to a ordset of function labels to which%% the operation can refer to. If 'external' is part of the%% set the operation can be externally defined.%%analyze(Tree) -> %%io:format("Handling ~w\n", [cerl:atom_val(cerl:module_name(Tree))]), {_, State} = traverse(Tree, map__new(), state__new(Tree), top), Esc = state__esc(State), %% Add dependency from 'external' to all escaping function State1 = state__add_deps(external, output(Esc), State), Deps = state__deps(State1), Calls = state__calls(State1), {map__finalize(Deps), set__to_ordsets(Esc), map__finalize(Calls)}.traverse(Tree, Out, State, CurrentFun) -> %%io:format("Type: ~w\n", [cerl:type(Tree)]), case cerl:type(Tree) of apply -> Op = cerl:apply_op(Tree), Args = cerl:apply_args(Tree), %% Op is always a variable and should not be marked as escaping %% based on its use. case var =:= cerl:type(Op) of false -> erlang:fault({apply_op_not_a_variable, cerl:type(Op)}); true -> ok end, OpFuns = case map__lookup(cerl_trees:get_label(Op), Out) of none -> output(none); {value, OF} -> OF end, {ArgFuns, State2} = traverse_list(Args, Out, State, CurrentFun), State3 = state__add_esc(merge_outs(ArgFuns), State2), State4 = state__add_deps(CurrentFun, OpFuns, State3), State5 = state__store_callsite(cerl_trees:get_label(Tree), OpFuns,State4), {output(set__singleton(external)), State5}; binary -> {output(none), State}; 'case' -> Arg = cerl:case_arg(Tree), {Funs, NewState} = traverse(Arg, Out, State, CurrentFun), Clauses = cerl:case_clauses(Tree), traverse_clauses(Clauses, Funs, Out, NewState, CurrentFun); call -> Args = cerl:call_args(Tree), {ArgFuns, State1} = traverse_list(Args, Out, State, CurrentFun), remote_call(Tree, merge_outs(ArgFuns), State1); 'catch' -> traverse(cerl:catch_body(Tree), Out, State, CurrentFun); cons -> {HdFuns, State1} = traverse(cerl:cons_hd(Tree), Out, State, CurrentFun), {TlFuns, State2} = traverse(cerl:cons_tl(Tree), Out, State1, CurrentFun), {merge_outs([HdFuns, TlFuns]), State2}; 'fun' -> %%io:format("Entering fun: ~w\n", [cerl_trees:get_label(Tree)]), Body = cerl:fun_body(Tree), Label = cerl_trees:get_label(Tree), if CurrentFun =:= top -> State1 = state__add_deps(top, output(set__singleton(Label)), State); true -> O1 = output(set__singleton(CurrentFun)), O2 = output(set__singleton(Label)), TmpState = state__add_deps(Label, O1, State), State1 = state__add_deps(CurrentFun, O2,TmpState) end, {BodyFuns, State2} = traverse(Body, Out, State1, cerl_trees:get_label(Tree)), {output(set__singleton(Label)), state__add_esc(BodyFuns, State2)}; 'let' -> Vars = cerl:let_vars(Tree), Arg = cerl:let_arg(Tree), Body = cerl:let_body(Tree), {ArgFuns, State1} = traverse(Arg, Out, State, CurrentFun), Out1 = bind_list(Vars, ArgFuns, Out), traverse(Body, Out1, State1, CurrentFun); letrec -> Defs = cerl:letrec_defs(Tree), Body = cerl:letrec_body(Tree), Out1 = bind_defs(Defs, Out), State1 = traverse_defs(Defs, Out1, State, CurrentFun), traverse(Body, Out1, State1, CurrentFun); literal -> {output(none), State}; module -> Defs = cerl:module_defs(Tree), Out1 = bind_defs(Defs, Out), State1 = traverse_defs(Defs, Out1, State, CurrentFun), {output(none), State1}; primop -> Args = cerl:primop_args(Tree), {ArgFuns, State1} = traverse_list(Args, Out, State, CurrentFun), primop(Tree, merge_outs(ArgFuns), State1); 'receive' -> Clauses = cerl:receive_clauses(Tree), TimeOut = cerl:receive_timeout(Tree), Action = cerl:receive_action(Tree), {ClauseFuns, State1} = traverse_clauses(Clauses, output(none), Out, State, CurrentFun), {_, State2} = traverse(TimeOut, Out, State1, CurrentFun), {ActionFuns, State3} = traverse(Action, Out, State2, CurrentFun), {merge_outs([ClauseFuns, ActionFuns]), State3}; seq -> {_, State1} = traverse(cerl:seq_arg(Tree), Out, State, CurrentFun), traverse(cerl:seq_body(Tree), Out, State1, CurrentFun); 'try' -> Arg = cerl:try_arg(Tree), Body = cerl:try_body(Tree), Vars = cerl:try_vars(Tree), EVars = cerl:try_evars(Tree), Handler = cerl:try_handler(Tree), {ArgFuns, State1} = traverse(Arg, Out, State, CurrentFun), Out1 = bind_list(Vars, ArgFuns, Out), {BodyFuns, State2} = traverse(Body, Out1, State1, CurrentFun), Out2 = bind_single(EVars, output(set__singleton(external)), Out), {HandlerFuns, State3} = traverse(Handler, Out2, State2, CurrentFun), {merge_outs([BodyFuns, HandlerFuns]), State3}; tuple -> Args = cerl:tuple_es(Tree), {List, State1} = traverse_list(Args, Out, State, CurrentFun), {merge_outs(List), State1}; values -> traverse_list(cerl:values_es(Tree), Out, State, CurrentFun); var -> case map__lookup(cerl_trees:get_label(Tree), Out) of none -> {output(none), State}; {value, Val} -> case is_only_external(Val) of true -> %% Do nothing {Val, State}; false -> %% If this is used in a function this means a dependency. {Val, state__add_deps(CurrentFun, Val, State)} end end end.traverse_list(Trees, Out, State, CurrentFun) -> traverse_list(Trees, Out, State, CurrentFun, []).traverse_list([Tree|Left], Out, State, CurrentFun, Acc) -> {X, State1} = traverse(Tree, Out, State, CurrentFun), traverse_list(Left, Out, State1, CurrentFun, [X|Acc]);traverse_list([], _Out, State, _CurrentFun, Acc) -> {output(lists:reverse(Acc)), State}.traverse_defs([{_, Fun}|Left], Out, State, CurrentFun) -> {_, State1} = traverse(Fun, Out, State, CurrentFun), traverse_defs(Left, Out, State1, CurrentFun);traverse_defs([], _Out, State, _CurrentFun) -> State.traverse_clauses(Clauses, ArgFuns, Out, State, CurrentFun) -> case filter_match_fail(Clauses) of [] -> %% Can happen for example with receives used as timouts. {output(none), State}; Clauses1 -> traverse_clauses(Clauses1, ArgFuns, Out, State, CurrentFun, []) end.traverse_clauses([Clause|Left], ArgFuns, Out, State, CurrentFun, Acc) -> Pats = cerl:clause_pats(Clause), Guard = cerl:clause_guard(Clause), Body = cerl:clause_body(Clause), Out1 = bind_pats_list(Pats, ArgFuns, Out), {_, State2} = traverse(Guard, Out1, State, CurrentFun), {BodyFuns, State3} = traverse(Body, Out1, State2, CurrentFun), traverse_clauses(Left, ArgFuns, Out, State3, CurrentFun, [BodyFuns|Acc]);traverse_clauses([], _ArgFuns, _Out, State, _CurrentFun, Acc) -> {merge_outs(Acc), State}.filter_match_fail([Clause]) -> Body = cerl:clause_body(Clause), case cerl:type(Body) of primop -> case cerl:atom_val(cerl:primop_name(Body)) of match_fail -> []; raise -> []; _ -> [Clause] end; _ -> [Clause] end;filter_match_fail([H|T]) -> [H|filter_match_fail(T)];filter_match_fail([]) -> %% This can actually happen, for example in %% receive after 1 -> ok end [].remote_call(Tree, ArgFuns, State) -> M = cerl:call_module(Tree), F = cerl:call_name(Tree), A = length(cerl:call_args(Tree)), case cerl:is_c_atom(M) andalso cerl:is_c_atom(F) of false -> %% Unknown function. {output(set__singleton(external)), state__add_esc(ArgFuns, State)}; true -> M1 = cerl:atom_val(M), F1 = cerl:atom_val(F), Literal = cerl_closurean:is_literal_op(M1, F1, A), case erl_bifs:is_pure(M1, F1, A) of true -> case Literal of true -> {output(none), State}; false -> {output(set__singleton(external)), state__add_esc(ArgFuns, State)} end; false -> State1 = case cerl_closurean:is_escape_op(M1, F1, A) of true -> state__add_esc(ArgFuns, State); false -> State end, case Literal of true -> {output(none), State1}; false -> {add_external(ArgFuns), State1} end end end.primop(Tree, ArgFuns, State) -> F = cerl:atom_val(cerl:primop_name(Tree)), A = length(cerl:primop_args(Tree)), State1 = case cerl_closurean:is_escape_op(F, A) of true -> state__add_esc(ArgFuns, State); false -> State end, case cerl_closurean:is_literal_op(F, A) of true -> {output(none), State1}; false -> {ArgFuns, State1}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?