📄 igor.erl
字号:
%% the target is the same as one of the source modules). It is %% however not "safe" by default. If no modules are explicitly %% specified as static, it is assumed that *all* are static. Static0 = ordsets:from_list(proplists:append_values(static, Opts)), case proplists:is_defined(static, Opts) of false -> Static = All; true -> Static = ordsets:add_element(Name, Static0) end, check_module_names(Static, All, "declared 'static'"), verbose("static modules: ~p.", [Static], Opts), %% If no modules are explicitly specified as "safe", it is assumed %% that *all* source modules are "safe" except the target module and %% those explicitly specified as "static". Safe = case proplists:is_defined(safe, Opts) of false -> ordsets:subtract(Sources, ordsets:add_element(Name, Static0)); true -> ordsets:from_list( proplists:append_values(safe, Opts)) end, check_module_names(Safe, All, "declared 'safe'"), verbose("safe modules: ~p.", [Safe], Opts), Preserved = (ordsets:is_element(Name, Sources) and ordsets:is_element(Name, Export)) or proplists:get_bool(no_banner, Opts), NoHeaders = proplists:get_bool(no_headers, Opts), Notes = proplists:get_value(notes, Opts, always), Rs = proplists:append_values(redirect, Opts), Redirect = case is_atom_map(Rs) of true -> Ms = ordsets:from_list([M || {M, _} <- Rs]), case ordsets:intersection(Sources, Ms) of [] -> ok; Ms1 -> report_error("cannot redirect calls to " "modules in input set: ~p.", [Ms1]), exit(error) end, dict:from_list(Rs); false -> report_error("bad value for `redirect' option: " "~P.", [Rs, 10]), exit(error) end, NoImports = case proplists:get_bool(no_imports, Opts) of true -> ordsets:from_list(Sources ++ dict:fetch_keys(Redirect)); false -> ordsets:from_list(dict:fetch_keys(Redirect)) end, Env = #merge{target = Name, sources = Sources, export = Export, safe = Safe, static = Static, preserved = Preserved, no_headers = NoHeaders, notes = Notes, redirect = Redirect, no_imports = NoImports, options = Opts}, merge_sources_2(Env, Modules, Trees, Opts).is_atom_map([{A1, A2} | As]) when is_atom(A1), is_atom(A2) -> is_atom_map(As);is_atom_map([]) -> true;is_atom_map(_) -> false.check_module_names(Names, Sources, Txt) -> case Names -- Sources of [] -> ok; Xs -> report_error("unknown modules ~s: ~p.", [Txt, Xs]), exit(error) end.%% This function performs all the stages of the actual merge:merge_sources_2(Env, Modules, Trees, Opts) -> %% Compute the merged name space and the list of renamings. {Names, Renaming} = merge_namespaces(Modules, Env), %% Merge the source module descriptions, computing a structure %% describing the resulting module, and a table of aliases which %% must be expanded. {Module, Expansions} = merge_info(Modules, Names, Renaming, Env), %% Merge the actual source code, also returning the "original %% header" (for the first code section in the output). St = #state{export = sets:new()}, {Tree, Header, St1} = merge_code(Trees, Modules, Expansions, Renaming, Env, St), %% Filter out unwanted program forms and add a preamble to the code, %% making a complete module. Tree1 = erl_syntax:form_list([make_preamble(Module, Header, Env, St1), filter_forms(Tree, Env)]), %% Tidy the final syntax tree (removing unused functions) and return %% it together with the list of stub descriptors. {tidy(Tree1, Opts), make_stubs(Modules, Renaming, Env)}.make_preamble(Module, Header, Env, St) -> Name = Module#module.name, Vars = Module#module.vars, Extras = ordsets:from_list(sets:to_list(St#state.export)), Exports = make_exports(Module#module.exports, Extras), Imports = make_imports(Module#module.aliases), Attributes = make_attributes(Module#module.attributes), erl_syntax:form_list(module_header(Header, Name, Vars, Env) ++ Exports ++ Imports ++ Attributes).%% If the target preserves one of the source modules, we do not generate%% a new header, but use the original.module_header(Forms, Name, Vars, Env) -> case Env#merge.preserved of true -> update_header(Forms, Name, Vars); false -> [comment([?COMMENT_BAR, "This module was formed by merging " "the following modules:", ""] ++ [lists:flatten(io_lib:fwrite("\t\t`~w'", [M])) || M <- Env#merge.sources] ++ ["", timestamp(), ""]), erl_syntax:attribute(erl_syntax:atom('module'), [erl_syntax:atom(Name)])] end.update_header(Fs, Name, Vars) -> [M | Fs1] = lists:reverse(Fs), Ps = if Vars == none -> []; true -> [erl_syntax:list([erl_syntax:variable(V) || V <- Vars])] end, M1 = rewrite(M, erl_syntax:attribute(erl_syntax:atom('module'), [erl_syntax:atom(Name) | Ps])), lists:reverse([M1 | Fs1]).%% Some functions may have been noted as necessary to export (because of%% how they are called) even though the user did not specify that the%% modules in which these functions originated should be part of the%% interface of the resulting module.make_exports(Exports, Extras) -> case ordsets:subtract(Extras, Exports) of [] -> [make_export(Exports)]; Es -> [make_export(Exports), comment(["** The following exports " "are not official: **"]), make_export(Es)] end.make_export(Names) -> Es = [erl_syntax:arity_qualifier(erl_syntax:atom(F), erl_syntax:integer(A)) || {F, A} <- Names], if Es == [] -> comment(["** Nothing is officially exported " "from this module! **"]); true -> erl_syntax:attribute(erl_syntax:atom('export'), [erl_syntax:list(Es)]) end.%% Any aliases that cannot be expressed using `import' (i.e. those not%% on the form `{F, {M, F}}') are ignored.make_imports(As) -> %% First remove any auto-imports and "non-proper" imports from %% the list. As1 = [A || {F, {_M, F}} = A <- As, not is_auto_import(F)], [make_import(M, Fs) || {M, Fs} <- group_imports(As1)].make_import(Module, Names) -> Is = [erl_syntax:arity_qualifier(erl_syntax:atom(F), erl_syntax:integer(A)) || {F, A} <- Names], erl_syntax:attribute(erl_syntax:atom('import'), [erl_syntax:atom(Module), erl_syntax:list(Is)]).%% Group aliases by module.group_imports(Imports) -> dict:to_list( lists:foldl( fun ({F, {M, F}}, D) -> case dict:find(M, D) of {ok, V} -> V1 = ordsets:add_element(F, V), dict:store(M, V1, D); error -> dict:store(M, [F], D) end end, dict:new(), Imports)).%% ---------------------------------------------------------------------%% Making stub descriptors%%%% These are generated for all exported modules that are not the target%% module.make_stubs(Modules, Renaming, Env) -> make_stubs_1(Modules, Renaming, Env).make_stubs_1([M | Ms], Renaming, Env) -> Name = M#module.name, if Name /= Env#merge.target -> case ordsets:is_element(Name, Env#merge.export) of true -> [make_stub(M, Renaming(Name), Env) | make_stubs_1(Ms, Renaming, Env)]; false -> make_stubs_1(Ms, Renaming, Env) end; true -> make_stubs_1(Ms, Renaming, Env) end;make_stubs_1([], _, _) -> [].make_stub(M, Map, Env) -> Target = Env#merge.target, Es = [{F, {Target, Map(F)}} || F <- M#module.exports], {M#module.name, Es, M#module.attributes}.%% ---------------------------------------------------------------------%% Removing and/or out-commenting program forms. The returned form%% sequence tree is not necessarily flat.-record(filter, {records, file_attributes, attributes}).filter_forms(Tree, Env) -> Forms = erl_syntax:form_list_elements( erl_syntax:flatten_form_list(Tree)), erl_syntax:form_list(filter_forms_1(Forms, Env)).filter_forms_1(Forms, Env) -> {Fs, _} = filter_forms_2(Forms, Env), lists:reverse(Fs).filter_forms_2(Forms, Env) -> FileAttrsOpt = proplists:get_value(file_attributes, Env#merge.options, comment), %% Sanity check and translation of option value: FileAttrs = case FileAttrsOpt of yes -> keep; no -> delete; comment -> kill; _ -> report_error("invalid value for option " "`file_attributes': ~w.", [FileAttrsOpt]), exit(error) end, Attrs = if length(Env#merge.sources) =:= 1 -> delete; %% keeping the originals looks weird true -> kill end, S = #filter{records = sets:new(), file_attributes = FileAttrs, attributes = Attrs}, lists:foldl( fun (F, {Fs, S0}) -> case filter_form(F, S0) of {keep, S1} -> {[F | Fs], S1}; % keep {kill, S1} -> {[kill_form(F) | Fs], S1}; % kill {delete, S1} -> %% Remove, or kill if it has comments (only %% top-level comments are examined). case erl_syntax:has_comments(F) of false -> {Fs, S1}; true -> {[kill_form(F) | Fs], S1} end end end, {[], S}, Forms).filter_form(F, S) -> case erl_syntax_lib:analyze_form(F) of {attribute, {'file', _}} -> {S#filter.file_attributes, S}; {attribute, {'module', _}} -> {delete, S}; {attribute, {'export', _}} -> {delete, S}; {attribute, {'import', _}} -> {delete, S}; {attribute, {'record', {R, _}}} -> Records = S#filter.records, case sets:is_element(R, Records) of true -> {kill, S}; % already defined above false -> S1 = S#filter{records = sets:add_element(R, Records)}, {keep, S1} end; {attribute, preprocessor} -> {keep, S}; %% keep all preprocessor attributes {attribute, _} -> {S#filter.attributes, S}; %% handle all other attributes {error_marker, _} -> {delete, S}; {warning_marker, _} -> {delete, S}; eof_marker -> {delete, S}; % these must be deleted! _ -> {keep, S} % keep all other Erlang forms end.%% This out-comments (kills) a program form. Any top-level pre-comments%% are moved out, to avoid "nested" comments.kill_form(F) -> F1 = erl_syntax:set_precomments(F, []), F2 = erl_syntax_lib:to_comment(F1, ?KILL_PREFIX), erl_syntax:set_precomments(F2, erl_syntax:get_precomments(F)).%% ---------------------------------------------------------------------%% Merging the name spaces of a set of modules. Returns the final set%% (see module `sets') of names and a total renaming function (atom())%% -> ({atom(), integer()}) -> {atom(), integer()}.%%%% Names are added in two passes, in order to avoid renaming the%% interface functions whenever possible: all exported functions are%% added to the name space before any nonexported are added, and%% "exported" modules are taken before any other. Thus, the order is:%%%% - exported functions of exported modules%% - exported functions of nonexported modules%% - internal functions of exported modules%% - internal functions of nonexported modules%%%% In fact, only the first group is important, but there might be some%% point in establishing the above order, for better readability of the%% final code.merge_namespaces(Modules, Env) -> Export = Env#merge.export,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -