PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/syntax_tools/src/igor.erl

https://github.com/bmizerany/jungerl
Erlang | 3033 lines | 1739 code | 281 blank | 1013 comment | 56 complexity | ff484948db07f89ac55ea1732e1dd4a7 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. %% =====================================================================
  2. %% Igor, the Module Merger
  3. %%
  4. %% Copyright (C) 1998-2001 Richard Carlsson
  5. %%
  6. %% This library is free software; you can redistribute it and/or modify
  7. %% it under the terms of the GNU Lesser General Public License as
  8. %% published by the Free Software Foundation; either version 2 of the
  9. %% License, or (at your option) any later version.
  10. %%
  11. %% This library is distributed in the hope that it will be useful, but
  12. %% WITHOUT ANY WARRANTY; without even the implied warranty of
  13. %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. %% Lesser General Public License for more details.
  15. %%
  16. %% You should have received a copy of the GNU Lesser General Public
  17. %% License along with this library; if not, write to the Free Software
  18. %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  19. %% USA
  20. %%
  21. %% Author contact: richardc@csd.uu.se
  22. %%
  23. %% $Id$
  24. %%
  25. %% =====================================================================
  26. %%
  27. %% @doc Igor: the Module Merger and Renamer.
  28. %%
  29. %% <p>The program Igor merges the source code of one or more Erlang
  30. %% modules into a single module, which can then replace the original set
  31. %% of modules. Igor is also able to rename a set of (possibly
  32. %% interdependent) modules, without joining them into a single
  33. %% module.</p>
  34. %%
  35. %% <p>The main user interface consists of the functions <a
  36. %% href="#merge-3"><code>merge</code></a> and <a
  37. %% href="#rename-3"><code>rename</code></a>. See also the <a
  38. %% href="#parse_transform-2"><code>parse_transform</code></a>
  39. %% function</p>
  40. %%
  41. %% <p>A note of warning: Igor cannot do anything about the case when the
  42. %% name of a remote function is passed to the built-in functions
  43. %% <code>apply</code> and <code>spawn</code> <em>unless</em> the module
  44. %% and function names are explicitly stated in the call, as in e.g.
  45. %% <code>apply(lists, reverse, [Xs])</code>. In all other cases, Igor
  46. %% leaves such calls unchanged, and warns the user that manual editing
  47. %% might be necessary.</p>
  48. %%
  49. %% <p>Also note that Erlang records will be renamed as necessary to
  50. %% avoid non-equivalent definitions using the same record name. This
  51. %% does not work if the source code accesses the name field of such
  52. %% record tuples by <code>element/2</code> or similar methods. Always
  53. %% use the record syntax to handle record tuples, if possible.</p>
  54. %%
  55. %% <p>Disclaimer: the author of this program takes no responsibility for
  56. %% the correctness of the produced output, or for any effects of its
  57. %% execution. In particular, the author may not be held responsible
  58. %% should Igor include the code of a deceased madman in the result.</p>
  59. %%
  60. %% <p>For further information on Igors in general, see e.g. "Young
  61. %% Frankenstein", Mel Brooks, 1974, and "The Fifth Elephant", Terry
  62. %% Pratchett, 1999.</p>
  63. %% @end
  64. %%
  65. %% =====================================================================
  66. %%
  67. %% This program is named after the character Igor, assistant to Dr.
  68. %% Frankenstein, in the 1939 film "Son of Frankenstein" (with Boris
  69. %% Karloff playing The Monster for the last time; Igor was played by
  70. %% Bela Lugosi). Igor's job (in the film) was mainly to bring reasonably
  71. %% fresh parts of various human corpses to the good Doctor, for his
  72. %% purpose of reanimating them in the shape of a new formidable, living
  73. %% creature.
  74. %%
  75. %% Merging code is done by joining the sources, possibly changing the
  76. %% order of declarations as necessary, renaming functions and records to
  77. %% avoid name clashes, and changing remote calls to local calls where
  78. %% possible. Stub modules may be automatically generated to redirect any
  79. %% calls that still use the old names. Indirectly, code merging can be
  80. %% used to simply rename a set of modules.
  81. %%
  82. %% What Igor does not do is to optimise the resulting code, which
  83. %% typically can benefit from techniques such as inlining, constant
  84. %% folding, specialisation, etc. This task is left to the Doctor.
  85. %% (Luckily, Igor can call on Inga to do some cleanup; cf. 'erl_tidy'.)
  86. %% FIXME: don't remove module qualifier if name is (auto-)imported!
  87. %% TODO: handle merging of parameterized modules (somehow).
  88. %% TODO: check for redefinition of macros; check equivalence; comment out.
  89. %% TODO: {export, [E]}, E = atom() | {atom(), atom(), integer()}.
  90. %% TODO: improve documentation.
  91. %% TODO: optionally rename all functions from specified (or all) modules.
  92. -module(igor).
  93. -export([create_stubs/2, merge/2, merge/3, merge_files/3, merge_files/4,
  94. merge_sources/3, parse_transform/2, rename/2, rename/3]).
  95. -include_lib("kernel/include/file.hrl").
  96. %% =====================================================================
  97. %% Global Constants
  98. -define(NOTE_HEADER, "Note from Igor: ").
  99. -define(COMMENT_PREFIX, "% ").
  100. -define(COMMENT_BAR,
  101. "======================="
  102. "======================="
  103. "=======================").
  104. -define(NOTE_PREFIX, "%! ").
  105. -define(KILL_PREFIX, "%<<< ").
  106. -define(DEFAULT_INCLUDES, ["."]).
  107. -define(DEFAULT_MACROS, []).
  108. -define(DEFAULT_SUFFIX, ".erl").
  109. -define(DEFAULT_BACKUP_SUFFIX, ".bak").
  110. -define(DEFAULT_DIR, "").
  111. -define(DEFAULT_STUB_DIR, "stubs").
  112. -define(TIDY_OPTS, [quiet]).
  113. %% This may also be used in patterns. R must not be an integer, i.e.,
  114. %% the structure must be distinct from function names.
  115. -define(record_name(R), {record, R}).
  116. %% Data structure for module information
  117. -record(module, {name, % = atom()
  118. vars = none, % = [atom()] | none
  119. functions, % = ordset({atom(), int()})
  120. exports, % = ordset({atom(), int()})
  121. % | ordset({{atom(), int()},
  122. % term()})
  123. aliases, % = ordset({{atom(), int()},
  124. % {atom(),
  125. % {atom(), int()}}})
  126. attributes, % = ordset({atom(), term()})
  127. records % = [{atom(), [{atom(), term()}]}]
  128. }).
  129. %% The default pretty-printing function.
  130. default_printer(Tree, Options) ->
  131. erl_prettypr:format(Tree, Options).
  132. %% =====================================================================
  133. %% @spec parse_transform(Forms::[syntaxTree()], Options::[term()]) ->
  134. %% [syntaxTree()]
  135. %%
  136. %% syntaxTree() = erl_syntax:syntaxTree()
  137. %%
  138. %% @doc Allows Igor to work as a component of the Erlang compiler.
  139. %% Including the term <code>{parse_transform, igor}</code> in the
  140. %% compile options when compiling an Erlang module (cf.
  141. %% <code>compile:file/2</code>), will call upon Igor to process the
  142. %% source code, allowing automatic inclusion of other source files. No
  143. %% files are created or overwritten when this function is used.
  144. %%
  145. %% <p>Igor will look for terms <code>{igor, List}</code> in the compile
  146. %% options, where <code>List</code> is a list of Igor-specific options,
  147. %% as follows:
  148. %% <dl>
  149. %% <dt><code>{files, [filename()]}</code></dt>
  150. %% <dd>The value specifies a list of source files to be merged with
  151. %% the file being compiled; cf. <code>merge_files/4</code>.</dd>
  152. %% </dl>
  153. %%
  154. %% See <code>merge_files/4</code> for further options. Note, however,
  155. %% that some options are preset by this function and cannot be
  156. %% overridden by the user; in particular, all cosmetic features are
  157. %% turned off, for efficiency. Preprocessing is turned on.</p>
  158. %%
  159. %% @see merge_files/4
  160. %% @see compile:file/2
  161. parse_transform(Forms, Options) ->
  162. M = get_module_info(Forms),
  163. Name = M#module.name,
  164. Opts = proplists:append_values(igor, Options),
  165. Files = proplists:append_values(files, Opts),
  166. %% We turn off all features that are only cosmetic, and make sure to
  167. %% turn on preservation of `file' attributes.
  168. Opts1 = [{comments, false},
  169. {notes, no},
  170. {no_imports, true},
  171. {file_attributes, yes},
  172. {preprocess, true},
  173. {export, [Name]}
  174. | Opts],
  175. {T, _} = merge_files(Name, [Forms], Files, Opts1),
  176. verbose("done.", Opts1),
  177. erl_syntax:revert_forms(T).
  178. %% =====================================================================
  179. %% @spec merge(Name::atom(), Files::[filename()]) -> [filename()]
  180. %% @equiv merge(Name, Files, [])
  181. merge(Name, Files) ->
  182. merge(Name, Files, []).
  183. %% =====================================================================
  184. %% @spec merge(Name::atom(), Files::[filename()], Options::[term()]) ->
  185. %% [filename()]
  186. %%
  187. %% filename() = file:filename()
  188. %%
  189. %% @doc Merges source code files to a single file. <code>Name</code>
  190. %% specifies the name of the resulting module - not the name of the
  191. %% output file. <code>Files</code> is a list of file names and/or module
  192. %% names of source modules to be read and merged (see
  193. %% <code>merge_files/4</code> for details). All the input modules must
  194. %% be distinctly named.
  195. %%
  196. %% <p>The resulting source code is written to a file named
  197. %% "<code><em>Name</em>.erl</code>" in the current directory, unless
  198. %% otherwise specified by the options <code>dir</code> and
  199. %% <code>outfile</code> described below.</p>
  200. %%
  201. %% <p>Examples:
  202. %% <ul>
  203. %% <li>given a module <code>m</code> in file "<code>m.erl</code>"
  204. %% which uses the standard library module <code>lists</code>, calling
  205. %% <code>igor:merge(m, [m, lists])</code> will create a new file
  206. %% "<code>m.erl</code> which contains the code from <code>m</code> and
  207. %% exports the same functions, and which includes the referenced code
  208. %% from the <code>lists</code> module. The original file will be
  209. %% renamed to "<code>m.erl.bak</code>".</li>
  210. %%
  211. %% <li>given modules <code>m1</code> and <code>m2</code>, in
  212. %% corresponding files, calling <code>igor:merge(m, [m1, m2])</code>
  213. %% will create a file "<code>m.erl</code>" which contains the code
  214. %% from <code>m1</code> and <code>m2</code> and exports the functions
  215. %% of <code>m1</code>.</li>
  216. %% </ul></p>
  217. %%
  218. %% <p>Stub module files are created for those modules that are to be
  219. %% exported by the target module (see options <code>export</code>,
  220. %% <code>stubs</code> and <code>stub_dir</code>).</p>
  221. %%
  222. %% <p>The function returns the list of file names of all created
  223. %% modules, including any automatically created stub modules. The file
  224. %% name of the target module is always first in the list.</p>
  225. %%
  226. %% <p>Note: If you get a "syntax error" message when trying to merge
  227. %% files (and you know those files to be correct), then try the
  228. %% <code>preprocess</code> option. It typically means that your code
  229. %% contains too strange macros to be handled without actually performing
  230. %% the preprocessor expansions.</p>
  231. %%
  232. %% <p>Options:
  233. %% <dl>
  234. %% <dt><code>{backup_suffix, string()}</code></dt>
  235. %%
  236. %% <dd>Specifies the file name suffix to be used when a backup file
  237. %% is created; the default value is <code>".bak"</code>.</dd>
  238. %%
  239. %% <dt><code>{backups, bool()}</code></dt>
  240. %%
  241. %% <dd>If the value is <code>true</code>, existing files will be
  242. %% renamed before new files are opened for writing. The new names
  243. %% are formed by appending the string given by the
  244. %% <code>backup_suffix</code> option to the original name. The
  245. %% default value is <code>true</code>.</dd>
  246. %%
  247. %% <dt><code>{dir, filename()}</code></dt>
  248. %%
  249. %% <dd>Specifies the name of the directory in which the output file
  250. %% is to be written. An empty string is interpreted as the current
  251. %% directory. By default, the current directory is used.</dd>
  252. %%
  253. %% <dt><code>{outfile, filename()}</code></dt>
  254. %%
  255. %% <dd>Specifies the name of the file (without suffix) to which the
  256. %% resulting source code is to be written. By default, this is the
  257. %% same as the <code>Name</code> argument.</dd>
  258. %%
  259. %% <dt><code>{preprocess, bool()}</code></dt>
  260. %%
  261. %% <dd>If the value is <code>true</code>, preprocessing will be done
  262. %% when reading the source code. See <code>merge_files/4</code> for
  263. %% details.</dd>
  264. %%
  265. %% <dt><code>{printer, Function}</code></dt>
  266. %% <dd><ul>
  267. %% <li><code>Function = (syntaxTree()) -> string()</code></li>
  268. %% </ul>
  269. %% Specifies a function for prettyprinting Erlang syntax trees.
  270. %% This is used for outputting the resulting module definition, as
  271. %% well as for creating stub files. The function is assumed to
  272. %% return formatted text for the given syntax tree, and should raise
  273. %% an exception if an error occurs. The default formatting function
  274. %% calls <code>erl_prettypr:format/2</code>.</dd>
  275. %%
  276. %% <dt><code>{stub_dir, filename()}</code></dt>
  277. %%
  278. %% <dd>Specifies the name of the directory to which any generated
  279. %% stub module files are written. The default value is
  280. %% <code>"stubs"</code>.</dd>
  281. %%
  282. %% <dt><code>{stubs, bool()}</code></dt>
  283. %%
  284. %% <dd>If the value is <code>true</code>, stub module files will be
  285. %% automatically generated for all exported modules that do not have
  286. %% the same name as the target module. The default value is
  287. %% <code>true</code>.</dd>
  288. %%
  289. %% <dt><code>{suffix, string()}</code></dt>
  290. %%
  291. %% <dd>Specifies the suffix to be used for the output file names;
  292. %% the default value is <code>".erl"</code>.</dd>
  293. %% </dl>
  294. %%
  295. %% See <code>merge_files/4</code> for further options.</p>
  296. %%
  297. %% @see merge/2
  298. %% @see merge_files/4
  299. %% The defaults for 'merge' are also used for 'create_stubs'.
  300. -define(DEFAULT_MERGE_OPTS,
  301. [{backup_suffix, ?DEFAULT_BACKUP_SUFFIX},
  302. backups,
  303. {dir, ?DEFAULT_DIR},
  304. {printer, fun default_printer/2},
  305. {stub_dir, ?DEFAULT_STUB_DIR},
  306. stubs,
  307. {suffix, ?DEFAULT_SUFFIX},
  308. {verbose, false}]).
  309. merge(Name, Files, Opts) ->
  310. Opts1 = Opts ++ ?DEFAULT_MERGE_OPTS,
  311. {Tree, Stubs} = merge_files(Name, Files, Opts1),
  312. Dir = proplists:get_value(dir, Opts1, ""),
  313. Filename = proplists:get_value(outfile, Opts1, Name),
  314. File = write_module(Tree, Filename, Dir, Opts1),
  315. [File | maybe_create_stubs(Stubs, Opts1)].
  316. %% =====================================================================
  317. %% @spec merge_files(Name::atom(), Files::[filename()],
  318. %% Options::[term()]) ->
  319. %% {syntaxTree(), [stubDescriptor()]}
  320. %% @equiv merge_files(Name, [], Files, Options)
  321. merge_files(Name, Files, Options) ->
  322. merge_files(Name, [], Files, Options).
  323. %% =====================================================================
  324. %% @spec merge_files(Name::atom(), Sources::[Forms],
  325. %% Files::[filename()], Options::[term()]) ->
  326. %% {syntaxTree(), [stubDescriptor()]}
  327. %% Forms = syntaxTree() | [syntaxTree()]
  328. %%
  329. %% @doc Merges source code files and syntax trees to a single syntax
  330. %% tree. This is a file-reading front end to
  331. %% <code>merge_sources/3</code>. <code>Name</code> specifies the name of
  332. %% the resulting module - not the name of the output file.
  333. %% <code>Sources</code> is a list of syntax trees and/or lists of
  334. %% "source code form" syntax trees, each entry representing a module
  335. %% definition. <code>Files</code> is a list of file names and/or module
  336. %% names of source modules to be read and included. All the input
  337. %% modules must be distinctly named.
  338. %%
  339. %% <p>If a name in <code>Files</code> is not the name of an existing
  340. %% file, Igor assumes it represents a module name, and tries to locate
  341. %% and read the corresponding source file. The parsed files are appended
  342. %% to <code>Sources</code> and passed on to
  343. %% <code>merge_sources/3</code>, i.e., entries in <code>Sources</code>
  344. %% are listed before entries read from files.</p>
  345. %%
  346. %% <p>If no exports are listed by an <code>export</code> option (see
  347. %% <code>merge_sources/3</code> for details), then if <code>Name</code>
  348. %% is also the name of one of the input modules, that module will be
  349. %% exported; otherwise, the first listed module will be exported. Cf.
  350. %% the examples under <code>merge/3</code>.</p>
  351. %%
  352. %% <p>The result is a pair <code>{Tree, Stubs}</code>, where
  353. %% <code>Tree</code> represents the source code that is the result of
  354. %% merging all the code in <code>Sources</code> and <code>Files</code>,
  355. %% and <code>Stubs</code> is a list of stub module descriptors (see
  356. %% <code>merge_sources/3</code> for details).</p>
  357. %%
  358. %% <p>Options:
  359. %% <dl>
  360. %% <dt><code>{comments, bool()}</code></dt>
  361. %%
  362. %% <dd>If the value is <code>true</code>, source code comments in
  363. %% the original files will be preserved in the output. The default
  364. %% value is <code>true</code>.</dd>
  365. %%
  366. %% <dt><code>{find_src_rules, [{string(), string()}]}</code></dt>
  367. %%
  368. %% <dd>Specifies a list of rules for associating object files with
  369. %% source files, to be passed to the function
  370. %% <code>filename:find_src/2</code>. This can be used to change the
  371. %% way Igor looks for source files. If this option is not specified,
  372. %% the default system rules are used. The first occurrence of this
  373. %% option completely overrides any later in the option list.</dd>
  374. %%
  375. %% <dt><code>{includes, [filename()]}</code></dt>
  376. %%
  377. %% <dd>Specifies a list of directory names for the Erlang
  378. %% preprocessor, if used, to search for include files (cf. the
  379. %% <code>preprocess</code> option). The default value is the empty
  380. %% list. The directory of the source file and the current directory
  381. %% are automatically appended to the list.</dd>
  382. %%
  383. %% <dt><code>{macros, [{atom(), term()}]}</code></dt>
  384. %%
  385. %% <dd>Specifies a list of "pre-defined" macro definitions for the
  386. %% Erlang preprocessor, if used (cf. the <code>preprocess</code>
  387. %% option). The default value is the empty list.</dd>
  388. %%
  389. %% <dt><code>{preprocess, bool()}</code></dt>
  390. %%
  391. %% <dd>If the value is <code>false</code>, Igor will read source
  392. %% files without passing them through the Erlang preprocessor
  393. %% (<code>epp</code>), in order to avoid expansion of preprocessor
  394. %% directives such as <code>-include(...).</code>,
  395. %% <code>-define(...).</code> and <code>-ifdef(...)</code>, and
  396. %% macro calls such as <code>?LINE</code> and <code>?MY_MACRO(x,
  397. %% y)</code>. The default value is <code>false</code>, i.e.,
  398. %% preprocessing is not done. (See the module
  399. %% <code>epp_dodger</code> for details.)
  400. %%
  401. %% <p>Notes: If a file contains too exotic definitions or uses of
  402. %% macros, it will not be possible to read it without preprocessing.
  403. %% Furthermore, Igor does not currently try to sort out multiple
  404. %% inclusions of the same file, or redefinitions of the same macro
  405. %% name. Therefore, when preprocessing is turned off, it may become
  406. %% necessary to edit the resulting source code, removing such
  407. %% re-inclusions and redefinitions.</p></dd>
  408. %% </dl>
  409. %%
  410. %% See <code>merge_sources/3</code> for further options.</p>
  411. %%
  412. %% @see merge/3
  413. %% @see merge_files/3
  414. %% @see merge_sources/3
  415. %% @see filename:find_src/2
  416. %% @see epp_dodger
  417. merge_files(_, _Trees, [], _) ->
  418. report_error("no files to merge."),
  419. exit(badarg);
  420. merge_files(Name, Trees, Files, Opts) ->
  421. Opts1 = Opts ++ [{includes, ?DEFAULT_INCLUDES},
  422. {macros, ?DEFAULT_MACROS},
  423. {preprocess, false},
  424. comments],
  425. Sources = [read_module(F, Opts1) || F <- Files],
  426. merge_sources(Name, Trees ++ Sources, Opts1).
  427. %% =====================================================================
  428. %% @spec merge_sources(Name::atom(), Sources::[Forms],
  429. %% Options::[term()]) ->
  430. %% {syntaxTree(), [stubDescriptor()]}
  431. %%
  432. %% Forms = syntaxTree() | [syntaxTree()]
  433. %%
  434. %% @type stubDescriptor() = [{ModuleName, Functions, [Attribute]}]
  435. %% ModuleName = atom()
  436. %% Functions = [{FunctionName, {ModuleName, FunctionName}}]
  437. %% FunctionName = {atom(), integer()}
  438. %% Attribute = {atom(), term()}.
  439. %%
  440. %% A stub module descriptor contains the module name, a list of
  441. %% exported functions, and a list of module attributes. Each
  442. %% function is described by its name (which includes its arity),
  443. %% and the corresponding module and function that it calls. (The
  444. %% arities should always match.) The attributes are simply
  445. %% described by key-value pairs.
  446. %%
  447. %% @doc Merges syntax trees to a single syntax tree. This is the main
  448. %% code merging "engine". <code>Name</code> specifies the name of the
  449. %% resulting module. <code>Sources</code> is a list of syntax trees of
  450. %% type <code>form_list</code> and/or lists of "source code form" syntax
  451. %% trees, each entry representing a module definition. All the input
  452. %% modules must be distinctly named.
  453. %%
  454. %% <p>Unless otherwise specified by the options, all modules are assumed
  455. %% to be at least "static", and all except the target module are assumed
  456. %% to be "safe". See the <code>static</code> and <code>safe</code>
  457. %% options for details.</p>
  458. %%
  459. %% <p>If <code>Name</code> is also the name of one of the input modules,
  460. %% the code from that module will occur at the top of the resulting
  461. %% code, and no extra "header" comments will be added. In other words,
  462. %% the look of that module will be preserved.</p>
  463. %%
  464. %% <p>The result is a pair <code>{Tree, Stubs}</code>, where
  465. %% <code>Tree</code> represents the source code that is the result of
  466. %% merging all the code in <code>Sources</code>, and <code>Stubs</code>
  467. %% is a list of stub module descriptors (see below).</p>
  468. %%
  469. %% <p><code>Stubs</code> contains one entry for each exported input
  470. %% module (cf. the <code>export</code> option), each entry describing a
  471. %% stub module that redirects calls of functions in the original module
  472. %% to the corresponding (possibly renamed) functions in the new module.
  473. %% The stub descriptors can be used to automatically generate stub
  474. %% modules; see <code>create_stubs/2</code>.</p>
  475. %%
  476. %% <p>Options:
  477. %% <dl>
  478. %% <dt><code>{export, [atom()]}</code></dt>
  479. %%
  480. %% <dd>Specifies a list of names of input modules whose interfaces
  481. %% should be exported by the output module. A stub descriptor is
  482. %% generated for each specified module, unless its name is
  483. %% <code>Name</code>. If no modules are specified, then if
  484. %% <code>Name</code> is also the name of an input module, that
  485. %% module will be exported; otherwise the first listed module in
  486. %% <code>Sources</code> will be exported. The default value is the
  487. %% empty list.</dd>
  488. %%
  489. %% <dt><code>{export_all, bool()}</code></dt>
  490. %%
  491. %% <dd>If the value is <code>true</code>, this is equivalent to
  492. %% listing all of the input modules in the <code>export</code>
  493. %% option. The default value is <code>false</code>.</dd>
  494. %%
  495. %% <dt><code>{file_attributes, Preserve}</code></dt>
  496. %% <dd><ul>
  497. %% <li><code>Preserve = yes | comment | no</code></li>
  498. %% </ul>
  499. %% If the value is <code>yes</code>, all file attributes
  500. %% <code>-file(...)</code> in the input sources will be preserved in
  501. %% the resulting code. If the value is <code>comment</code>, they
  502. %% will be turned into comments, but remain in their original
  503. %% positions in the code relative to the other source code forms. If
  504. %% the value is <code>no</code>, all file attributes will be removed
  505. %% from the code, unless they have attached comments, in which case
  506. %% they will be handled as in the <code>comment</code> case. The
  507. %% default value is <code>no</code>.</dd>
  508. %%
  509. %% <dt><code>{no_banner, bool()}</code></dt>
  510. %%
  511. %% <dd>If the value is <code>true</code>, no banner comment will be
  512. %% added at the top of the resulting module, even if the target
  513. %% module does not have the same name as any of the input modules.
  514. %% Instead, Igor will try to preserve the look of the module whose
  515. %% code is at the top of the output. The default value is
  516. %% <code>false</code>.</dd>
  517. %%
  518. %% <dt><code>{no_headers, bool()}</code></dt>
  519. %%
  520. %% <dd>If the value is <code>true</code>, no header comments will be
  521. %% added to the resulting module at the beginning of each section of
  522. %% code that originates from a particular input module. The default
  523. %% value is <code>false</code>, which means that section headers are
  524. %% normally added whenever more than two or more modules are
  525. %% merged.</dd>
  526. %%
  527. %% <dt><code>{no_imports, bool()}</code></dt>
  528. %%
  529. %% <dd>If the value is <code>true</code>, all
  530. %% <code>-import(...)</code> declarations in the original code will
  531. %% be expanded in the result; otherwise, as much as possible of the
  532. %% original import declarations will be preserved. The default value
  533. %% is <code>false</code>.</dd>
  534. %%
  535. %% <dt><code>{notes, Notes}</code></dt>
  536. %% <dd><ul>
  537. %% <li><code>Notes = always | yes | no</code></li>
  538. %% </ul>
  539. %% If the value is <code>yes</code>, comments will be inserted where
  540. %% important changes have been made in the code. If the value is
  541. %% <code>always</code>, <em>all</em> changes to the code will be
  542. %% commented. If the value is <code>no</code>, changes will be made
  543. %% without comments. The default value is <code>yes</code>.</dd>
  544. %%
  545. %% <dt><code>{redirect, [{atom(), atom()}]}</code></dt>
  546. %%
  547. %% <dd>Specifies a list of pairs of module names, representing a
  548. %% mapping from old names to new. <em>The set of old names may not
  549. %% include any of the names of the input modules.</em> All calls to
  550. %% the listed old modules will be rewritten to refer to the
  551. %% corresponding new modules. <em>The redirected calls will not be
  552. %% further processed, even if the new destination is in one of the
  553. %% input modules.</em> This option mainly exists to support module
  554. %% renaming; cf. <code>rename/3</code>. The default value is the
  555. %% empty list.</dd>
  556. %%
  557. %% <dt><code>{safe, [atom()]}</code></dt>
  558. %%
  559. %% <dd>Specifies a list of names of input modules such that calls to
  560. %% these "safe" modules may be turned into direct local calls, that
  561. %% do not test for code replacement. Typically, this can be done for
  562. %% e.g. standard library modules. If a module is "safe", it is per
  563. %% definition also "static" (cf. below). The list may be empty. By
  564. %% default, all involved modules <em>except the target module</em>
  565. %% are considered "safe".</dd>
  566. %%
  567. %% <dt><code>{static, [atom()]}</code></dt>
  568. %%
  569. %% <dd>Specifies a list of names of input modules which will be
  570. %% assumed never to be replaced (reloaded) unless the target module
  571. %% is also first replaced. The list may be empty. The target module
  572. %% itself (which may also be one of the input modules) is always
  573. %% regarded as "static", regardless of the value of this option. By
  574. %% default, all involved modules are assumed to be static.</dd>
  575. %%
  576. %% <dt><code>{tidy, bool()}</code></dt>
  577. %%
  578. %% <dd>If the value is <code>true</code>, the resulting code will be
  579. %% processed using the <code>erl_tidy</code> module, which removes
  580. %% unused functions and does general code cleanup. (See
  581. %% <code>erl_tidy:module/2</code> for additional options.) The
  582. %% default value is <code>true</code>.</dd>
  583. %%
  584. %% <dt><code>{verbose, bool()}</code></dt>
  585. %%
  586. %% <dd>If the value is <code>true</code>, progress messages will be
  587. %% output while the program is running; the default value is
  588. %% <code>false</code>.</dd>
  589. %% </dl></p>
  590. %%
  591. %% <p>Note: The distinction between "static" and "safe" modules is
  592. %% necessary in order not to break the semantics of dynamic code
  593. %% replacement. A "static" source module will not be replaced unless the
  594. %% target module also is. Now imagine a state machine implemented by
  595. %% placing the code for each state in a separate module, and suppose
  596. %% that we want to merge this into a single target module, marking all
  597. %% source modules as static. At each point in the original code where a
  598. %% call is made from one of the modules to another (i.e., the state
  599. %% transitions), code replacement is expected to be detected. Then, if
  600. %% we in the merged code do not check at these points if the
  601. %% <em>target</em> module (the result of the merge) has been replaced,
  602. %% we can not be sure in general that we will be able to do code
  603. %% replacement of the merged state machine - it could run forever
  604. %% without detecting the code change. Therefore, all such calls must
  605. %% remain remote-calls (detecting code changes), but may call the target
  606. %% module directly.</p>
  607. %%
  608. %% <p>If we are sure that this kind of situation cannot ensue, we may
  609. %% specify the involved modules as "safe", and all calls between them
  610. %% will become local. Note that if the target module itself is specified
  611. %% as safe, "remote" calls to itself will be turned into local calls.
  612. %% This would destroy the code replacement properties of e.g. a typical
  613. %% server loop.</p>
  614. %%
  615. %% @see create_stubs/2
  616. %% @see rename/3
  617. %% @see erl_tidy:module/2
  618. %% Currently, there is no run-time support in Erlang for detecting
  619. %% whether some module has been changed since the current module was
  620. %% loaded. Therefore, if a source module is specified as non-static, not
  621. %% much will be gained from merging: a call to a non-static module will
  622. %% remain a remote call using the old module name, even when it is
  623. %% performed from within the merged code. If that module is specified as
  624. %% exported, the old name could then refer to an auto-generated stub,
  625. %% redirecting the call back to the corresponding function in the target
  626. %% module. This could possibly be useful in some cases, but efficiency
  627. %% is not improved by such a transformation. If support for efficient
  628. %% testing for module updates is added to Erlang in future versions,
  629. %% code merging will be able to use local calls even for non-static
  630. %% source modules, opening the way for compiler optimisations over the
  631. %% module boundaries.
  632. %% Data structure for merging environment.
  633. -record(merge, {target, % = atom()
  634. sources, % = ordset(atom())
  635. export, % = ordset(atom())
  636. static, % = ordset(atom())
  637. safe, % = ordset(atom())
  638. preserved, % = bool()
  639. no_headers, % = bool()
  640. notes, % = bool()
  641. redirect, % = dict(atom(), atom())
  642. no_imports, % = ordset(atom())
  643. options % = [term()]
  644. }).
  645. merge_sources(Name, Sources, Opts) ->
  646. %% Prepare the options and the inputs.
  647. Opts1 = Opts ++ [{export_all, false},
  648. {file_attributes, no},
  649. {no_imports, false},
  650. {notes, yes},
  651. tidy,
  652. {verbose, false}],
  653. Trees = case Sources of
  654. [] ->
  655. report_error("no sources to merge."),
  656. exit(badarg);
  657. _ ->
  658. [if list(M) -> erl_syntax:form_list(M);
  659. true -> M
  660. end
  661. || M <- Sources]
  662. end,
  663. %% There must be at least one module to work with.
  664. Modules = [get_module_info(T) || T <- Trees],
  665. merge_sources_1(Name, Modules, Trees, Opts1).
  666. %% Data structure for keeping state during transformation.
  667. -record(state, {export}).
  668. state__add_export(Name, Arity, S) ->
  669. S#state{export = sets:add_element({Name, Arity},
  670. S#state.export)}.
  671. merge_sources_1(Name, Modules, Trees, Opts) ->
  672. %% Get the (nonempty) list of source module names, in the given
  673. %% order. Multiple occurrences of the same source module name are
  674. %% not accepted.
  675. Ns = [M#module.name || M <- Modules],
  676. case duplicates(Ns) of
  677. [] ->
  678. ok;
  679. Ns1 ->
  680. report_error("same module names repeated in input: ~p.",
  681. [Ns1]),
  682. exit(error)
  683. end,
  684. Sources = ordsets:from_list(Ns),
  685. All = ordsets:add_element(Name, Sources),
  686. %% Initialise the merging environment from the given options.
  687. %%
  688. %% If the `export' option is the empty list, then if the target
  689. %% module is the same as one of the sources, that module will be
  690. %% exported; otherwise the first listed source module is exported.
  691. %% This simplifies use in most cases, and guarantees that the
  692. %% generated module has a well-defined interface. If `export_all' is
  693. %% `true', we expand it here by including the set of source module
  694. %% names.
  695. Es = case proplists:append_values(export, Opts) of
  696. [] ->
  697. case ordsets:is_element(Name, Sources) of
  698. true ->
  699. [Name];
  700. false ->
  701. [hd(Ns)]
  702. end;
  703. Es1 when list(Es1) ->
  704. ordsets:from_list(Es1);
  705. Es1 ->
  706. report_error("bad value for `export' option: ~P.",
  707. [Es1, 5])
  708. end,
  709. Export = case proplists:get_bool(export_all, Opts) of
  710. false ->
  711. Es;
  712. true ->
  713. ordsets:union(Sources, Es)
  714. end,
  715. check_module_names(Export, Sources, "declared as exported"),
  716. verbose("modules exported from `~w': ~p.", [Name, Export], Opts),
  717. %% The target module is always "static". (Particularly useful when
  718. %% the target is the same as one of the source modules). It is
  719. %% however not "safe" by default. If no modules are explicitly
  720. %% specified as static, it is assumed that *all* are static.
  721. Static0 = ordsets:from_list(proplists:append_values(static, Opts)),
  722. case proplists:is_defined(static, Opts) of
  723. false ->
  724. Static = All;
  725. true ->
  726. Static = ordsets:add_element(Name, Static0)
  727. end,
  728. check_module_names(Static, All, "declared 'static'"),
  729. verbose("static modules: ~p.", [Static], Opts),
  730. %% If no modules are explicitly specified as "safe", it is assumed
  731. %% that *all* source modules are "safe" except the target module and
  732. %% those explicitly specified as "static".
  733. Safe = case proplists:is_defined(safe, Opts) of
  734. false ->
  735. ordsets:subtract(Sources,
  736. ordsets:add_element(Name, Static0));
  737. true ->
  738. ordsets:from_list(
  739. proplists:append_values(safe, Opts))
  740. end,
  741. check_module_names(Safe, All, "declared 'safe'"),
  742. verbose("safe modules: ~p.", [Safe], Opts),
  743. Preserved = (ordsets:is_element(Name, Sources)
  744. and ordsets:is_element(Name, Export))
  745. or proplists:get_bool(no_banner, Opts),
  746. NoHeaders = proplists:get_bool(no_headers, Opts),
  747. Notes = proplists:get_value(notes, Opts, always),
  748. Rs = proplists:append_values(redirect, Opts),
  749. Redirect = case is_atom_map(Rs) of
  750. true ->
  751. Ms = ordsets:from_list([M || {M, _} <- Rs]),
  752. case ordsets:intersection(Sources, Ms) of
  753. [] ->
  754. ok;
  755. Ms1 ->
  756. report_error("cannot redirect calls to "
  757. "modules in input set: ~p.",
  758. [Ms1]),
  759. exit(error)
  760. end,
  761. dict:from_list(Rs);
  762. false ->
  763. report_error("bad value for `redirect' option: "
  764. "~P.",
  765. [Rs, 10]),
  766. exit(error)
  767. end,
  768. NoImports = case proplists:get_bool(no_imports, Opts) of
  769. true ->
  770. ordsets:from_list(Sources ++
  771. dict:fetch_keys(Redirect));
  772. false ->
  773. ordsets:from_list(dict:fetch_keys(Redirect))
  774. end,
  775. Env = #merge{target = Name,
  776. sources = Sources,
  777. export = Export,
  778. safe = Safe,
  779. static = Static,
  780. preserved = Preserved,
  781. no_headers = NoHeaders,
  782. notes = Notes,
  783. redirect = Redirect,
  784. no_imports = NoImports,
  785. options = Opts},
  786. merge_sources_2(Env, Modules, Trees, Opts).
  787. is_atom_map([{A1, A2} | As]) when atom(A1), atom(A2) ->
  788. is_atom_map(As);
  789. is_atom_map([]) ->
  790. true;
  791. is_atom_map(_) ->
  792. false.
  793. check_module_names(Names, Sources, Txt) ->
  794. case Names -- Sources of
  795. [] ->
  796. ok;
  797. Xs ->
  798. report_error("unknown modules ~s: ~p.", [Txt, Xs]),
  799. exit(error)
  800. end.
  801. %% This function performs all the stages of the actual merge:
  802. merge_sources_2(Env, Modules, Trees, Opts) ->
  803. %% Compute the merged name space and the list of renamings.
  804. {Names, Renaming} = merge_namespaces(Modules, Env),
  805. %% Merge the source module descriptions, computing a structure
  806. %% describing the resulting module, and a table of aliases which
  807. %% must be expanded.
  808. {Module, Expansions} = merge_info(Modules, Names, Renaming,
  809. Env),
  810. %% Merge the actual source code, also returning the "original
  811. %% header" (for the first code section in the output).
  812. St = #state{export = sets:new()},
  813. {Tree, Header, St1} = merge_code(Trees, Modules, Expansions,
  814. Renaming, Env, St),
  815. %% Filter out unwanted program forms and add a preamble to the code,
  816. %% making a complete module.
  817. Tree1 = erl_syntax:form_list([make_preamble(Module, Header,
  818. Env, St1),
  819. filter_forms(Tree, Env)]),
  820. %% Tidy the final syntax tree (removing unused functions) and return
  821. %% it together with the list of stub descriptors.
  822. {tidy(Tree1, Opts), make_stubs(Modules, Renaming, Env)}.
  823. make_preamble(Module, Header, Env, St) ->
  824. Name = Module#module.name,
  825. Vars = Module#module.vars,
  826. Extras = ordsets:from_list(sets:to_list(St#state.export)),
  827. Exports = make_exports(Module#module.exports, Extras),
  828. Imports = make_imports(Module#module.aliases),
  829. Attributes = make_attributes(Module#module.attributes),
  830. erl_syntax:form_list(module_header(Header, Name, Vars, Env)
  831. ++ Exports
  832. ++ Imports
  833. ++ Attributes).
  834. %% If the target preserves one of the source modules, we do not generate
  835. %% a new header, but use the original.
  836. module_header(Forms, Name, Vars, Env) ->
  837. case Env#merge.preserved of
  838. true ->
  839. update_header(Forms, Name, Vars);
  840. false ->
  841. [comment([?COMMENT_BAR,
  842. "This module was formed by merging "
  843. "the following modules:",
  844. ""]
  845. ++ [lists:flatten(io_lib:fwrite("\t\t`~w'",
  846. [M]))
  847. || M <- Env#merge.sources]
  848. ++ ["",
  849. timestamp(),
  850. ""]),
  851. erl_syntax:attribute(erl_syntax:atom('module'),
  852. [erl_syntax:atom(Name)])]
  853. end.
  854. update_header(Fs, Name, Vars) ->
  855. [M | Fs1] = lists:reverse(Fs),
  856. Ps = if Vars == none -> [];
  857. true -> [erl_syntax:list([erl_syntax:variable(V)
  858. || V <- Vars])]
  859. end,
  860. M1 = rewrite(M, erl_syntax:attribute(erl_syntax:atom('module'),
  861. [erl_syntax:atom(Name) | Ps])),
  862. lists:reverse([M1 | Fs1]).
  863. %% Some functions may have been noted as necessary to export (because of
  864. %% how they are called) even though the user did not specify that the
  865. %% modules in which these functions originated should be part of the
  866. %% interface of the resulting module.
  867. make_exports(Exports, Extras) ->
  868. case ordsets:subtract(Extras, Exports) of
  869. [] ->
  870. [make_export(Exports)];
  871. Es ->
  872. [make_export(Exports),
  873. comment(["** The following exports "
  874. "are not official: **"]),
  875. make_export(Es)]
  876. end.
  877. make_export(Names) ->
  878. Es = [erl_syntax:arity_qualifier(erl_syntax:atom(F),
  879. erl_syntax:integer(A))
  880. || {F, A} <- Names],
  881. if Es == [] ->
  882. comment(["** Nothing is officially exported "
  883. "from this module! **"]);
  884. true ->
  885. erl_syntax:attribute(erl_syntax:atom('export'),
  886. [erl_syntax:list(Es)])
  887. end.
  888. %% Any aliases that cannot be expressed using `import' (i.e. those not
  889. %% on the form `{F, {M, F}}') are ignored.
  890. make_imports(As) ->
  891. %% First remove any auto-imports and "non-proper" imports from
  892. %% the list.
  893. As1 = [A || {F, {_M, F}} = A <- As, not is_auto_import(F)],
  894. [make_import(M, Fs) || {M, Fs} <- group_imports(As1)].
  895. make_import(Module, Names) ->
  896. Is = [erl_syntax:arity_qualifier(erl_syntax:atom(F),
  897. erl_syntax:integer(A))
  898. || {F, A} <- Names],
  899. erl_syntax:attribute(erl_syntax:atom('import'),
  900. [erl_syntax:atom(Module),
  901. erl_syntax:list(Is)]).
  902. %% Group aliases by module.
  903. group_imports(Imports) ->
  904. dict:to_list(
  905. lists:foldl(
  906. fun ({F, {M, F}}, D) ->
  907. case dict:find(M, D) of
  908. {ok, V} ->
  909. V1 = ordsets:add_element(F, V),
  910. dict:store(M, V1, D);
  911. error ->
  912. dict:store(M, [F], D)
  913. end
  914. end,
  915. dict:new(), Imports)).
  916. %% ---------------------------------------------------------------------
  917. %% Making stub descriptors
  918. %%
  919. %% These are generated for all exported modules that are not the target
  920. %% module.
  921. make_stubs(Modules, Renaming, Env) ->
  922. make_stubs_1(Modules, Renaming, Env).
  923. make_stubs_1([M | Ms], Renaming, Env) ->
  924. Name = M#module.name,
  925. if Name /= Env#merge.target ->
  926. case ordsets:is_element(Name, Env#merge.export) of
  927. true ->
  928. [make_stub(M, Renaming(Name), Env)
  929. | make_stubs_1(Ms, Renaming, Env)];
  930. false ->
  931. make_stubs_1(Ms, Renaming, Env)
  932. end;
  933. true ->
  934. make_stubs_1(Ms, Renaming, Env)
  935. end;
  936. make_stubs_1([], _, _) ->
  937. [].
  938. make_stub(M, Map, Env) ->
  939. Target = Env#merge.target,
  940. Es = [{F, {Target, Map(F)}} || F <- M#module.exports],
  941. {M#module.name, Es, M#module.attributes}.
  942. %% ---------------------------------------------------------------------
  943. %% Removing and/or out-commenting program forms. The returned form
  944. %% sequence tree is not necessarily flat.
  945. -record(filter, {records, file_attributes, attributes}).
  946. filter_forms(Tree, Env) ->
  947. Forms = erl_syntax:form_list_elements(
  948. erl_syntax:flatten_form_list(Tree)),
  949. erl_syntax:form_list(filter_forms_1(Forms, Env)).
  950. filter_forms_1(Forms, Env) ->
  951. {Fs, _} = filter_forms_2(Forms, Env),
  952. lists:reverse(Fs).
  953. filter_forms_2(Forms, Env) ->
  954. FileAttrsOpt = proplists:get_value(file_attributes,
  955. Env#merge.options, comment),
  956. %% Sanity check and translation of option value:
  957. FileAttrs = case FileAttrsOpt of
  958. yes -> keep;
  959. no -> delete;
  960. comment -> kill;
  961. _ ->
  962. report_error("invalid value for option "
  963. "`file_attributes': ~w.",
  964. [FileAttrsOpt]),
  965. exit(error)
  966. end,
  967. Attrs = if length(Env#merge.sources) == 1 ->
  968. delete; %% keeping the originals looks weird
  969. true ->
  970. kill
  971. end,
  972. S = #filter{records = sets:new(),
  973. file_attributes = FileAttrs,
  974. attributes = Attrs},
  975. lists:foldl(
  976. fun (F, {Fs, S0}) ->
  977. case filter_form(F, S0) of
  978. {keep, S1} ->
  979. {[F | Fs], S1}; % keep
  980. {kill, S1} ->
  981. {[kill_form(F) | Fs], S1}; % kill
  982. {delete, S1} ->
  983. %% Remove, or kill if it has comments (only
  984. %% top-level comments are examined).
  985. case erl_syntax:has_comments(F) of
  986. false ->
  987. {Fs, S1};
  988. true ->
  989. {[kill_form(F) | Fs], S1}
  990. end
  991. end
  992. end,
  993. {[], S}, Forms).
  994. filter_form(F, S) ->
  995. case erl_syntax_lib:analyze_form(F) of
  996. {attribute, {'file', _}} ->
  997. {S#filter.file_attributes, S};
  998. {attribute, {'module', _}} ->
  999. {delete, S};
  1000. {attribute, {'export', _}} ->
  1001. {delete, S};
  1002. {attribute, {'import', _}} ->
  1003. {delete, S};
  1004. {attribute, {'record', {R, _}}} ->
  1005. Records = S#filter.records,
  1006. case sets:is_element(R, Records) of
  1007. true ->
  1008. {kill, S}; % already defined above
  1009. false ->
  1010. S1 = S#filter{records =
  1011. sets:add_element(R, Records)},
  1012. {keep, S1}
  1013. end;
  1014. {attribute, preprocessor} ->
  1015. {keep, S}; %% keep all preprocessor attributes
  1016. {attribute, _} ->
  1017. {S#filter.attributes, S}; %% handle all other attributes
  1018. {error_marker, _} ->
  1019. {delete, S};
  1020. {warning_marker, _} ->
  1021. {delete, S};
  1022. eof_marker ->
  1023. {delete, S}; % these must be deleted!
  1024. _ ->
  1025. {keep, S} % keep all other Erlang forms
  1026. end.
  1027. %% This out-comments (kills) a program form. Any top-level pre-comments
  1028. %% are moved out, to avoid "nested" comments.
  1029. kill_form(F) ->
  1030. F1 = erl_syntax:set_precomments(F, []),
  1031. F2 = erl_syntax_lib:to_comment(F1, ?KILL_PREFIX),
  1032. erl_syntax:set_precomments(F2,
  1033. erl_syntax:get_precomments(F)).
  1034. %% ---------------------------------------------------------------------
  1035. %% Merging the name spaces of a set of modules. Returns the final set
  1036. %% (see module `sets') of names and a total renaming function (atom())
  1037. %% -> ({atom(), integer()}) -> {atom(), integer()}.
  1038. %%
  1039. %% Names are added in two passes, in order to avoid renaming the
  1040. %% interface functions whenever possible: all exported functions are
  1041. %% added to the name space before any nonexported are added, and
  1042. %% "exported" modules are taken before any other. Thus, the order is:
  1043. %%
  1044. %% - exported functions of exported modules
  1045. %% - exported functions of nonexported modules
  1046. %% - internal functions of exported modules
  1047. %% - internal functions of nonexported modules
  1048. %%
  1049. %% In fact, only the first group is important, but there might be some
  1050. %% point in establishing the above order, for better readability of the
  1051. %% final code.
  1052. merge_namespaces(Modules, Env) ->
  1053. Export = Env#merge.export,
  1054. Split = fun (M) ->
  1055. ordsets:is_element(M#module.name, Export)
  1056. end,
  1057. {M1, M2} = split_list(Split, Modules),
  1058. R = dict:new(),
  1059. Acc = {sets:new(), R},
  1060. {M3, Acc1} = merge_namespaces_1(M1, Acc),
  1061. %% Detect and warn about renamed interface functions
  1062. {_, Maps0} = Acc1,
  1063. case [{M, dict:to_list(Map)}
  1064. || {M, Map} <- dict:to_list(Maps0), dict:size(Map) /= 0] of
  1065. [] ->
  1066. ok;
  1067. Fs ->
  1068. report_warning("interface functions renamed:\n\t~p.",
  1069. [Fs])
  1070. end,
  1071. {M4, Acc2} = merge_namespaces_1(M2, Acc1),
  1072. Ms = M3 ++ M4,
  1073. Acc3 = merge_namespaces_2(Ms, Acc2),
  1074. {{Names, Maps}, _} = merge_namespaces_3(Ms, Acc3),
  1075. {Names, make_renaming_function(Maps)}.
  1076. %% Adding exported names. (Note that the list gets a new temporary
  1077. %% format also containing the exports.) This first step initialises the
  1078. %% Maps "dict-of-dicts" structure.
  1079. merge_namespaces_1(Modules, Acc) ->
  1080. lists:mapfoldl(
  1081. fun (Module, {Names, Maps}) ->
  1082. Exports = sets:from_list(Module#module.exports),
  1083. M = Module#module.name,
  1084. {Names1, Map} = add_function_renamings(M, Exports, Names,
  1085. dict:new()),
  1086. Maps1 = dict:store(M, Map, Maps),
  1087. {{Module, Exports}, {Names1, Maps1}}
  1088. end,
  1089. Acc, Modules).
  1090. %% Adding nonexported names.
  1091. merge_namespaces_2(Modules, Acc) ->
  1092. lists:foldl(
  1093. fun ({Module, Exports}, {Names, Maps}) ->
  1094. Other = sets:subtract(
  1095. sets:from_list(Module#module.functions),
  1096. Exports),
  1097. M = Module#module.name,
  1098. Map = dict:fetch(M, Maps),
  1099. {Names1, Map1} = add_function_renamings(M, Other, Names,
  1100. Map),
  1101. Maps1 = dict:store(M, Map1, Maps),
  1102. {Names1, Maps1}
  1103. end,
  1104. Acc, Modules).
  1105. %% Adding record names. We need to keep a global
  1106. %% "record-definition-to-new-record-name" mapping RMap while doing this.
  1107. merge_namespaces_3(Modules, Acc) ->
  1108. lists:foldl(
  1109. fun ({Module, _Exports}, {{Names, Maps}, RMap}) ->
  1110. Records = Module#module.records,
  1111. M = Module#module.name,
  1112. Map = dict:fetch(M, Maps),
  1113. {Names1, Map1, RMap1} = add_record_renamings(M, Records,
  1114. Names, Map,
  1115. RMap),
  1116. Maps1 = dict:store(M, Map1, Maps),
  1117. {{Names1, Maps1}, RMap1}
  1118. end,
  1119. {Acc, dict:new()}, Modules).
  1120. %% This takes the set of added function names together with the existing
  1121. %% name set, creates new function names where necessary, and returns the
  1122. %% final name set together with the list of renamings.
  1123. add_function_renamings(Module, New, Names, Map) ->
  1124. Clashes = sets:to_list(sets:intersection(New, Names)),
  1125. lists:foldl(
  1126. fun (F = {_, A}, {Names, Map}) when integer(A) ->
  1127. F1 = new_function_name(Module, F, Names),
  1128. {sets:add_element(F1, Names), dict:store(F, F1, Map)}
  1129. end,
  1130. {sets:union(New, Names), Map}, Clashes).
  1131. %% This is similar to the above, but for record names. Note that we add
  1132. %% both the record name and the whole definition to the namespace.
  1133. add_record_renamings(Module, Records, Names, Map, RMap) ->
  1134. lists:foldl(
  1135. fun (N = {R, Fs}, {Names, Map, RMap}) ->
  1136. case sets:is_element(?record_name(R), Names) of
  1137. true ->
  1138. %% The name is already in use.
  1139. case sets:is_element(?record_name(N), Names) of
  1140. true ->
  1141. %% We have seen this definition before;
  1142. %% make sure we use the same name.
  1143. {R1, _} = remap_record_name(N, RMap),
  1144. Map1 = dict:store(?record_name(R),
  1145. ?record_name(R1), Map),
  1146. {Names, Map1, RMap};
  1147. false ->
  1148. %% Redefinition of existing name. Create
  1149. %% new name and set up renamings.
  1150. N1 = {R1, _} = new_record_name(Module, R,
  1151. Fs, Names),
  1152. Map1 = dict:store(?record_name(R),
  1153. ?record_name(R1), Map),
  1154. RMap1 = dict:store(N, N1, RMap),
  1155. Names1 = sets:add_element(?record_name(N1),
  1156. Names),
  1157. {Names1, Map1, RMap1}
  1158. end;
  1159. false ->
  1160. %% A previously unused record name.
  1161. Names1 = sets:add_element(?record_name(R), Names),
  1162. Names2 = sets:add_element(?record_name(N), Names1),
  1163. {Names2, Map, RMap}
  1164. end
  1165. end,
  1166. {Names, Map, RMap}, Records).
  1167. remap_record_name(N, Map) ->
  1168. case dict:find(N, Map) of
  1169. {ok, N1} -> N1;
  1170. error -> N
  1171. end.
  1172. %% This hides the implementation of the record namespace. Since Map
  1173. %% yields identity for non-remapped names, the remapped names must be
  1174. %% stored in wrapped form.
  1175. map_record_name(R, Map) ->
  1176. ?record_name(R1) = Map(?record_name(R)),
  1177. R1.
  1178. %% When we rename a function, we want the new name to be as close as
  1179. %% possible to the old, and as informative as possible. Therefore, we
  1180. %% first prefix it with the name of the originating module, followed by
  1181. %% two underscore characters, and then if there still is a name clash,
  1182. %% we suffix the name by "_N", where N is the smallest possible positive
  1183. %% integer that does not cause a clash.
  1184. new_function_name(M, {F, A}, Names) ->
  1185. Base = atom_to_list(M) ++ "__" ++ atom_to_list(F),
  1186. Name = {list_to_atom(Base), A},
  1187. case sets:is_element(Name, Names) of
  1188. false ->
  1189. Name;
  1190. true ->
  1191. new_function_name(1, A, Base, Names)
  1192. end.
  1193. new_function_name(N, Arity, Base, Names) ->
  1194. Name = {list_to_atom(Base ++ "_" ++ integer_to_list(N)),
  1195. Arity},
  1196. case sets:is_element(Name, Names) of
  1197. false ->
  1198. Name;
  1199. true ->
  1200. %% Increment counter and try again.
  1201. new_function_name(N + 1, Arity, Base, Names)
  1202. end.
  1203. %% This is pretty much the same as new_function_name, for now.
  1204. new_record_name(M, R, Fs, Names) ->
  1205. Base = atom_to_list(M) ++ "__" ++ atom_to_list(R),
  1206. Name = {list_to_atom(Base), Fs},
  1207. case sets:is_element(?record_name(Name), Names) of
  1208. false ->
  1209. Name;
  1210. true ->
  1211. new_record_name_1(1, Base, Fs, Names)
  1212. end.
  1213. new_record_name_1(N, Base, Fs, Names) ->
  1214. Name = {list_to_atom(Base ++ "_" ++ integer_to_list(N)), Fs},
  1215. case sets:is_element(?record_name(Name), Names) of
  1216. false ->
  1217. Name;
  1218. true ->
  1219. %% Increment counter and try again.
  1220. new_record_name_1(N + 1, Base, Fs, Names)
  1221. end.
  1222. %% This returns a *total* function from the set of module names to the
  1223. %% set of *total* operators on function names, yielding identity for all
  1224. %% functi…

Large files files are truncated, but you can click here to view the full file