consolidator.js 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  1. /**
  2. * @preserve Copyright 2012 Robert Gust-Bardon <http://robert.gust-bardon.org/>.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * * Redistributions of source code must retain the above
  10. * copyright notice, this list of conditions and the following
  11. * disclaimer.
  12. *
  13. * * Redistributions in binary form must reproduce the above
  14. * copyright notice, this list of conditions and the following
  15. * disclaimer in the documentation and/or other materials
  16. * provided with the distribution.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
  22. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  23. * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  25. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  27. * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  28. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. * SUCH DAMAGE.
  30. */
  31. /**
  32. * @fileoverview Enhances <a href="https://github.com/mishoo/UglifyJS/"
  33. * >UglifyJS</a> with consolidation of null, Boolean, and String values.
  34. * <p>Also known as aliasing, this feature has been deprecated in <a href=
  35. * "http://closure-compiler.googlecode.com/">the Closure Compiler</a> since its
  36. * initial release, where it is unavailable from the <abbr title=
  37. * "command line interface">CLI</a>. The Closure Compiler allows one to log and
  38. * influence this process. In contrast, this implementation does not introduce
  39. * any variable declarations in global code and derives String values from
  40. * identifier names used as property accessors.</p>
  41. * <p>Consolidating literals may worsen the data compression ratio when an <a
  42. * href="http://tools.ietf.org/html/rfc2616#section-3.5">encoding
  43. * transformation</a> is applied. For instance, <a href=
  44. * "http://code.jquery.com/jquery-1.7.1.js">jQuery 1.7.1</a> takes 248235 bytes.
  45. * Building it with <a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">
  46. * UglifyJS v1.2.5</a> results in 93647 bytes (37.73% of the original) which are
  47. * then compressed to 33154 bytes (13.36% of the original) using <a href=
  48. * "http://linux.die.net/man/1/gzip">gzip(1)</a>. Building it with the same
  49. * version of UglifyJS 1.2.5 patched with the implementation of consolidation
  50. * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison
  51. * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes
  52. * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned
  53. * 33154 bytes).</p>
  54. * <p>Written in <a href="http://es5.github.com/#x4.2.2">the strict variant</a>
  55. * of <a href="http://es5.github.com/">ECMA-262 5.1 Edition</a>. Encoded in <a
  56. * href="http://tools.ietf.org/html/rfc3629">UTF-8</a>. Follows <a href=
  57. * "http://google-styleguide.googlecode.com/svn-history/r76/trunk/javascriptguide.xml"
  58. * >Revision 2.28 of the Google JavaScript Style Guide</a> (except for the
  59. * discouraged use of the {@code function} tag and the {@code namespace} tag).
  60. * 100% typed for the <a href=
  61. * "http://closure-compiler.googlecode.com/files/compiler-20120123.tar.gz"
  62. * >Closure Compiler Version 1741</a>.</p>
  63. * <p>Should you find this software useful, please consider <a href=
  64. * "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JZLW72X8FD4WG"
  65. * >a donation</a>.</p>
  66. * @author follow.me@RGustBardon (Robert Gust-Bardon)
  67. * @supported Tested with:
  68. * <ul>
  69. * <li><a href="http://nodejs.org/dist/v0.6.10/">Node v0.6.10</a>,</li>
  70. * <li><a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">UglifyJS
  71. * v1.2.5</a>.</li>
  72. * </ul>
  73. */
  74. /*global console:false, exports:true, module:false, require:false */
  75. /*jshint sub:true */
  76. /**
  77. * Consolidates null, Boolean, and String values found inside an <abbr title=
  78. * "abstract syntax tree">AST</abbr>.
  79. * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object
  80. * representing an <abbr title="abstract syntax tree">AST</abbr>.
  81. * @return {!TSyntacticCodeUnit} An array-like object representing an <abbr
  82. * title="abstract syntax tree">AST</abbr> with its null, Boolean, and
  83. * String values consolidated.
  84. */
  85. // TODO(user) Consolidation of mathematical values found in numeric literals.
  86. // TODO(user) Unconsolidation.
  87. // TODO(user) Consolidation of ECMA-262 6th Edition programs.
  88. // TODO(user) Rewrite in ECMA-262 6th Edition.
  89. exports['ast_consolidate'] = function(oAbstractSyntaxTree) {
  90. 'use strict';
  91. /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
  92. latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
  93. onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
  94. sub:false, trailing:true */
  95. var _,
  96. /**
  97. * A record consisting of data about one or more source elements.
  98. * @constructor
  99. * @nosideeffects
  100. */
  101. TSourceElementsData = function() {
  102. /**
  103. * The category of the elements.
  104. * @type {number}
  105. * @see ESourceElementCategories
  106. */
  107. this.nCategory = ESourceElementCategories.N_OTHER;
  108. /**
  109. * The number of occurrences (within the elements) of each primitive
  110. * value that could be consolidated.
  111. * @type {!Array.<!Object.<string, number>>}
  112. */
  113. this.aCount = [];
  114. this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {};
  115. this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {};
  116. this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] =
  117. {};
  118. /**
  119. * Identifier names found within the elements.
  120. * @type {!Array.<string>}
  121. */
  122. this.aIdentifiers = [];
  123. /**
  124. * Prefixed representation Strings of each primitive value that could be
  125. * consolidated within the elements.
  126. * @type {!Array.<string>}
  127. */
  128. this.aPrimitiveValues = [];
  129. },
  130. /**
  131. * A record consisting of data about a primitive value that could be
  132. * consolidated.
  133. * @constructor
  134. * @nosideeffects
  135. */
  136. TPrimitiveValue = function() {
  137. /**
  138. * The difference in the number of terminal symbols between the original
  139. * source text and the one with the primitive value consolidated. If the
  140. * difference is positive, the primitive value is considered worthwhile.
  141. * @type {number}
  142. */
  143. this.nSaving = 0;
  144. /**
  145. * An identifier name of the variable that will be declared and assigned
  146. * the primitive value if the primitive value is consolidated.
  147. * @type {string}
  148. */
  149. this.sName = '';
  150. },
  151. /**
  152. * A record consisting of data on what to consolidate within the range of
  153. * source elements that is currently being considered.
  154. * @constructor
  155. * @nosideeffects
  156. */
  157. TSolution = function() {
  158. /**
  159. * An object whose keys are prefixed representation Strings of each
  160. * primitive value that could be consolidated within the elements and
  161. * whose values are corresponding data about those primitive values.
  162. * @type {!Object.<string, {nSaving: number, sName: string}>}
  163. * @see TPrimitiveValue
  164. */
  165. this.oPrimitiveValues = {};
  166. /**
  167. * The difference in the number of terminal symbols between the original
  168. * source text and the one with all the worthwhile primitive values
  169. * consolidated.
  170. * @type {number}
  171. * @see TPrimitiveValue#nSaving
  172. */
  173. this.nSavings = 0;
  174. },
  175. /**
  176. * The processor of <abbr title="abstract syntax tree">AST</abbr>s found
  177. * in UglifyJS.
  178. * @namespace
  179. * @type {!TProcessor}
  180. */
  181. oProcessor = (/** @type {!TProcessor} */ require('./process')),
  182. /**
  183. * A record consisting of a number of constants that represent the
  184. * difference in the number of terminal symbols between a source text with
  185. * a modified syntactic code unit and the original one.
  186. * @namespace
  187. * @type {!Object.<string, number>}
  188. */
  189. oWeights = {
  190. /**
  191. * The difference in the number of punctuators required by the bracket
  192. * notation and the dot notation.
  193. * <p><code>'[]'.length - '.'.length</code></p>
  194. * @const
  195. * @type {number}
  196. */
  197. N_PROPERTY_ACCESSOR: 1,
  198. /**
  199. * The number of punctuators required by a variable declaration with an
  200. * initialiser.
  201. * <p><code>':'.length + ';'.length</code></p>
  202. * @const
  203. * @type {number}
  204. */
  205. N_VARIABLE_DECLARATION: 2,
  206. /**
  207. * The number of terminal symbols required to introduce a variable
  208. * statement (excluding its variable declaration list).
  209. * <p><code>'var '.length</code></p>
  210. * @const
  211. * @type {number}
  212. */
  213. N_VARIABLE_STATEMENT_AFFIXATION: 4,
  214. /**
  215. * The number of terminal symbols needed to enclose source elements
  216. * within a function call with no argument values to a function with an
  217. * empty parameter list.
  218. * <p><code>'(function(){}());'.length</code></p>
  219. * @const
  220. * @type {number}
  221. */
  222. N_CLOSURE: 17
  223. },
  224. /**
  225. * Categories of primary expressions from which primitive values that
  226. * could be consolidated are derivable.
  227. * @namespace
  228. * @enum {number}
  229. */
  230. EPrimaryExpressionCategories = {
  231. /**
  232. * Identifier names used as property accessors.
  233. * @type {number}
  234. */
  235. N_IDENTIFIER_NAMES: 0,
  236. /**
  237. * String literals.
  238. * @type {number}
  239. */
  240. N_STRING_LITERALS: 1,
  241. /**
  242. * Null and Boolean literals.
  243. * @type {number}
  244. */
  245. N_NULL_AND_BOOLEAN_LITERALS: 2
  246. },
  247. /**
  248. * Prefixes of primitive values that could be consolidated.
  249. * The String values of the prefixes must have same number of characters.
  250. * The prefixes must not be used in any properties defined in any version
  251. * of <a href=
  252. * "http://www.ecma-international.org/publications/standards/Ecma-262.htm"
  253. * >ECMA-262</a>.
  254. * @namespace
  255. * @enum {string}
  256. */
  257. EValuePrefixes = {
  258. /**
  259. * Identifies String values.
  260. * @type {string}
  261. */
  262. S_STRING: '#S',
  263. /**
  264. * Identifies null and Boolean values.
  265. * @type {string}
  266. */
  267. S_SYMBOLIC: '#O'
  268. },
  269. /**
  270. * Categories of source elements in terms of their appropriateness of
  271. * having their primitive values consolidated.
  272. * @namespace
  273. * @enum {number}
  274. */
  275. ESourceElementCategories = {
  276. /**
  277. * Identifies a source element that includes the <a href=
  278. * "http://es5.github.com/#x12.10">{@code with}</a> statement.
  279. * @type {number}
  280. */
  281. N_WITH: 0,
  282. /**
  283. * Identifies a source element that includes the <a href=
  284. * "http://es5.github.com/#x15.1.2.1">{@code eval}</a> identifier name.
  285. * @type {number}
  286. */
  287. N_EVAL: 1,
  288. /**
  289. * Identifies a source element that must be excluded from the process
  290. * unless its whole scope is examined.
  291. * @type {number}
  292. */
  293. N_EXCLUDABLE: 2,
  294. /**
  295. * Identifies source elements not posing any problems.
  296. * @type {number}
  297. */
  298. N_OTHER: 3
  299. },
  300. /**
  301. * The list of literals (other than the String ones) whose primitive
  302. * values can be consolidated.
  303. * @const
  304. * @type {!Array.<string>}
  305. */
  306. A_OTHER_SUBSTITUTABLE_LITERALS = [
  307. 'null', // The null literal.
  308. 'false', // The Boolean literal {@code false}.
  309. 'true' // The Boolean literal {@code true}.
  310. ];
  311. (/**
  312. * Consolidates all worthwhile primitive values in a syntactic code unit.
  313. * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object
  314. * representing the branch of the abstract syntax tree representing the
  315. * syntactic code unit along with its scope.
  316. * @see TPrimitiveValue#nSaving
  317. */
  318. function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) {
  319. var _,
  320. /**
  321. * Indicates whether the syntactic code unit represents global code.
  322. * @type {boolean}
  323. */
  324. bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0],
  325. /**
  326. * Indicates whether the whole scope is being examined.
  327. * @type {boolean}
  328. */
  329. bIsWhollyExaminable = !bIsGlobal,
  330. /**
  331. * An array-like object representing source elements that constitute a
  332. * syntactic code unit.
  333. * @type {!TSyntacticCodeUnit}
  334. */
  335. oSourceElements,
  336. /**
  337. * A record consisting of data about the source element that is
  338. * currently being examined.
  339. * @type {!TSourceElementsData}
  340. */
  341. oSourceElementData,
  342. /**
  343. * The scope of the syntactic code unit.
  344. * @type {!TScope}
  345. */
  346. oScope,
  347. /**
  348. * An instance of an object that allows the traversal of an <abbr
  349. * title="abstract syntax tree">AST</abbr>.
  350. * @type {!TWalker}
  351. */
  352. oWalker,
  353. /**
  354. * An object encompassing collections of functions used during the
  355. * traversal of an <abbr title="abstract syntax tree">AST</abbr>.
  356. * @namespace
  357. * @type {!Object.<string, !Object.<string, function(...[*])>>}
  358. */
  359. oWalkers = {
  360. /**
  361. * A collection of functions used during the surveyance of source
  362. * elements.
  363. * @namespace
  364. * @type {!Object.<string, function(...[*])>}
  365. */
  366. oSurveySourceElement: {
  367. /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
  368. /**
  369. * Classifies the source element as excludable if it does not
  370. * contain a {@code with} statement or the {@code eval} identifier
  371. * name. Adds the identifier of the function and its formal
  372. * parameters to the list of identifier names found.
  373. * @param {string} sIdentifier The identifier of the function.
  374. * @param {!Array.<string>} aFormalParameterList Formal parameters.
  375. * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
  376. */
  377. 'defun': function(
  378. sIdentifier,
  379. aFormalParameterList,
  380. oFunctionBody) {
  381. fClassifyAsExcludable();
  382. fAddIdentifier(sIdentifier);
  383. aFormalParameterList.forEach(fAddIdentifier);
  384. },
  385. /**
  386. * Increments the count of the number of occurrences of the String
  387. * value that is equivalent to the sequence of terminal symbols
  388. * that constitute the encountered identifier name.
  389. * @param {!TSyntacticCodeUnit} oExpression The nonterminal
  390. * MemberExpression.
  391. * @param {string} sIdentifierName The identifier name used as the
  392. * property accessor.
  393. * @return {!Array} The encountered branch of an <abbr title=
  394. * "abstract syntax tree">AST</abbr> with its nonterminal
  395. * MemberExpression traversed.
  396. */
  397. 'dot': function(oExpression, sIdentifierName) {
  398. fCountPrimaryExpression(
  399. EPrimaryExpressionCategories.N_IDENTIFIER_NAMES,
  400. EValuePrefixes.S_STRING + sIdentifierName);
  401. return ['dot', oWalker.walk(oExpression), sIdentifierName];
  402. },
  403. /**
  404. * Adds the optional identifier of the function and its formal
  405. * parameters to the list of identifier names found.
  406. * @param {?string} sIdentifier The optional identifier of the
  407. * function.
  408. * @param {!Array.<string>} aFormalParameterList Formal parameters.
  409. * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
  410. */
  411. 'function': function(
  412. sIdentifier,
  413. aFormalParameterList,
  414. oFunctionBody) {
  415. if ('string' === typeof sIdentifier) {
  416. fAddIdentifier(sIdentifier);
  417. }
  418. aFormalParameterList.forEach(fAddIdentifier);
  419. },
  420. /**
  421. * Either increments the count of the number of occurrences of the
  422. * encountered null or Boolean value or classifies a source element
  423. * as containing the {@code eval} identifier name.
  424. * @param {string} sIdentifier The identifier encountered.
  425. */
  426. 'name': function(sIdentifier) {
  427. if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) {
  428. fCountPrimaryExpression(
  429. EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS,
  430. EValuePrefixes.S_SYMBOLIC + sIdentifier);
  431. } else {
  432. if ('eval' === sIdentifier) {
  433. oSourceElementData.nCategory =
  434. ESourceElementCategories.N_EVAL;
  435. }
  436. fAddIdentifier(sIdentifier);
  437. }
  438. },
  439. /**
  440. * Classifies the source element as excludable if it does not
  441. * contain a {@code with} statement or the {@code eval} identifier
  442. * name.
  443. * @param {TSyntacticCodeUnit} oExpression The expression whose
  444. * value is to be returned.
  445. */
  446. 'return': function(oExpression) {
  447. fClassifyAsExcludable();
  448. },
  449. /**
  450. * Increments the count of the number of occurrences of the
  451. * encountered String value.
  452. * @param {string} sStringValue The String value of the string
  453. * literal encountered.
  454. */
  455. 'string': function(sStringValue) {
  456. if (sStringValue.length > 0) {
  457. fCountPrimaryExpression(
  458. EPrimaryExpressionCategories.N_STRING_LITERALS,
  459. EValuePrefixes.S_STRING + sStringValue);
  460. }
  461. },
  462. /**
  463. * Adds the identifier reserved for an exception to the list of
  464. * identifier names found.
  465. * @param {!TSyntacticCodeUnit} oTry A block of code in which an
  466. * exception can occur.
  467. * @param {Array} aCatch The identifier reserved for an exception
  468. * and a block of code to handle the exception.
  469. * @param {TSyntacticCodeUnit} oFinally An optional block of code
  470. * to be evaluated regardless of whether an exception occurs.
  471. */
  472. 'try': function(oTry, aCatch, oFinally) {
  473. if (Array.isArray(aCatch)) {
  474. fAddIdentifier(aCatch[0]);
  475. }
  476. },
  477. /**
  478. * Classifies the source element as excludable if it does not
  479. * contain a {@code with} statement or the {@code eval} identifier
  480. * name. Adds the identifier of each declared variable to the list
  481. * of identifier names found.
  482. * @param {!Array.<!Array>} aVariableDeclarationList Variable
  483. * declarations.
  484. */
  485. 'var': function(aVariableDeclarationList) {
  486. fClassifyAsExcludable();
  487. aVariableDeclarationList.forEach(fAddVariable);
  488. },
  489. /**
  490. * Classifies a source element as containing the {@code with}
  491. * statement.
  492. * @param {!TSyntacticCodeUnit} oExpression An expression whose
  493. * value is to be converted to a value of type Object and
  494. * become the binding object of a new object environment
  495. * record of a new lexical environment in which the statement
  496. * is to be executed.
  497. * @param {!TSyntacticCodeUnit} oStatement The statement to be
  498. * executed in the augmented lexical environment.
  499. * @return {!Array} An empty array to stop the traversal.
  500. */
  501. 'with': function(oExpression, oStatement) {
  502. oSourceElementData.nCategory = ESourceElementCategories.N_WITH;
  503. return [];
  504. }
  505. /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
  506. },
  507. /**
  508. * A collection of functions used while looking for nested functions.
  509. * @namespace
  510. * @type {!Object.<string, function(...[*])>}
  511. */
  512. oExamineFunctions: {
  513. /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
  514. /**
  515. * Orders an examination of a nested function declaration.
  516. * @this {!TSyntacticCodeUnit} An array-like object representing
  517. * the branch of an <abbr title="abstract syntax tree"
  518. * >AST</abbr> representing the syntactic code unit along with
  519. * its scope.
  520. * @return {!Array} An empty array to stop the traversal.
  521. */
  522. 'defun': function() {
  523. fExamineSyntacticCodeUnit(this);
  524. return [];
  525. },
  526. /**
  527. * Orders an examination of a nested function expression.
  528. * @this {!TSyntacticCodeUnit} An array-like object representing
  529. * the branch of an <abbr title="abstract syntax tree"
  530. * >AST</abbr> representing the syntactic code unit along with
  531. * its scope.
  532. * @return {!Array} An empty array to stop the traversal.
  533. */
  534. 'function': function() {
  535. fExamineSyntacticCodeUnit(this);
  536. return [];
  537. }
  538. /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
  539. }
  540. },
  541. /**
  542. * Records containing data about source elements.
  543. * @type {Array.<TSourceElementsData>}
  544. */
  545. aSourceElementsData = [],
  546. /**
  547. * The index (in the source text order) of the source element
  548. * immediately following a <a href="http://es5.github.com/#x14.1"
  549. * >Directive Prologue</a>.
  550. * @type {number}
  551. */
  552. nAfterDirectivePrologue = 0,
  553. /**
  554. * The index (in the source text order) of the source element that is
  555. * currently being considered.
  556. * @type {number}
  557. */
  558. nPosition,
  559. /**
  560. * The index (in the source text order) of the source element that is
  561. * the last element of the range of source elements that is currently
  562. * being considered.
  563. * @type {(undefined|number)}
  564. */
  565. nTo,
  566. /**
  567. * Initiates the traversal of a source element.
  568. * @param {!TWalker} oWalker An instance of an object that allows the
  569. * traversal of an abstract syntax tree.
  570. * @param {!TSyntacticCodeUnit} oSourceElement A source element from
  571. * which the traversal should commence.
  572. * @return {function(): !TSyntacticCodeUnit} A function that is able to
  573. * initiate the traversal from a given source element.
  574. */
  575. cContext = function(oWalker, oSourceElement) {
  576. /**
  577. * @return {!TSyntacticCodeUnit} A function that is able to
  578. * initiate the traversal from a given source element.
  579. */
  580. var fLambda = function() {
  581. return oWalker.walk(oSourceElement);
  582. };
  583. return fLambda;
  584. },
  585. /**
  586. * Classifies the source element as excludable if it does not
  587. * contain a {@code with} statement or the {@code eval} identifier
  588. * name.
  589. */
  590. fClassifyAsExcludable = function() {
  591. if (oSourceElementData.nCategory ===
  592. ESourceElementCategories.N_OTHER) {
  593. oSourceElementData.nCategory =
  594. ESourceElementCategories.N_EXCLUDABLE;
  595. }
  596. },
  597. /**
  598. * Adds an identifier to the list of identifier names found.
  599. * @param {string} sIdentifier The identifier to be added.
  600. */
  601. fAddIdentifier = function(sIdentifier) {
  602. if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) {
  603. oSourceElementData.aIdentifiers.push(sIdentifier);
  604. }
  605. },
  606. /**
  607. * Adds the identifier of a variable to the list of identifier names
  608. * found.
  609. * @param {!Array} aVariableDeclaration A variable declaration.
  610. */
  611. fAddVariable = function(aVariableDeclaration) {
  612. fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]);
  613. },
  614. /**
  615. * Increments the count of the number of occurrences of the prefixed
  616. * String representation attributed to the primary expression.
  617. * @param {number} nCategory The category of the primary expression.
  618. * @param {string} sName The prefixed String representation attributed
  619. * to the primary expression.
  620. */
  621. fCountPrimaryExpression = function(nCategory, sName) {
  622. if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) {
  623. oSourceElementData.aCount[nCategory][sName] = 0;
  624. if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) {
  625. oSourceElementData.aPrimitiveValues.push(sName);
  626. }
  627. }
  628. oSourceElementData.aCount[nCategory][sName] += 1;
  629. },
  630. /**
  631. * Consolidates all worthwhile primitive values in a range of source
  632. * elements.
  633. * @param {number} nFrom The index (in the source text order) of the
  634. * source element that is the first element of the range.
  635. * @param {number} nTo The index (in the source text order) of the
  636. * source element that is the last element of the range.
  637. * @param {boolean} bEnclose Indicates whether the range should be
  638. * enclosed within a function call with no argument values to a
  639. * function with an empty parameter list if any primitive values
  640. * are consolidated.
  641. * @see TPrimitiveValue#nSaving
  642. */
  643. fExamineSourceElements = function(nFrom, nTo, bEnclose) {
  644. var _,
  645. /**
  646. * The index of the last mangled name.
  647. * @type {number}
  648. */
  649. nIndex = oScope.cname,
  650. /**
  651. * The index of the source element that is currently being
  652. * considered.
  653. * @type {number}
  654. */
  655. nPosition,
  656. /**
  657. * A collection of functions used during the consolidation of
  658. * primitive values and identifier names used as property
  659. * accessors.
  660. * @namespace
  661. * @type {!Object.<string, function(...[*])>}
  662. */
  663. oWalkersTransformers = {
  664. /**
  665. * If the String value that is equivalent to the sequence of
  666. * terminal symbols that constitute the encountered identifier
  667. * name is worthwhile, a syntactic conversion from the dot
  668. * notation to the bracket notation ensues with that sequence
  669. * being substituted by an identifier name to which the value
  670. * is assigned.
  671. * Applies to property accessors that use the dot notation.
  672. * @param {!TSyntacticCodeUnit} oExpression The nonterminal
  673. * MemberExpression.
  674. * @param {string} sIdentifierName The identifier name used as
  675. * the property accessor.
  676. * @return {!Array} A syntactic code unit that is equivalent to
  677. * the one encountered.
  678. * @see TPrimitiveValue#nSaving
  679. */
  680. 'dot': function(oExpression, sIdentifierName) {
  681. /**
  682. * The prefixed String value that is equivalent to the
  683. * sequence of terminal symbols that constitute the
  684. * encountered identifier name.
  685. * @type {string}
  686. */
  687. var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName;
  688. return oSolutionBest.oPrimitiveValues.hasOwnProperty(
  689. sPrefixed) &&
  690. oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
  691. ['sub',
  692. oWalker.walk(oExpression),
  693. ['name',
  694. oSolutionBest.oPrimitiveValues[sPrefixed].sName]] :
  695. ['dot', oWalker.walk(oExpression), sIdentifierName];
  696. },
  697. /**
  698. * If the encountered identifier is a null or Boolean literal
  699. * and its value is worthwhile, the identifier is substituted
  700. * by an identifier name to which that value is assigned.
  701. * Applies to identifier names.
  702. * @param {string} sIdentifier The identifier encountered.
  703. * @return {!Array} A syntactic code unit that is equivalent to
  704. * the one encountered.
  705. * @see TPrimitiveValue#nSaving
  706. */
  707. 'name': function(sIdentifier) {
  708. /**
  709. * The prefixed representation String of the identifier.
  710. * @type {string}
  711. */
  712. var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier;
  713. return [
  714. 'name',
  715. oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) &&
  716. oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
  717. oSolutionBest.oPrimitiveValues[sPrefixed].sName :
  718. sIdentifier
  719. ];
  720. },
  721. /**
  722. * If the encountered String value is worthwhile, it is
  723. * substituted by an identifier name to which that value is
  724. * assigned.
  725. * Applies to String values.
  726. * @param {string} sStringValue The String value of the string
  727. * literal encountered.
  728. * @return {!Array} A syntactic code unit that is equivalent to
  729. * the one encountered.
  730. * @see TPrimitiveValue#nSaving
  731. */
  732. 'string': function(sStringValue) {
  733. /**
  734. * The prefixed representation String of the primitive value
  735. * of the literal.
  736. * @type {string}
  737. */
  738. var sPrefixed =
  739. EValuePrefixes.S_STRING + sStringValue;
  740. return oSolutionBest.oPrimitiveValues.hasOwnProperty(
  741. sPrefixed) &&
  742. oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
  743. ['name',
  744. oSolutionBest.oPrimitiveValues[sPrefixed].sName] :
  745. ['string', sStringValue];
  746. }
  747. },
  748. /**
  749. * Such data on what to consolidate within the range of source
  750. * elements that is currently being considered that lead to the
  751. * greatest known reduction of the number of the terminal symbols
  752. * in comparison to the original source text.
  753. * @type {!TSolution}
  754. */
  755. oSolutionBest = new TSolution(),
  756. /**
  757. * Data representing an ongoing attempt to find a better
  758. * reduction of the number of the terminal symbols in comparison
  759. * to the original source text than the best one that is
  760. * currently known.
  761. * @type {!TSolution}
  762. * @see oSolutionBest
  763. */
  764. oSolutionCandidate = new TSolution(),
  765. /**
  766. * A record consisting of data about the range of source elements
  767. * that is currently being examined.
  768. * @type {!TSourceElementsData}
  769. */
  770. oSourceElementsData = new TSourceElementsData(),
  771. /**
  772. * Variable declarations for each primitive value that is to be
  773. * consolidated within the elements.
  774. * @type {!Array.<!Array>}
  775. */
  776. aVariableDeclarations = [],
  777. /**
  778. * Augments a list with a prefixed representation String.
  779. * @param {!Array.<string>} aList A list that is to be augmented.
  780. * @return {function(string)} A function that augments a list
  781. * with a prefixed representation String.
  782. */
  783. cAugmentList = function(aList) {
  784. /**
  785. * @param {string} sPrefixed Prefixed representation String of
  786. * a primitive value that could be consolidated within the
  787. * elements.
  788. */
  789. var fLambda = function(sPrefixed) {
  790. if (-1 === aList.indexOf(sPrefixed)) {
  791. aList.push(sPrefixed);
  792. }
  793. };
  794. return fLambda;
  795. },
  796. /**
  797. * Adds the number of occurrences of a primitive value of a given
  798. * category that could be consolidated in the source element with
  799. * a given index to the count of occurrences of that primitive
  800. * value within the range of source elements that is currently
  801. * being considered.
  802. * @param {number} nPosition The index (in the source text order)
  803. * of a source element.
  804. * @param {number} nCategory The category of the primary
  805. * expression from which the primitive value is derived.
  806. * @return {function(string)} A function that performs the
  807. * addition.
  808. * @see cAddOccurrencesInCategory
  809. */
  810. cAddOccurrences = function(nPosition, nCategory) {
  811. /**
  812. * @param {string} sPrefixed The prefixed representation String
  813. * of a primitive value.
  814. */
  815. var fLambda = function(sPrefixed) {
  816. if (!oSourceElementsData.aCount[nCategory].hasOwnProperty(
  817. sPrefixed)) {
  818. oSourceElementsData.aCount[nCategory][sPrefixed] = 0;
  819. }
  820. oSourceElementsData.aCount[nCategory][sPrefixed] +=
  821. aSourceElementsData[nPosition].aCount[nCategory][
  822. sPrefixed];
  823. };
  824. return fLambda;
  825. },
  826. /**
  827. * Adds the number of occurrences of each primitive value of a
  828. * given category that could be consolidated in the source
  829. * element with a given index to the count of occurrences of that
  830. * primitive values within the range of source elements that is
  831. * currently being considered.
  832. * @param {number} nPosition The index (in the source text order)
  833. * of a source element.
  834. * @return {function(number)} A function that performs the
  835. * addition.
  836. * @see fAddOccurrences
  837. */
  838. cAddOccurrencesInCategory = function(nPosition) {
  839. /**
  840. * @param {number} nCategory The category of the primary
  841. * expression from which the primitive value is derived.
  842. */
  843. var fLambda = function(nCategory) {
  844. Object.keys(
  845. aSourceElementsData[nPosition].aCount[nCategory]
  846. ).forEach(cAddOccurrences(nPosition, nCategory));
  847. };
  848. return fLambda;
  849. },
  850. /**
  851. * Adds the number of occurrences of each primitive value that
  852. * could be consolidated in the source element with a given index
  853. * to the count of occurrences of that primitive values within
  854. * the range of source elements that is currently being
  855. * considered.
  856. * @param {number} nPosition The index (in the source text order)
  857. * of a source element.
  858. */
  859. fAddOccurrences = function(nPosition) {
  860. Object.keys(aSourceElementsData[nPosition].aCount).forEach(
  861. cAddOccurrencesInCategory(nPosition));
  862. },
  863. /**
  864. * Creates a variable declaration for a primitive value if that
  865. * primitive value is to be consolidated within the elements.
  866. * @param {string} sPrefixed Prefixed representation String of a
  867. * primitive value that could be consolidated within the
  868. * elements.
  869. * @see aVariableDeclarations
  870. */
  871. cAugmentVariableDeclarations = function(sPrefixed) {
  872. if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) {
  873. aVariableDeclarations.push([
  874. oSolutionBest.oPrimitiveValues[sPrefixed].sName,
  875. [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ?
  876. 'name' : 'string',
  877. sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)]
  878. ]);
  879. }
  880. },
  881. /**
  882. * Sorts primitive values with regard to the difference in the
  883. * number of terminal symbols between the original source text
  884. * and the one with those primitive values consolidated.
  885. * @param {string} sPrefixed0 The prefixed representation String
  886. * of the first of the two primitive values that are being
  887. * compared.
  888. * @param {string} sPrefixed1 The prefixed representation String
  889. * of the second of the two primitive values that are being
  890. * compared.
  891. * @return {number}
  892. * <dl>
  893. * <dt>-1</dt>
  894. * <dd>if the first primitive value must be placed before
  895. * the other one,</dd>
  896. * <dt>0</dt>
  897. * <dd>if the first primitive value may be placed before
  898. * the other one,</dd>
  899. * <dt>1</dt>
  900. * <dd>if the first primitive value must not be placed
  901. * before the other one.</dd>
  902. * </dl>
  903. * @see TSolution.oPrimitiveValues
  904. */
  905. cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) {
  906. /**
  907. * The difference between:
  908. * <ol>
  909. * <li>the difference in the number of terminal symbols
  910. * between the original source text and the one with the
  911. * first primitive value consolidated, and</li>
  912. * <li>the difference in the number of terminal symbols
  913. * between the original source text and the one with the
  914. * second primitive value consolidated.</li>
  915. * </ol>
  916. * @type {number}
  917. */
  918. var nDifference =
  919. oSolutionCandidate.oPrimitiveValues[sPrefixed0].nSaving -
  920. oSolutionCandidate.oPrimitiveValues[sPrefixed1].nSaving;
  921. return nDifference > 0 ? -1 : nDifference < 0 ? 1 : 0;
  922. },
  923. /**
  924. * Assigns an identifier name to a primitive value and calculates
  925. * whether instances of that primitive value are worth
  926. * consolidating.
  927. * @param {string} sPrefixed The prefixed representation String
  928. * of a primitive value that is being evaluated.
  929. */
  930. fEvaluatePrimitiveValue = function(sPrefixed) {
  931. var _,
  932. /**
  933. * The index of the last mangled name.
  934. * @type {number}
  935. */
  936. nIndex,
  937. /**
  938. * The representation String of the primitive value that is
  939. * being evaluated.
  940. * @type {string}
  941. */
  942. sName =
  943. sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length),
  944. /**
  945. * The number of source characters taken up by the
  946. * representation String of the primitive value that is
  947. * being evaluated.
  948. * @type {number}
  949. */
  950. nLengthOriginal = sName.length,
  951. /**
  952. * The number of source characters taken up by the
  953. * identifier name that could substitute the primitive
  954. * value that is being evaluated.
  955. * substituted.
  956. * @type {number}
  957. */
  958. nLengthSubstitution,
  959. /**
  960. * The number of source characters taken up by by the
  961. * representation String of the primitive value that is
  962. * being evaluated when it is represented by a string
  963. * literal.
  964. * @type {number}
  965. */
  966. nLengthString = oProcessor.make_string(sName).length;
  967. oSolutionCandidate.oPrimitiveValues[sPrefixed] =
  968. new TPrimitiveValue();
  969. do { // Find an identifier unused in this or any nested scope.
  970. nIndex = oScope.cname;
  971. oSolutionCandidate.oPrimitiveValues[sPrefixed].sName =
  972. oScope.next_mangled();
  973. } while (-1 !== oSourceElementsData.aIdentifiers.indexOf(
  974. oSolutionCandidate.oPrimitiveValues[sPrefixed].sName));
  975. nLengthSubstitution = oSolutionCandidate.oPrimitiveValues[
  976. sPrefixed].sName.length;
  977. if (0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC)) {
  978. // foo:null, or foo:null;
  979. oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
  980. nLengthSubstitution + nLengthOriginal +
  981. oWeights.N_VARIABLE_DECLARATION;
  982. // null vs foo
  983. oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
  984. oSourceElementsData.aCount[
  985. EPrimaryExpressionCategories.
  986. N_NULL_AND_BOOLEAN_LITERALS][sPrefixed] *
  987. (nLengthOriginal - nLengthSubstitution);
  988. } else {
  989. // foo:'fromCharCode';
  990. oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
  991. nLengthSubstitution + nLengthString +
  992. oWeights.N_VARIABLE_DECLARATION;
  993. // .fromCharCode vs [foo]
  994. if (oSourceElementsData.aCount[
  995. EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
  996. ].hasOwnProperty(sPrefixed)) {
  997. oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
  998. oSourceElementsData.aCount[
  999. EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
  1000. ][sPrefixed] *
  1001. (nLengthOriginal - nLengthSubstitution -
  1002. oWeights.N_PROPERTY_ACCESSOR);
  1003. }
  1004. // 'fromCharCode' vs foo
  1005. if (oSourceElementsData.aCount[
  1006. EPrimaryExpressionCategories.N_STRING_LITERALS
  1007. ].hasOwnProperty(sPrefixed)) {
  1008. oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
  1009. oSourceElementsData.aCount[
  1010. EPrimaryExpressionCategories.N_STRING_LITERALS
  1011. ][sPrefixed] *
  1012. (nLengthString - nLengthSubstitution);
  1013. }
  1014. }
  1015. if (oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving >
  1016. 0) {
  1017. oSolutionCandidate.nSavings +=
  1018. oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving;
  1019. } else {
  1020. oScope.cname = nIndex; // Free the identifier name.
  1021. }
  1022. },
  1023. /**
  1024. * Adds a variable declaration to an existing variable statement.
  1025. * @param {!Array} aVariableDeclaration A variable declaration
  1026. * with an initialiser.
  1027. */
  1028. cAddVariableDeclaration = function(aVariableDeclaration) {
  1029. (/** @type {!Array} */ oSourceElements[nFrom][1]).unshift(
  1030. aVariableDeclaration);
  1031. };
  1032. if (nFrom > nTo) {
  1033. return;
  1034. }
  1035. // If the range is a closure, reuse the closure.
  1036. if (nFrom === nTo &&
  1037. 'stat' === oSourceElements[nFrom][0] &&
  1038. 'call' === oSourceElements[nFrom][1][0] &&
  1039. 'function' === oSourceElements[nFrom][1][1][0]) {
  1040. fExamineSyntacticCodeUnit(oSourceElements[nFrom][1][1]);
  1041. return;
  1042. }
  1043. // Create a list of all derived primitive values within the range.
  1044. for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
  1045. aSourceElementsData[nPosition].aPrimitiveValues.forEach(
  1046. cAugmentList(oSourceElementsData.aPrimitiveValues));
  1047. }
  1048. if (0 === oSourceElementsData.aPrimitiveValues.length) {
  1049. return;
  1050. }
  1051. for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
  1052. // Add the number of occurrences to the total count.
  1053. fAddOccurrences(nPosition);
  1054. // Add identifiers of this or any nested scope to the list.
  1055. aSourceElementsData[nPosition].aIdentifiers.forEach(
  1056. cAugmentList(oSourceElementsData.aIdentifiers));
  1057. }
  1058. // Distribute identifier names among derived primitive values.
  1059. do { // If there was any progress, find a better distribution.
  1060. oSolutionBest = oSolutionCandidate;
  1061. if (Object.keys(oSolutionCandidate.oPrimitiveValues).length > 0) {
  1062. // Sort primitive values descending by their worthwhileness.
  1063. oSourceElementsData.aPrimitiveValues.sort(cSortPrimitiveValues);
  1064. }
  1065. oSolutionCandidate = new TSolution();
  1066. oSourceElementsData.aPrimitiveValues.forEach(
  1067. fEvaluatePrimitiveValue);
  1068. oScope.cname = nIndex;
  1069. } while (oSolutionCandidate.nSavings > oSolutionBest.nSavings);
  1070. // Take the necessity of adding a variable statement into account.
  1071. if ('var' !== oSourceElements[nFrom][0]) {
  1072. oSolutionBest.nSavings -= oWeights.N_VARIABLE_STATEMENT_AFFIXATION;
  1073. }
  1074. if (bEnclose) {
  1075. // Take the necessity of forming a closure into account.
  1076. oSolutionBest.nSavings -= oWeights.N_CLOSURE;
  1077. }
  1078. if (oSolutionBest.nSavings > 0) {
  1079. // Create variable declarations suitable for UglifyJS.
  1080. Object.keys(oSolutionBest.oPrimitiveValues).forEach(
  1081. cAugmentVariableDeclarations);
  1082. // Rewrite expressions that contain worthwhile primitive values.
  1083. for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
  1084. oWalker = oProcessor.ast_walker();
  1085. oSourceElements[nPosition] =
  1086. oWalker.with_walkers(
  1087. oWalkersTransformers,
  1088. cContext(oWalker, oSourceElements[nPosition]));
  1089. }
  1090. if ('var' === oSourceElements[nFrom][0]) { // Reuse the statement.
  1091. (/** @type {!Array.<!Array>} */ aVariableDeclarations.reverse(
  1092. )).forEach(cAddVariableDeclaration);
  1093. } else { // Add a variable statement.
  1094. Array.prototype.splice.call(
  1095. oSourceElements,
  1096. nFrom,
  1097. 0,
  1098. ['var', aVariableDeclarations]);
  1099. nTo += 1;
  1100. }
  1101. if (bEnclose) {
  1102. // Add a closure.
  1103. Array.prototype.splice.call(
  1104. oSourceElements,
  1105. nFrom,
  1106. 0,
  1107. ['stat', ['call', ['function', null, [], []], []]]);
  1108. // Copy source elements into the closure.
  1109. for (nPosition = nTo + 1; nPosition > nFrom; nPosition -= 1) {
  1110. Array.prototype.unshift.call(
  1111. oSourceElements[nFrom][1][1][3],
  1112. oSourceElements[nPosition]);
  1113. }
  1114. // Remove source elements outside the closure.
  1115. Array.prototype.splice.call(
  1116. oSourceElements,
  1117. nFrom + 1,
  1118. nTo - nFrom + 1);
  1119. }
  1120. }
  1121. if (bEnclose) {
  1122. // Restore the availability of identifier names.
  1123. oScope.cname = nIndex;
  1124. }
  1125. };
  1126. oSourceElements = (/** @type {!TSyntacticCodeUnit} */
  1127. oSyntacticCodeUnit[bIsGlobal ? 1 : 3]);
  1128. if (0 === oSourceElements.length) {
  1129. return;
  1130. }
  1131. oScope = bIsGlobal ? oSyntacticCodeUnit.scope : oSourceElements.scope;
  1132. // Skip a Directive Prologue.
  1133. while (nAfterDirectivePrologue < oSourceElements.length &&
  1134. 'directive' === oSourceElements[nAfterDirectivePrologue][0]) {
  1135. nAfterDirectivePrologue += 1;
  1136. aSourceElementsData.push(null);
  1137. }
  1138. if (oSourceElements.length === nAfterDirectivePrologue) {
  1139. return;
  1140. }
  1141. for (nPosition = nAfterDirectivePrologue;
  1142. nPosition < oSourceElements.length;
  1143. nPosition += 1) {
  1144. oSourceElementData = new TSourceElementsData();
  1145. oWalker = oProcessor.ast_walker();
  1146. // Classify a source element.
  1147. // Find its derived primitive values and count their occurrences.
  1148. // Find all identifiers used (including nested scopes).
  1149. oWalker.with_walkers(
  1150. oWalkers.oSurveySourceElement,
  1151. cContext(oWalker, oSourceElements[nPosition]));
  1152. // Establish whether the scope is still wholly examinable.
  1153. bIsWhollyExaminable = bIsWhollyExaminable &&
  1154. ESourceElementCategories.N_WITH !== oSourceElementData.nCategory &&
  1155. ESourceElementCategories.N_EVAL !== oSourceElementData.nCategory;
  1156. aSourceElementsData.push(oSourceElementData);
  1157. }
  1158. if (bIsWhollyExaminable) { // Examine the whole scope.
  1159. fExamineSourceElements(
  1160. nAfterDirectivePrologue,
  1161. oSourceElements.length - 1,
  1162. false);
  1163. } else { // Examine unexcluded ranges of source elements.
  1164. for (nPosition = oSourceElements.length - 1;
  1165. nPosition >= nAfterDirectivePrologue;
  1166. nPosition -= 1) {
  1167. oSourceElementData = (/** @type {!TSourceElementsData} */
  1168. aSourceElementsData[nPosition]);
  1169. if (ESourceElementCategories.N_OTHER ===
  1170. oSourceElementData.nCategory) {
  1171. if ('undefined' === typeof nTo) {
  1172. nTo = nPosition; // Indicate the end of a range.
  1173. }
  1174. // Examine the range if it immediately follows a Directive Prologue.
  1175. if (nPosition === nAfterDirectivePrologue) {
  1176. fExamineSourceElements(nPosition, nTo, true);
  1177. }
  1178. } else {
  1179. if ('undefined' !== typeof nTo) {
  1180. // Examine the range that immediately follows this source element.
  1181. fExamineSourceElements(nPosition + 1, nTo, true);
  1182. nTo = void 0; // Obliterate the range.
  1183. }
  1184. // Examine nested functions.
  1185. oWalker = oProcessor.ast_walker();
  1186. oWalker.with_walkers(
  1187. oWalkers.oExamineFunctions,
  1188. cContext(oWalker, oSourceElements[nPosition]));
  1189. }
  1190. }
  1191. }
  1192. }(oAbstractSyntaxTree = oProcessor.ast_add_scope(oAbstractSyntaxTree)));
  1193. return oAbstractSyntaxTree;
  1194. };
  1195. /*jshint sub:false */
  1196. /* Local Variables: */
  1197. /* mode: js */
  1198. /* coding: utf-8 */
  1199. /* indent-tabs-mode: nil */
  1200. /* tab-width: 2 */
  1201. /* End: */
  1202. /* vim: set ft=javascript fenc=utf-8 et ts=2 sts=2 sw=2: */
  1203. /* :mode=javascript:noTabs=true:tabSize=2:indentSize=2:deepIndent=true: */