📄 xref_base.erl
字号:
%% -> {ok, Value} | Errorget_default(State, Option) -> case catch current_default(State, Option) of {'EXIT', _} -> error({invalid_options, [Option]}); Value -> {ok, Value} end.%% -> [{Option, Value}]get_default(State) -> Fun = fun(O) -> V = current_default(State, O), {O, V} end, map(Fun, [builtins, recurse, verbose, warnings]).%% -> {ok, NewState} -> Errorset_default(State, Options) -> Opts = [builtins, recurse, verbose, warnings], ValidOptions = option_values(Opts, State), case xref_utils:options(Options, ValidOptions) of {Values = [[_], [_], [_], [_]], []} -> {ok, set_defaults(Opts, Values, State)}; _ -> error({invalid_options, Options}) end.format_error({error, Module, Error}) -> Module:format_error(Error);format_error({invalid_options, Options}) -> io_lib:format("Unknown option(s) or invalid option value(s): ~p~n", [Options]);format_error({invalid_filename, Term}) -> io_lib:format("A file name (a string) was expected: ~p~n", [Term]);format_error({no_debug_info, FileName}) -> io_lib:format("The BEAM file ~p has no debug info~n", [FileName]);format_error({invalid_path, Term}) -> io_lib:format("A path (a list of strings) was expected: ~p~n", [Term]);format_error({invalid_query, Term}) -> io_lib:format("A query (a string or an atom) was expected: ~p~n", [Term]);format_error({not_user_variable, Variable}) -> io_lib:format("~p is not a user variable~n", [Variable]);format_error({unknown_analysis, Term}) -> io_lib:format("~p is not a predefined analysis~n", [Term]);format_error({module_mismatch, Module, ReadModule}) -> io_lib:format("Name of read module ~p does not match analyzed module ~p~n", [ReadModule, Module]);format_error({release_clash, {Release, Dir, OldDir}}) -> io_lib:format("The release ~p read from ~p clashes with release " "already read from ~p~n", [Release, Dir, OldDir]);format_error({application_clash, {Application, Dir, OldDir}}) -> io_lib:format("The application ~p read from ~p clashes with application " "already read from ~p~n", [Application, Dir, OldDir]);format_error({module_clash, {Module, Dir, OldDir}}) -> io_lib:format("The module ~p read from ~p clashes with module " "already read from ~p~n", [Module, Dir, OldDir]);format_error({no_such_release, Name}) -> io_lib:format("There is no analyzed release ~p~n", [Name]);format_error({no_such_application, Name}) -> io_lib:format("There is no analyzed application ~p~n", [Name]);format_error({no_such_module, Name}) -> io_lib:format("There is no analyzed module ~p~n", [Name]);format_error({no_such_info, Term}) -> io_lib:format("~p is not one of 'modules', 'applications', " "'releases' and 'libraries'~n", [Term]);format_error(E) -> io_lib:format("~p~n", [E]).%%%% Local functions%%check_name([N]) when is_atom(N) -> true;check_name(_) -> false.do_update(OV, OW, State) -> Changed = updated_modules(State), Fun = fun({Mod,File}, S) -> {ok, _M, NS} = do_replace_module(Mod, File, OV, OW, S), NS end, NewState = foldl(Fun, State, Changed), {ok, NewState, to_external(domain(a_function(Changed)))}.%% -> [{Module, File}]updated_modules(State) -> Fun = fun({M,XMod}, L) -> RTime = XMod#xref_mod.mtime, File = module_file(XMod), case xref_utils:file_info(File) of {ok, {_, file, readable, MTime}} when MTime =/= RTime -> [{M,File} | L]; _Else -> L end end, foldl(Fun, [], dict:to_list(State#xref.modules)).do_forget([Variable | Variables], Vars, Vs, State) -> case dict:find(Variable, Vars) of {ok, #xref_var{vtype = user}} -> do_forget(Variables, Vars, Vs, State); _ -> error({not_user_variable, Variable}) end;do_forget([], Vars, Vs, State) -> Fun = fun(V, VT) -> {ok, #xref_var{value = Value}} = dict:find(V, VT), VT1 = xref_compiler:update_graph_counter(Value, -1, VT), dict:erase(V, VT1) end, NewVars = foldl(Fun, Vars, Vs), NewState = State#xref{variables = NewVars}, {ok, NewState}.%% -> {ok, Module, State} | throw(Error)do_replace_module(Module, File, OV, OW, State) -> {ok, OldXMod, State1} = do_remove_module(State, Module), OldApp = OldXMod#xref_mod.app_name, OB = OldXMod#xref_mod.builtins, case do_add_a_module(File, OldApp, OB, OV, OW, State1) of {ok, [Module], NewState} -> {ok, Module, NewState}; {ok, [ReadModule], _State} -> throw_error({module_mismatch, Module, ReadModule}); {ok, [], _NewState} -> throw_error({no_debug_info, File}) end.do_replace_application(Appl, Dir, OB, OV, OW, State) -> {ok, OldXApp, State1} = do_remove_application(State, Appl), Rel = OldXApp#xref_app.rel_name, N = OldXApp#xref_app.name, %% The application name is kept; the name of Dir is not used %% as source for a "new" application name. do_add_application(Dir, Rel, [N], OB, OV, OW, State1).%% -> {ok, ReleaseName, NewState} | throw(Error)do_add_release(Dir, RelName, OB, OV, OW, State) -> ok = is_filename(Dir), case xref_utils:release_directory(Dir, true, "ebin") of {ok, ReleaseDirName, ApplDir, Dirs} -> ApplDirs = xref_utils:select_last_application_version(Dirs), Release = case RelName of [[]] -> ReleaseDirName; [Name] -> Name end, XRel = #xref_rel{name = Release, dir = ApplDir}, NewState = do_add_release(State, XRel), add_rel_appls(ApplDirs, [Release], OB, OV, OW, NewState); Error -> throw(Error) end.do_add_release(S, XRel) -> Release = XRel#xref_rel.name, case dict:find(Release, S#xref.releases) of {ok, OldXRel} -> Dir = XRel#xref_rel.dir, OldDir = OldXRel#xref_rel.dir, throw_error({release_clash, {Release, Dir, OldDir}}); error -> D1 = dict:store(Release, XRel, S#xref.releases), S#xref{releases = D1} end.add_rel_appls([ApplDir | ApplDirs], Release, OB, OV, OW, State) -> {ok, _AppName, NewState} = add_appldir(ApplDir, Release, [[]], OB, OV, OW, State), add_rel_appls(ApplDirs, Release, OB, OV, OW, NewState);add_rel_appls([], [Release], _OB, _OV, _OW, NewState) -> {ok, Release, NewState}.do_add_application(Dir0, Release, Name, OB, OV, OW, State) -> ok = is_filename(Dir0), case xref_utils:select_application_directories([Dir0], "ebin") of {ok, [ApplD]} -> add_appldir(ApplD, Release, Name, OB, OV, OW, State); Error -> throw(Error) end.%% -> {ok, AppName, NewState} | throw(Error)add_appldir(ApplDir, Release, Name, OB, OV, OW, OldState) -> {AppName0, Vsn, Dir} = ApplDir, AppName = case Name of [[]] -> AppName0; [N] -> N end, AppInfo = #xref_app{name = AppName, rel_name = Release, vsn = Vsn, dir = Dir}, State1 = do_add_application(OldState, AppInfo), {ok, _Modules, NewState} = do_add_directory(Dir, [AppName], OB, false, OV, OW, State1), {ok, AppName, NewState}.%% -> State | throw(Error)do_add_application(S, XApp) -> Application = XApp#xref_app.name, case dict:find(Application, S#xref.applications) of {ok, OldXApp} -> Dir = XApp#xref_app.dir, OldDir = OldXApp#xref_app.dir, throw_error({application_clash, {Application, Dir, OldDir}}); error -> D1 = dict:store(Application, XApp, S#xref.applications), S#xref{applications = D1} end.%% -> {ok, Modules, NewState} | throw(Error)do_add_directory(Dir, AppName, Bui, Rec, Ver, War, State) -> ok = is_filename(Dir), {FileNames, Errors, Jams, Unreadable} = xref_utils:scan_directory(Dir, Rec, [?Suffix], [".jam"]), warnings(War, jam, Jams), warnings(War, unreadable, Unreadable), case Errors of [] -> do_add_modules(FileNames, AppName, Bui, Ver, War, State, []); [Error | _] -> throw(Error) end.do_add_modules([], _AppName, _OB, _OV, _OW, State, Modules) -> {ok, sort(Modules), State};do_add_modules([File | Files], AppName, OB, OV, OW, State, Modules) -> {ok, M, NewState} = do_add_module(File, AppName, OB, OV, OW, State), do_add_modules(Files, AppName, OB, OV, OW, NewState, M ++ Modules).%% -> {ok, Module, State} | throw(Error)do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) -> case xref_utils:split_filename(File, ?Suffix) of false -> throw_error({invalid_filename, File}); Splitname -> do_add_module(Splitname, AppName, Builtins, Verbose, Warnings, State) end.%% -> {ok, Module, State} | throw(Error)%% Options: verbose, warnings, builtinsdo_add_module({Dir, Basename}, AppName, Builtins, Verbose, Warnings, State) -> File = filename:join(Dir, Basename), {ok, M, Bad, NewState} = do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State), filter(fun({Tag,B}) -> warnings(Warnings, Tag, [[File,B]]) end, Bad), {ok, M, NewState}.do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State) -> message(Verbose, reading_beam, [File]), Mode = State#xref.mode, Me = self(), Fun = fun() -> Me ! {self(), abst(File, Builtins, Mode)} end, case xref_utils:subprocess(Fun, [link, {min_heap_size,100000}]) of {ok, _M, no_abstract_code} when Verbose -> message(Verbose, skipped_beam, []), {ok, [], [], State}; {ok, _M, no_abstract_code} when not Verbose -> message(Warnings, no_debug_info, [File]), {ok, [], [], State}; {ok, M, Data, UnresCalls0} -> %% Remove duplicates. Identical unresolved calls on the %% same line are counted as _one_ unresolved call. UnresCalls = usort(UnresCalls0), message(Verbose, done, []), NoUnresCalls = length(UnresCalls), case NoUnresCalls of 0 -> ok; 1 -> warnings(Warnings, unresolved_summary1, [[M]]); N -> warnings(Warnings, unresolved_summary, [[M, N]]) end, T = case xref_utils:file_info(File) of {ok, {_, _, _, Time}} -> Time; Error -> throw(Error) end, XMod = #xref_mod{name = M, app_name = AppName, dir = Dir, mtime = T, builtins = Builtins, no_unresolved = NoUnresCalls}, do_add_module(State, XMod, UnresCalls, Data); Error -> message(Verbose, error, []), throw(Error) end.abst(File, Builtins, Mode) when Mode =:= functions -> case beam_lib:chunks(File, [abstract_code, exports, attributes]) of {ok, {M,[{abstract_code,NoA},_X,_A]}} when NoA =:= no_abstract_code -> {ok, M, NoA}; {ok, {M, [{abstract_code, {abstract_v1, Forms}}, {exports,X0}, {attributes,A}]}} -> %% R7. X = xref_utils:fa_to_mfa(X0, M), D = deprecated(A, X, M), xref_reader:module(M, Forms, Builtins, X, D); {ok, {M, [{abstract_code, {abstract_v2, Forms}}, {exports,X0}, {attributes,A}]}} -> %% R8-R9B. X = xref_utils:fa_to_mfa(X0, M), D = deprecated(A, X, M), xref_reader:module(M, Forms, Builtins, X, D); {ok, {M, [{abstract_code, {raw_abstract_v1, Code}}, {exports,X0}, {attributes,A}]}} -> %% R9C- Forms0 = epp:interpret_file_attribute(Code), {_,_,Forms,_} = sys_pre_expand:module(Forms0, []), X = mfa_exports(X0, A, M), D = deprecated(A, X, M), xref_reader:module(M, Forms, Builtins, X, D); Error when element(1, Error) =:= error -> Error end;abst(File, Builtins, Mode) when Mode =:= modules -> case beam_lib:chunks(File, [exports, imports, attributes]) of {ok, {Mod, [{exports,X0}, {imports,I0}, {attributes,At}]}} -> X1 = mfa_exports(X0, At, Mod), X = filter(fun(MFA) -> not (predef_fun())(MFA) end, X1), D = deprecated(At, X, Mod), I = case Builtins of true -> I0; false -> Fun = fun({M,F,A}) -> not xref_utils:is_builtin(M, F, A) end, filter(Fun, I0) end, {ok, Mod, {X, I, D}, []}; Error when element(1, Error) =:= error -> Error end.mfa_exports(X0, Attributes, M) -> %% Adjust arities for abstract modules. X1 = case member({abstract, true}, Attributes) of true -> [{F,adjust_arity(F,A)} || {F,A} <- X0]; false -> X0 end, xref_utils:fa_to_mfa(X1, M).adjust_arity(F, A) -> case xref_utils:is_static_function(F, A) of true -> A; false -> A - 1 end.deprecated(A, X, M) -> DF = {[],[],[],[]}, case keysearch(deprecated, 1, A) of {value, {deprecated, D0}} -> depr(D0, M, DF, X, []); false -> {DF,[]} end.depr([D | Depr], M, DF, X, Bad) -> case depr_cat(D, M, X) of {I,Dt} -> NDF = setelement(I, DF, Dt ++ element(I, DF)), depr(Depr, M, NDF, X, Bad); undefined -> depr(Depr, M, DF, X, [D | Bad]) end;depr([], _M, DF, _X, Bad) -> {DF, reverse(Bad)}.depr_cat({F, A, Flg}, M, X) -> case deprecated_flag(Flg) of undefined -> undefined; I -> depr_fa(F, A, X, M, I) end;depr_cat({F, A}, M, X) -> depr_fa(F, A, X, M, 4);depr_cat(module, M, X) -> depr_fa('_', '_', X, M, 4);depr_cat(_D, _M, _X) -> undefined.depr_fa('_', '_', X, _M, I) -> {I, X};depr_fa(F, '_', X, _M, I) when is_atom(F) -> {I, filter(fun({_,F1,_}) -> F1 =:= F end, X)};depr_fa(F, A, _X, M, I) when is_atom(F), is_integer(A), A >= 0 -> {I, [{M,F,A}]};depr_fa(_F, _A, _X, _M, _I) -> undefined.%% deprecated_flag(Flag) -> integer() | undefined%% Maps symbolic flags for deprecated functions to integers.%deprecated_flag(1) -> 1;%deprecated_flag(2) -> 2;%deprecated_flag(3) -> 3;deprecated_flag(next_version) -> 1;deprecated_flag(next_major_release) -> 2;deprecated_flag(eventually) -> 3;deprecated_flag(_) -> undefined.%% -> {ok, Module, Bad, State} | throw(Error)%% Assumes:%% L U X is a subset of dom DefAt%% dom CallAt = LC U XC%% Attrs is collected from the attribute 'xref' (experimental).do_add_module(S, XMod, Unres, Data) -> M = XMod#xref_mod.name, case dict:find(M, S#xref.modules) of {ok, OldXMod} -> BF2 = module_file(XMod), BF1 = module_file(OldXMod), throw_error({module_clash, {M, BF1, BF2}}); error -> do_add_module(S, M, XMod, Unres, Data) end.%%do_add_module(S, M, _XMod, _Unres, Data)->%% {ok, M, [], S};do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions -> {DefAt0, LPreCAt0, XPreCAt0, LC0, XC0, X0, Attrs, Depr} = Data, %% Bad is a list of bad values of 'xref' attributes. {ALC0,AXC0,Bad0} = Attrs, FT = [tspec(func)], FET = [tspec(fun_edge)], PCA = [tspec(pre_call_at)], XPreCAt1 = xref_utils:xset(XPreCAt0, PCA), LPreCAt1 = xref_utils:xset(LPreCAt0, PCA), DefAt = xref_utils:xset(DefAt0, [tspec(def_at)]), X1 = xref_utils:xset(X0, FT), XC1 = xref_utils:xset(XC0, FET), LC1 = xref_utils:xset(LC0, FET), AXC1 = xref_utils:xset(AXC0, PCA), ALC1 = xref_utils:xset(ALC0, PCA), UnresCalls = xref_utils:xset(Unres0, PCA), Unres = domain(UnresCalls), DefinedFuns = domain(DefAt), {AXC, ALC, Bad1, LPreCAt2, XPreCAt2} = extra_edges(AXC1, ALC1, Bad0, DefinedFuns), Bad = map(fun(B) -> {xref_attr, B} end, Bad1), LPreCAt = union(LPreCAt1, LPreCAt2), XPreCAt = union(XPreCAt1, XPreCAt2), NoCalls = no_elements(LPreCAt) + no_elements(XPreCAt), LCallAt = relation_to_family(LPreCAt), XCallAt = relation_to_family(XPreCAt), CallAt = family_union(LCallAt, XCallAt), %% Local and exported functions with no definitions are removed. L = difference(DefinedFuns, X1), X = difference(DefinedFuns, L), XC = union(XC1, AXC), LC = union(LC1, ALC), {DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -