📄 xref_base.erl
字号:
var_type('AM') -> {module, vertex};var_type('LM') -> {module, vertex};var_type('M') -> {module, vertex};var_type('UM') -> {module, vertex};var_type('R') -> {release, vertex};var_type('E') -> {function, edge};var_type('EE') -> {function, edge};var_type('LC') -> {function, edge};var_type('UC') -> {function, edge};var_type('XC') -> {function, edge};var_type('AE') -> {application, edge}; var_type('ME') -> {module, edge}; var_type('RE') -> {release, edge};var_type(_) -> {foo, bar}.make_families(ModDictList, N) -> Fun1 = fun({_,XMod}) -> XMod#xref_mod.data end, Ss = from_sets(map(Fun1, ModDictList)), %% io:format("~n~p <= module data <= ~p~n", %% [pack:lsize(Ss), pack:usize(Ss)]), make_fams(N, Ss, []).make_fams(1, _Ss, L) -> L;make_fams(I, Ss, L) -> Fun = {external, fun(R) -> {element(1, R), element(I, R)} end}, make_fams(I-1, Ss, [projection(Fun, Ss) | L]).make_M2A(ModDictList) -> Fun = fun({M,XMod}) -> {M, XMod#xref_mod.app_name} end, Mod0 = family(map(Fun, ModDictList)), Mod = family_to_relation(Mod0), Mod.make_A2R(ApplDict) -> AppDict = dict:to_list(ApplDict), Fun = fun({A,XApp}) -> {A, XApp#xref_app.rel_name} end, Appl0 = family(map(Fun, AppDict)), AllApps = domain(Appl0), Appl = family_to_relation(Appl0), {Appl, AllApps}.do_set_up_1(XC) -> %% Call Graph cross reference... XCp = union_of_family(XC), XC_1 = user_family(XCp), %% I - functions used externally from some module %% XU - functions used externally per module. I = range(XCp), {XU, XPredefined} = make_predefined(I, domain(XC)), {XC_1, XU, XPredefined}.make_predefined(I, CallingModules) -> XPredefined0 = predefined_funs(I), XPredefined1 = converse(substitution(1, XPredefined0)), %% predefined funs in undefined modules are still undefined... XPredefined2 = restriction(XPredefined1, CallingModules), XPredefined = relation_to_family(XPredefined2), XU = partition_family(1, I), {XU, XPredefined}.predefined_funs(Functions) -> specification({external, predef_fun()}, Functions).predef_fun() -> PredefinedFuns = xref_utils:predefined_functions(), fun({_M,F,A}) -> member({F,A}, PredefinedFuns) end.make_defat(Undef, DefAt0) -> % Complete DefAt with unknown functions: Zero = from_term(0), DAL = family_projection(fun(S) -> constant_function(S, Zero) end, Undef), family_union(DefAt0, DAL).%% -> {Unknown U Lib, Unknown, Lib} | throw(Error)make_libs(XU, F, AM, LibPath, LibDict) -> Undef = family_difference(XU, F), UM = difference(domain(family_to_relation(Undef)), AM), Fs = case is_empty_set(UM) of true -> []; false when LibPath =:= code_path -> BFun = fun(M, A) -> case xref_utils:find_beam(M) of {ok, File} -> [File | A]; _ -> A end end, foldl(BFun, [], to_external(UM)); false -> Libraries = dict:to_list(LibDict), Lb = restriction(a_function(Libraries), UM), MFun = fun({M,XLib}) -> #xref_lib{dir = Dir} = XLib, xref_utils:module_filename(Dir, M) end, map(MFun, to_external(Lb)) end, Fun = fun(FileName, Deprs) -> case beam_lib:chunks(FileName, [exports, attributes]) of {ok, {M, [{exports,X}, {attributes,A}]}} -> Exports = mfa_exports(X, A, M), %% No warnings for bad attributes... {Deprecated,_Bad} = deprecated(A, Exports, M), {{M,Exports}, [{M,Deprecated} | Deprs]}; Error -> throw(Error) end end, {XL, DL} = mapfoldl(Fun, [], Fs), LF = from_term(XL), %% Undef is the first argument to make sure that the whole of LF %% becomes garbage: Lib = family_intersection(Undef, LF), {B,_} = make_builtins(Undef), DLib = family_union(Lib, B), [DF_1,DF_21,DF_31,DF1] = depr_lib(4, DL, DL, [], [], DLib), DF_2 = family_union(DF_21, DF_1), DF_3 = family_union(DF_31, DF_2), DF = family_union(DF1, DF_3), U = family_difference(Undef, Lib), {Undef, U, Lib, DF, DF_1, DF_2, DF_3}.depr_lib(0, _, _, LL, [], _Lib) -> LL;depr_lib(I, [], DL, LL, L, Lib) -> DT = family_intersection(Lib, from_term(L)), depr_lib(I-1, DL, DL, [DT | LL], [], Lib);depr_lib(I, [{M,D} | Ds], DL, LL, L, Lib) -> depr_lib(I, Ds, DL, LL, [{M,element(I, D)} | L], Lib).make_builtins(U0) -> Tmp = family_to_relation(U0), Fun2 = {external, fun({_M,{M,F,A}}) -> xref_utils:is_builtin(M, F, A) end}, B = relation_to_family(specification(Fun2, Tmp)), U = family_difference(U0, B), {B, U}.% Returns a family that may not be defined for all modules.user_family(R) -> partition_family({external, fun({_MFA1, {M2,_,_}}) -> M2 end}, R).do_variables(State) -> Fun = fun({Name, #xref_var{vtype = user}}, {P,U}) -> {P,[Name | U]}; ({Name, #xref_var{vtype = predef}}, A={P,U}) -> case atom_to_list(Name) of [H|_] when H>= $a, H=<$z -> A; _Else -> {[Name | P], U} end; ({{tmp, V}, _}, A) -> io:format("Bug in ~p: temporary ~p~n", [?MODULE, V]), A; (_V, A) -> A end, {U,P} = foldl(Fun, {[],[]}, dict:to_list(State#xref.variables)), {sort(P), sort(U)}.%% Throws away the variables derived from raw data.take_down(S) when S#xref.variables =:= not_set_up -> S;take_down(S) -> S#xref{variables = not_set_up}.make_query(Format, Args) -> flatten(io_lib:format(Format, Args)).set_defaults([O | Os], [[V] | Vs], State) -> NewState = set_def(O, V, State), set_defaults(Os, Vs, NewState);set_defaults([], [], State) -> State.set_def(builtins, Value, State) -> State#xref{builtins_default = Value};set_def(recurse, Value, State) -> State#xref{recurse_default = Value};set_def(verbose, Value, State) -> State#xref{verbose_default = Value};set_def(warnings, Value, State) -> State#xref{warnings_default = Value}.option_values([Option | Options], State) -> Default = current_default(State, Option), [{Option, [Default,true,false]} | option_values(Options, State)];option_values([], _State) -> [].current_default(State, builtins) -> State#xref.builtins_default;current_default(State, recurse) -> State#xref.recurse_default;current_default(State, verbose) -> State#xref.verbose_default;current_default(State, warnings) -> State#xref.warnings_default.%% sets are used here to avoid long execution timesdo_info(S, modules) -> D = sort(dict:to_list(S#xref.modules)), map(fun({_M,XMod}) -> mod_info(XMod) end, D);do_info(S, applications) -> AppMods = to_external(relation_to_family(relation(app_mods(S)))), Sum = sum_mods(S, AppMods), map(fun(AppSum) -> app_info(AppSum, S) end, Sum);do_info(S, releases) -> {RA, RRA} = rel_apps(S), rel_apps_sums(RA, RRA, S);do_info(S, libraries) -> D = sort(dict:to_list(S#xref.libraries)), map(fun({_L,XLib}) -> lib_info(XLib) end, D);do_info(_S, I) -> error({no_such_info, I}). do_info(S, Type, E) when is_atom(E) -> do_info(S, Type, [E]);do_info(S, modules, Modules0) when is_list(Modules0) -> Modules = to_external(set(Modules0)), XMods = find_info(Modules, S#xref.modules, no_such_module), map(fun(XMod) -> mod_info(XMod) end, XMods);do_info(S, applications, Applications) when is_list(Applications) -> _XA = find_info(Applications, S#xref.applications, no_such_application), AM = relation(app_mods(S)), App = set(Applications), AppMods_S = relation_to_family(restriction(AM, App)), AppSums = sum_mods(S, to_external(AppMods_S)), map(fun(AppSum) -> app_info(AppSum, S) end, AppSums);do_info(S, releases, Releases) when is_list(Releases) -> _XR = find_info(Releases, S#xref.releases, no_such_release), {AR, RRA} = rel_apps(S), AR_S = restriction(2, relation(AR), set(Releases)), rel_apps_sums(to_external(AR_S), RRA, S);do_info(S, libraries, Libraries0) when is_list(Libraries0) -> Libraries = to_external(set(Libraries0)), XLibs = find_info(Libraries, S#xref.libraries, no_such_library), map(fun(XLib) -> lib_info(XLib) end, XLibs);do_info(_S, I, J) when is_list(J) -> throw_error({no_such_info, I}).find_info([E | Es], Dict, Error) -> case dict:find(E, Dict) of error -> throw_error({Error, E}); {ok, X} -> [X | find_info(Es, Dict, Error)] end;find_info([], _Dict, _Error) -> [].%% -> {[{AppName, RelName}], [{RelName, XApp}]}rel_apps(S) -> D = sort(dict:to_list(S#xref.applications)), Fun = fun({_A, XApp}, Acc={AR, RRA}) -> case XApp#xref_app.rel_name of [] -> Acc; [R] -> AppName = XApp#xref_app.name, {[{AppName, R} | AR], [{R, XApp} | RRA]} end end, foldl(Fun, {[], []}, D).%% -> [{{RelName, [XApp]}, Sums}]rel_apps_sums(AR, RRA0, S) -> AppMods = app_mods(S), % [{AppName, XMod}] RRA1 = relation_to_family(relation(RRA0)), RRA = inverse(substitution(1, RRA1)), %% RRA is [{RelName,{RelName,[XApp]}}] RelMods = relative_product1(relation(AR), relation(AppMods)), RelAppsMods = relative_product1(RRA, RelMods), RelsAppsMods = to_external(relation_to_family(RelAppsMods)), %% [{{RelName, [XApp]}, [XMod]}] Sum = sum_mods(S, RelsAppsMods), map(fun(RelAppsSums) -> rel_info(RelAppsSums, S) end, Sum).%% -> [{AppName, XMod}]app_mods(S) -> D = sort(dict:to_list(S#xref.modules)), Fun = fun({_M,XMod}, Acc) -> case XMod#xref_mod.app_name of [] -> Acc; [AppName] -> [{AppName, XMod} | Acc] end end, foldl(Fun, [], D).mod_info(XMod) -> #xref_mod{name = M, app_name = AppName, builtins = BuiltIns, dir = Dir, info = Info} = XMod, App = sup_info(AppName), {M, [{application, App}, {builtins, BuiltIns}, {directory, Dir} | Info]}.app_info({AppName, ModSums}, S) -> XApp = dict:fetch(AppName, S#xref.applications), #xref_app{rel_name = RelName, vsn = Vsn, dir = Dir} = XApp, Release = sup_info(RelName), {AppName, [{directory,Dir}, {release, Release}, {version,Vsn} | ModSums]}. rel_info({{RelName, XApps}, ModSums}, S) -> NoApps = length(XApps), XRel = dict:fetch(RelName, S#xref.releases), Dir = XRel#xref_rel.dir, {RelName, [{directory, Dir}, {no_applications, NoApps} | ModSums]}.lib_info(XLib) -> #xref_lib{name = LibName, dir = Dir} = XLib, {LibName, [{directory,Dir}]}.sup_info([]) -> [];sup_info([Name]) -> [Name].sum_mods(S, AppsMods) -> sum_mods(S, AppsMods, []).sum_mods(S, [{N, XMods} | NX], L) -> sum_mods(S, NX, [{N, no_sum(S, XMods)} | L]);sum_mods(_S, [], L) -> reverse(L).no_sum(S, L) when S#xref.mode =:= functions -> no_sum(L, 0, 0, 0, 0, 0, 0, 0, 0, length(L));no_sum(S, L) when S#xref.mode =:= modules -> [{no_analyzed_modules, length(L)}].no_sum([XMod | D], C0, UC0, LC0, XC0, UFC0, L0, X0, EV0, NoM) -> [{no_calls, {C,UC}}, {no_function_calls, {LC,XC,UFC}}, {no_functions, {L,X}}, {no_inter_function_calls, EV}] = XMod#xref_mod.info, no_sum(D, C0+C, UC0+UC, LC0+LC, XC0+XC, UFC0+UFC, L0+L, X0+X, EV0+EV, NoM);no_sum([], C, UC, LC, XC, UFC, L, X, EV, NoM) -> [{no_analyzed_modules, NoM}, {no_calls, {C,UC}}, {no_function_calls, {LC,XC,UFC}}, {no_functions, {L,X}}, {no_inter_function_calls, EV}].%% -> ok | throw(Error)is_filename(F) when is_atom(F) -> ok;is_filename(F) -> case xref_utils:is_string(F, 31) of true -> ok; false -> throw_error({invalid_filename, F}) end.module_file(XMod) -> xref_utils:module_filename(XMod#xref_mod.dir, XMod#xref_mod.name).warnings(_Flag, _Message, []) -> true;warnings(Flag, Message, [F | Fs]) -> message(Flag, Message, F), warnings(Flag, Message, Fs).%% pack(term()) -> term()%%%% The identify function. The returned term does not use more heap%% than the given term. Tuples that are equal (=:=/2) are made %% "the same".%%%% The process dictionary is used because it seems to be faster than%% anything else right now...%%%pack(T) -> T;pack(T) -> PD = erase(), NT = pack1(T), %% true = T =:= NT, %% io:format("erasing ~p elements...~n", [length(erase())]), erase(), % wasting heap (and time)... map(fun({K,V}) -> put(K, V) end, PD), NT.pack1(C) when not is_tuple(C), not is_list(C) -> C;pack1([T | Ts]) -> %% don't store conscells... [pack1(T) | pack1(Ts)];%% Optimization.pack1(T={Mod,Fun,_}) when is_atom(Mod), is_atom(Fun) -> % MFA case get(T) of undefined -> put(T, T), T; NT -> NT end;pack1({C, L}) when is_list(L) -> % CallAt {pack1(C), L};pack1({MFA, L}) when is_integer(L) -> % DefAt {pack1(MFA), L};%% End optimization.pack1([]) -> [];pack1(T) -> % when tuple(T) case get(T) of undefined -> NT = tpack(T, size(T), []), put(NT, NT), NT; NT -> NT end.tpack(_T, 0, L) -> list_to_tuple(L);tpack(T, I, L) -> tpack(T, I-1, [pack1(element(I, T)) | L]).message(true, What, Arg) -> case What of reading_beam -> io:format("~s... ", Arg); skipped_beam -> io:format("skipped (no debug information)~n", Arg); no_debug_info -> io:format("Skipping ~s (no debug information)~n", Arg); unresolved_summary1 -> io:format("~p: 1 unresolved call~n", Arg); unresolved_summary -> io:format("~p: ~p unresolved calls~n", Arg); jam -> io:format("Skipping ~s (probably JAM file)~n", [Arg]); unreadable -> io:format("Skipping ~s (unreadable)~n", [Arg]); xref_attr -> io:format("~s: Skipping 'xref' attribute ~w~n", Arg); depr_attr -> io:format("~s: Skipping 'deprecated' attribute ~w~n", Arg); lib_search -> io:format("Scanning library path for BEAM files... ", []); lib_check -> io:format("Checking library files... ", []); set_up -> io:format("Setting up...", Arg); done -> io:format("done~n", Arg); error -> io:format("error~n", Arg); Else -> io:format("~p~n", [{Else,Arg}]) end;message(_, _, _) -> true.throw_error(Reason) -> throw(error(Reason)).error(Reason) -> {error, ?MODULE, Reason}.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -