123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221 |
- /**
- * @preserve Copyright 2012 Robert Gust-Bardon <http://robert.gust-bardon.org/>.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- /**
- * @fileoverview Enhances <a href="https://github.com/mishoo/UglifyJS/"
- * >UglifyJS</a> with consolidation of null, Boolean, and String values.
- * <p>Also known as aliasing, this feature has been deprecated in <a href=
- * "http://closure-compiler.googlecode.com/">the Closure Compiler</a> since its
- * initial release, where it is unavailable from the <abbr title=
- * "command line interface">CLI</a>. The Closure Compiler allows one to log and
- * influence this process. In contrast, this implementation does not introduce
- * any variable declarations in global code and derives String values from
- * identifier names used as property accessors.</p>
- * <p>Consolidating literals may worsen the data compression ratio when an <a
- * href="http://tools.ietf.org/html/rfc2616#section-3.5">encoding
- * transformation</a> is applied. For instance, <a href=
- * "http://code.jquery.com/jquery-1.7.1.js">jQuery 1.7.1</a> takes 248235 bytes.
- * Building it with <a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">
- * UglifyJS v1.2.5</a> results in 93647 bytes (37.73% of the original) which are
- * then compressed to 33154 bytes (13.36% of the original) using <a href=
- * "http://linux.die.net/man/1/gzip">gzip(1)</a>. Building it with the same
- * version of UglifyJS 1.2.5 patched with the implementation of consolidation
- * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison
- * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes
- * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned
- * 33154 bytes).</p>
- * <p>Written in <a href="http://es5.github.com/#x4.2.2">the strict variant</a>
- * of <a href="http://es5.github.com/">ECMA-262 5.1 Edition</a>. Encoded in <a
- * href="http://tools.ietf.org/html/rfc3629">UTF-8</a>. Follows <a href=
- * "http://google-styleguide.googlecode.com/svn-history/r76/trunk/javascriptguide.xml"
- * >Revision 2.28 of the Google JavaScript Style Guide</a> (except for the
- * discouraged use of the {@code function} tag and the {@code namespace} tag).
- * 100% typed for the <a href=
- * "http://closure-compiler.googlecode.com/files/compiler-20120123.tar.gz"
- * >Closure Compiler Version 1741</a>.</p>
- * <p>Should you find this software useful, please consider <a href=
- * "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JZLW72X8FD4WG"
- * >a donation</a>.</p>
- * @author follow.me@RGustBardon (Robert Gust-Bardon)
- * @supported Tested with:
- * <ul>
- * <li><a href="http://nodejs.org/dist/v0.6.10/">Node v0.6.10</a>,</li>
- * <li><a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">UglifyJS
- * v1.2.5</a>.</li>
- * </ul>
- */
- /*global console:false, exports:true, module:false, require:false */
- /*jshint sub:true */
- /**
- * Consolidates null, Boolean, and String values found inside an <abbr title=
- * "abstract syntax tree">AST</abbr>.
- * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object
- * representing an <abbr title="abstract syntax tree">AST</abbr>.
- * @return {!TSyntacticCodeUnit} An array-like object representing an <abbr
- * title="abstract syntax tree">AST</abbr> with its null, Boolean, and
- * String values consolidated.
- */
- // TODO(user) Consolidation of mathematical values found in numeric literals.
- // TODO(user) Unconsolidation.
- // TODO(user) Consolidation of ECMA-262 6th Edition programs.
- // TODO(user) Rewrite in ECMA-262 6th Edition.
- exports['ast_consolidate'] = function(oAbstractSyntaxTree) {
- 'use strict';
- /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
- latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
- onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
- sub:false, trailing:true */
- var _,
- /**
- * A record consisting of data about one or more source elements.
- * @constructor
- * @nosideeffects
- */
- TSourceElementsData = function() {
- /**
- * The category of the elements.
- * @type {number}
- * @see ESourceElementCategories
- */
- this.nCategory = ESourceElementCategories.N_OTHER;
- /**
- * The number of occurrences (within the elements) of each primitive
- * value that could be consolidated.
- * @type {!Array.<!Object.<string, number>>}
- */
- this.aCount = [];
- this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {};
- this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {};
- this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] =
- {};
- /**
- * Identifier names found within the elements.
- * @type {!Array.<string>}
- */
- this.aIdentifiers = [];
- /**
- * Prefixed representation Strings of each primitive value that could be
- * consolidated within the elements.
- * @type {!Array.<string>}
- */
- this.aPrimitiveValues = [];
- },
- /**
- * A record consisting of data about a primitive value that could be
- * consolidated.
- * @constructor
- * @nosideeffects
- */
- TPrimitiveValue = function() {
- /**
- * The difference in the number of terminal symbols between the original
- * source text and the one with the primitive value consolidated. If the
- * difference is positive, the primitive value is considered worthwhile.
- * @type {number}
- */
- this.nSaving = 0;
- /**
- * An identifier name of the variable that will be declared and assigned
- * the primitive value if the primitive value is consolidated.
- * @type {string}
- */
- this.sName = '';
- },
- /**
- * A record consisting of data on what to consolidate within the range of
- * source elements that is currently being considered.
- * @constructor
- * @nosideeffects
- */
- TSolution = function() {
- /**
- * An object whose keys are prefixed representation Strings of each
- * primitive value that could be consolidated within the elements and
- * whose values are corresponding data about those primitive values.
- * @type {!Object.<string, {nSaving: number, sName: string}>}
- * @see TPrimitiveValue
- */
- this.oPrimitiveValues = {};
- /**
- * The difference in the number of terminal symbols between the original
- * source text and the one with all the worthwhile primitive values
- * consolidated.
- * @type {number}
- * @see TPrimitiveValue#nSaving
- */
- this.nSavings = 0;
- },
- /**
- * The processor of <abbr title="abstract syntax tree">AST</abbr>s found
- * in UglifyJS.
- * @namespace
- * @type {!TProcessor}
- */
- oProcessor = (/** @type {!TProcessor} */ require('./process')),
- /**
- * A record consisting of a number of constants that represent the
- * difference in the number of terminal symbols between a source text with
- * a modified syntactic code unit and the original one.
- * @namespace
- * @type {!Object.<string, number>}
- */
- oWeights = {
- /**
- * The difference in the number of punctuators required by the bracket
- * notation and the dot notation.
- * <p><code>'[]'.length - '.'.length</code></p>
- * @const
- * @type {number}
- */
- N_PROPERTY_ACCESSOR: 1,
- /**
- * The number of punctuators required by a variable declaration with an
- * initialiser.
- * <p><code>':'.length + ';'.length</code></p>
- * @const
- * @type {number}
- */
- N_VARIABLE_DECLARATION: 2,
- /**
- * The number of terminal symbols required to introduce a variable
- * statement (excluding its variable declaration list).
- * <p><code>'var '.length</code></p>
- * @const
- * @type {number}
- */
- N_VARIABLE_STATEMENT_AFFIXATION: 4,
- /**
- * The number of terminal symbols needed to enclose source elements
- * within a function call with no argument values to a function with an
- * empty parameter list.
- * <p><code>'(function(){}());'.length</code></p>
- * @const
- * @type {number}
- */
- N_CLOSURE: 17
- },
- /**
- * Categories of primary expressions from which primitive values that
- * could be consolidated are derivable.
- * @namespace
- * @enum {number}
- */
- EPrimaryExpressionCategories = {
- /**
- * Identifier names used as property accessors.
- * @type {number}
- */
- N_IDENTIFIER_NAMES: 0,
- /**
- * String literals.
- * @type {number}
- */
- N_STRING_LITERALS: 1,
- /**
- * Null and Boolean literals.
- * @type {number}
- */
- N_NULL_AND_BOOLEAN_LITERALS: 2
- },
- /**
- * Prefixes of primitive values that could be consolidated.
- * The String values of the prefixes must have same number of characters.
- * The prefixes must not be used in any properties defined in any version
- * of <a href=
- * "http://www.ecma-international.org/publications/standards/Ecma-262.htm"
- * >ECMA-262</a>.
- * @namespace
- * @enum {string}
- */
- EValuePrefixes = {
- /**
- * Identifies String values.
- * @type {string}
- */
- S_STRING: '#S',
- /**
- * Identifies null and Boolean values.
- * @type {string}
- */
- S_SYMBOLIC: '#O'
- },
- /**
- * Categories of source elements in terms of their appropriateness of
- * having their primitive values consolidated.
- * @namespace
- * @enum {number}
- */
- ESourceElementCategories = {
- /**
- * Identifies a source element that includes the <a href=
- * "http://es5.github.com/#x12.10">{@code with}</a> statement.
- * @type {number}
- */
- N_WITH: 0,
- /**
- * Identifies a source element that includes the <a href=
- * "http://es5.github.com/#x15.1.2.1">{@code eval}</a> identifier name.
- * @type {number}
- */
- N_EVAL: 1,
- /**
- * Identifies a source element that must be excluded from the process
- * unless its whole scope is examined.
- * @type {number}
- */
- N_EXCLUDABLE: 2,
- /**
- * Identifies source elements not posing any problems.
- * @type {number}
- */
- N_OTHER: 3
- },
- /**
- * The list of literals (other than the String ones) whose primitive
- * values can be consolidated.
- * @const
- * @type {!Array.<string>}
- */
- A_OTHER_SUBSTITUTABLE_LITERALS = [
- 'null', // The null literal.
- 'false', // The Boolean literal {@code false}.
- 'true' // The Boolean literal {@code true}.
- ];
- (/**
- * Consolidates all worthwhile primitive values in a syntactic code unit.
- * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object
- * representing the branch of the abstract syntax tree representing the
- * syntactic code unit along with its scope.
- * @see TPrimitiveValue#nSaving
- */
- function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) {
- var _,
- /**
- * Indicates whether the syntactic code unit represents global code.
- * @type {boolean}
- */
- bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0],
- /**
- * Indicates whether the whole scope is being examined.
- * @type {boolean}
- */
- bIsWhollyExaminable = !bIsGlobal,
- /**
- * An array-like object representing source elements that constitute a
- * syntactic code unit.
- * @type {!TSyntacticCodeUnit}
- */
- oSourceElements,
- /**
- * A record consisting of data about the source element that is
- * currently being examined.
- * @type {!TSourceElementsData}
- */
- oSourceElementData,
- /**
- * The scope of the syntactic code unit.
- * @type {!TScope}
- */
- oScope,
- /**
- * An instance of an object that allows the traversal of an <abbr
- * title="abstract syntax tree">AST</abbr>.
- * @type {!TWalker}
- */
- oWalker,
- /**
- * An object encompassing collections of functions used during the
- * traversal of an <abbr title="abstract syntax tree">AST</abbr>.
- * @namespace
- * @type {!Object.<string, !Object.<string, function(...[*])>>}
- */
- oWalkers = {
- /**
- * A collection of functions used during the surveyance of source
- * elements.
- * @namespace
- * @type {!Object.<string, function(...[*])>}
- */
- oSurveySourceElement: {
- /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name. Adds the identifier of the function and its formal
- * parameters to the list of identifier names found.
- * @param {string} sIdentifier The identifier of the function.
- * @param {!Array.<string>} aFormalParameterList Formal parameters.
- * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
- */
- 'defun': function(
- sIdentifier,
- aFormalParameterList,
- oFunctionBody) {
- fClassifyAsExcludable();
- fAddIdentifier(sIdentifier);
- aFormalParameterList.forEach(fAddIdentifier);
- },
- /**
- * Increments the count of the number of occurrences of the String
- * value that is equivalent to the sequence of terminal symbols
- * that constitute the encountered identifier name.
- * @param {!TSyntacticCodeUnit} oExpression The nonterminal
- * MemberExpression.
- * @param {string} sIdentifierName The identifier name used as the
- * property accessor.
- * @return {!Array} The encountered branch of an <abbr title=
- * "abstract syntax tree">AST</abbr> with its nonterminal
- * MemberExpression traversed.
- */
- 'dot': function(oExpression, sIdentifierName) {
- fCountPrimaryExpression(
- EPrimaryExpressionCategories.N_IDENTIFIER_NAMES,
- EValuePrefixes.S_STRING + sIdentifierName);
- return ['dot', oWalker.walk(oExpression), sIdentifierName];
- },
- /**
- * Adds the optional identifier of the function and its formal
- * parameters to the list of identifier names found.
- * @param {?string} sIdentifier The optional identifier of the
- * function.
- * @param {!Array.<string>} aFormalParameterList Formal parameters.
- * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
- */
- 'function': function(
- sIdentifier,
- aFormalParameterList,
- oFunctionBody) {
- if ('string' === typeof sIdentifier) {
- fAddIdentifier(sIdentifier);
- }
- aFormalParameterList.forEach(fAddIdentifier);
- },
- /**
- * Either increments the count of the number of occurrences of the
- * encountered null or Boolean value or classifies a source element
- * as containing the {@code eval} identifier name.
- * @param {string} sIdentifier The identifier encountered.
- */
- 'name': function(sIdentifier) {
- if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) {
- fCountPrimaryExpression(
- EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS,
- EValuePrefixes.S_SYMBOLIC + sIdentifier);
- } else {
- if ('eval' === sIdentifier) {
- oSourceElementData.nCategory =
- ESourceElementCategories.N_EVAL;
- }
- fAddIdentifier(sIdentifier);
- }
- },
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name.
- * @param {TSyntacticCodeUnit} oExpression The expression whose
- * value is to be returned.
- */
- 'return': function(oExpression) {
- fClassifyAsExcludable();
- },
- /**
- * Increments the count of the number of occurrences of the
- * encountered String value.
- * @param {string} sStringValue The String value of the string
- * literal encountered.
- */
- 'string': function(sStringValue) {
- if (sStringValue.length > 0) {
- fCountPrimaryExpression(
- EPrimaryExpressionCategories.N_STRING_LITERALS,
- EValuePrefixes.S_STRING + sStringValue);
- }
- },
- /**
- * Adds the identifier reserved for an exception to the list of
- * identifier names found.
- * @param {!TSyntacticCodeUnit} oTry A block of code in which an
- * exception can occur.
- * @param {Array} aCatch The identifier reserved for an exception
- * and a block of code to handle the exception.
- * @param {TSyntacticCodeUnit} oFinally An optional block of code
- * to be evaluated regardless of whether an exception occurs.
- */
- 'try': function(oTry, aCatch, oFinally) {
- if (Array.isArray(aCatch)) {
- fAddIdentifier(aCatch[0]);
- }
- },
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name. Adds the identifier of each declared variable to the list
- * of identifier names found.
- * @param {!Array.<!Array>} aVariableDeclarationList Variable
- * declarations.
- */
- 'var': function(aVariableDeclarationList) {
- fClassifyAsExcludable();
- aVariableDeclarationList.forEach(fAddVariable);
- },
- /**
- * Classifies a source element as containing the {@code with}
- * statement.
- * @param {!TSyntacticCodeUnit} oExpression An expression whose
- * value is to be converted to a value of type Object and
- * become the binding object of a new object environment
- * record of a new lexical environment in which the statement
- * is to be executed.
- * @param {!TSyntacticCodeUnit} oStatement The statement to be
- * executed in the augmented lexical environment.
- * @return {!Array} An empty array to stop the traversal.
- */
- 'with': function(oExpression, oStatement) {
- oSourceElementData.nCategory = ESourceElementCategories.N_WITH;
- return [];
- }
- /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- },
- /**
- * A collection of functions used while looking for nested functions.
- * @namespace
- * @type {!Object.<string, function(...[*])>}
- */
- oExamineFunctions: {
- /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- /**
- * Orders an examination of a nested function declaration.
- * @this {!TSyntacticCodeUnit} An array-like object representing
- * the branch of an <abbr title="abstract syntax tree"
- * >AST</abbr> representing the syntactic code unit along with
- * its scope.
- * @return {!Array} An empty array to stop the traversal.
- */
- 'defun': function() {
- fExamineSyntacticCodeUnit(this);
- return [];
- },
- /**
- * Orders an examination of a nested function expression.
- * @this {!TSyntacticCodeUnit} An array-like object representing
- * the branch of an <abbr title="abstract syntax tree"
- * >AST</abbr> representing the syntactic code unit along with
- * its scope.
- * @return {!Array} An empty array to stop the traversal.
- */
- 'function': function() {
- fExamineSyntacticCodeUnit(this);
- return [];
- }
- /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- }
- },
- /**
- * Records containing data about source elements.
- * @type {Array.<TSourceElementsData>}
- */
- aSourceElementsData = [],
- /**
- * The index (in the source text order) of the source element
- * immediately following a <a href="http://es5.github.com/#x14.1"
- * >Directive Prologue</a>.
- * @type {number}
- */
- nAfterDirectivePrologue = 0,
- /**
- * The index (in the source text order) of the source element that is
- * currently being considered.
- * @type {number}
- */
- nPosition,
- /**
- * The index (in the source text order) of the source element that is
- * the last element of the range of source elements that is currently
- * being considered.
- * @type {(undefined|number)}
- */
- nTo,
- /**
- * Initiates the traversal of a source element.
- * @param {!TWalker} oWalker An instance of an object that allows the
- * traversal of an abstract syntax tree.
- * @param {!TSyntacticCodeUnit} oSourceElement A source element from
- * which the traversal should commence.
- * @return {function(): !TSyntacticCodeUnit} A function that is able to
- * initiate the traversal from a given source element.
- */
- cContext = function(oWalker, oSourceElement) {
- /**
- * @return {!TSyntacticCodeUnit} A function that is able to
- * initiate the traversal from a given source element.
- */
- var fLambda = function() {
- return oWalker.walk(oSourceElement);
- };
- return fLambda;
- },
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name.
- */
- fClassifyAsExcludable = function() {
- if (oSourceElementData.nCategory ===
- ESourceElementCategories.N_OTHER) {
- oSourceElementData.nCategory =
- ESourceElementCategories.N_EXCLUDABLE;
- }
- },
- /**
- * Adds an identifier to the list of identifier names found.
- * @param {string} sIdentifier The identifier to be added.
- */
- fAddIdentifier = function(sIdentifier) {
- if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) {
- oSourceElementData.aIdentifiers.push(sIdentifier);
- }
- },
- /**
- * Adds the identifier of a variable to the list of identifier names
- * found.
- * @param {!Array} aVariableDeclaration A variable declaration.
- */
- fAddVariable = function(aVariableDeclaration) {
- fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]);
- },
- /**
- * Increments the count of the number of occurrences of the prefixed
- * String representation attributed to the primary expression.
- * @param {number} nCategory The category of the primary expression.
- * @param {string} sName The prefixed String representation attributed
- * to the primary expression.
- */
- fCountPrimaryExpression = function(nCategory, sName) {
- if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) {
- oSourceElementData.aCount[nCategory][sName] = 0;
- if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) {
- oSourceElementData.aPrimitiveValues.push(sName);
- }
- }
- oSourceElementData.aCount[nCategory][sName] += 1;
- },
- /**
- * Consolidates all worthwhile primitive values in a range of source
- * elements.
- * @param {number} nFrom The index (in the source text order) of the
- * source element that is the first element of the range.
- * @param {number} nTo The index (in the source text order) of the
- * source element that is the last element of the range.
- * @param {boolean} bEnclose Indicates whether the range should be
- * enclosed within a function call with no argument values to a
- * function with an empty parameter list if any primitive values
- * are consolidated.
- * @see TPrimitiveValue#nSaving
- */
- fExamineSourceElements = function(nFrom, nTo, bEnclose) {
- var _,
- /**
- * The index of the last mangled name.
- * @type {number}
- */
- nIndex = oScope.cname,
- /**
- * The index of the source element that is currently being
- * considered.
- * @type {number}
- */
- nPosition,
- /**
- * A collection of functions used during the consolidation of
- * primitive values and identifier names used as property
- * accessors.
- * @namespace
- * @type {!Object.<string, function(...[*])>}
- */
- oWalkersTransformers = {
- /**
- * If the String value that is equivalent to the sequence of
- * terminal symbols that constitute the encountered identifier
- * name is worthwhile, a syntactic conversion from the dot
- * notation to the bracket notation ensues with that sequence
- * being substituted by an identifier name to which the value
- * is assigned.
- * Applies to property accessors that use the dot notation.
- * @param {!TSyntacticCodeUnit} oExpression The nonterminal
- * MemberExpression.
- * @param {string} sIdentifierName The identifier name used as
- * the property accessor.
- * @return {!Array} A syntactic code unit that is equivalent to
- * the one encountered.
- * @see TPrimitiveValue#nSaving
- */
- 'dot': function(oExpression, sIdentifierName) {
- /**
- * The prefixed String value that is equivalent to the
- * sequence of terminal symbols that constitute the
- * encountered identifier name.
- * @type {string}
- */
- var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName;
- return oSolutionBest.oPrimitiveValues.hasOwnProperty(
- sPrefixed) &&
- oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
- ['sub',
- oWalker.walk(oExpression),
- ['name',
- oSolutionBest.oPrimitiveValues[sPrefixed].sName]] :
- ['dot', oWalker.walk(oExpression), sIdentifierName];
- },
- /**
- * If the encountered identifier is a null or Boolean literal
- * and its value is worthwhile, the identifier is substituted
- * by an identifier name to which that value is assigned.
- * Applies to identifier names.
- * @param {string} sIdentifier The identifier encountered.
- * @return {!Array} A syntactic code unit that is equivalent to
- * the one encountered.
- * @see TPrimitiveValue#nSaving
- */
- 'name': function(sIdentifier) {
- /**
- * The prefixed representation String of the identifier.
- * @type {string}
- */
- var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier;
- return [
- 'name',
- oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) &&
- oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
- oSolutionBest.oPrimitiveValues[sPrefixed].sName :
- sIdentifier
- ];
- },
- /**
- * If the encountered String value is worthwhile, it is
- * substituted by an identifier name to which that value is
- * assigned.
- * Applies to String values.
- * @param {string} sStringValue The String value of the string
- * literal encountered.
- * @return {!Array} A syntactic code unit that is equivalent to
- * the one encountered.
- * @see TPrimitiveValue#nSaving
- */
- 'string': function(sStringValue) {
- /**
- * The prefixed representation String of the primitive value
- * of the literal.
- * @type {string}
- */
- var sPrefixed =
- EValuePrefixes.S_STRING + sStringValue;
- return oSolutionBest.oPrimitiveValues.hasOwnProperty(
- sPrefixed) &&
- oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
- ['name',
- oSolutionBest.oPrimitiveValues[sPrefixed].sName] :
- ['string', sStringValue];
- }
- },
- /**
- * Such data on what to consolidate within the range of source
- * elements that is currently being considered that lead to the
- * greatest known reduction of the number of the terminal symbols
- * in comparison to the original source text.
- * @type {!TSolution}
- */
- oSolutionBest = new TSolution(),
- /**
- * Data representing an ongoing attempt to find a better
- * reduction of the number of the terminal symbols in comparison
- * to the original source text than the best one that is
- * currently known.
- * @type {!TSolution}
- * @see oSolutionBest
- */
- oSolutionCandidate = new TSolution(),
- /**
- * A record consisting of data about the range of source elements
- * that is currently being examined.
- * @type {!TSourceElementsData}
- */
- oSourceElementsData = new TSourceElementsData(),
- /**
- * Variable declarations for each primitive value that is to be
- * consolidated within the elements.
- * @type {!Array.<!Array>}
- */
- aVariableDeclarations = [],
- /**
- * Augments a list with a prefixed representation String.
- * @param {!Array.<string>} aList A list that is to be augmented.
- * @return {function(string)} A function that augments a list
- * with a prefixed representation String.
- */
- cAugmentList = function(aList) {
- /**
- * @param {string} sPrefixed Prefixed representation String of
- * a primitive value that could be consolidated within the
- * elements.
- */
- var fLambda = function(sPrefixed) {
- if (-1 === aList.indexOf(sPrefixed)) {
- aList.push(sPrefixed);
- }
- };
- return fLambda;
- },
- /**
- * Adds the number of occurrences of a primitive value of a given
- * category that could be consolidated in the source element with
- * a given index to the count of occurrences of that primitive
- * value within the range of source elements that is currently
- * being considered.
- * @param {number} nPosition The index (in the source text order)
- * of a source element.
- * @param {number} nCategory The category of the primary
- * expression from which the primitive value is derived.
- * @return {function(string)} A function that performs the
- * addition.
- * @see cAddOccurrencesInCategory
- */
- cAddOccurrences = function(nPosition, nCategory) {
- /**
- * @param {string} sPrefixed The prefixed representation String
- * of a primitive value.
- */
- var fLambda = function(sPrefixed) {
- if (!oSourceElementsData.aCount[nCategory].hasOwnProperty(
- sPrefixed)) {
- oSourceElementsData.aCount[nCategory][sPrefixed] = 0;
- }
- oSourceElementsData.aCount[nCategory][sPrefixed] +=
- aSourceElementsData[nPosition].aCount[nCategory][
- sPrefixed];
- };
- return fLambda;
- },
- /**
- * Adds the number of occurrences of each primitive value of a
- * given category that could be consolidated in the source
- * element with a given index to the count of occurrences of that
- * primitive values within the range of source elements that is
- * currently being considered.
- * @param {number} nPosition The index (in the source text order)
- * of a source element.
- * @return {function(number)} A function that performs the
- * addition.
- * @see fAddOccurrences
- */
- cAddOccurrencesInCategory = function(nPosition) {
- /**
- * @param {number} nCategory The category of the primary
- * expression from which the primitive value is derived.
- */
- var fLambda = function(nCategory) {
- Object.keys(
- aSourceElementsData[nPosition].aCount[nCategory]
- ).forEach(cAddOccurrences(nPosition, nCategory));
- };
- return fLambda;
- },
- /**
- * Adds the number of occurrences of each primitive value that
- * could be consolidated in the source element with a given index
- * to the count of occurrences of that primitive values within
- * the range of source elements that is currently being
- * considered.
- * @param {number} nPosition The index (in the source text order)
- * of a source element.
- */
- fAddOccurrences = function(nPosition) {
- Object.keys(aSourceElementsData[nPosition].aCount).forEach(
- cAddOccurrencesInCategory(nPosition));
- },
- /**
- * Creates a variable declaration for a primitive value if that
- * primitive value is to be consolidated within the elements.
- * @param {string} sPrefixed Prefixed representation String of a
- * primitive value that could be consolidated within the
- * elements.
- * @see aVariableDeclarations
- */
- cAugmentVariableDeclarations = function(sPrefixed) {
- if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) {
- aVariableDeclarations.push([
- oSolutionBest.oPrimitiveValues[sPrefixed].sName,
- [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ?
- 'name' : 'string',
- sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)]
- ]);
- }
- },
- /**
- * Sorts primitive values with regard to the difference in the
- * number of terminal symbols between the original source text
- * and the one with those primitive values consolidated.
- * @param {string} sPrefixed0 The prefixed representation String
- * of the first of the two primitive values that are being
- * compared.
- * @param {string} sPrefixed1 The prefixed representation String
- * of the second of the two primitive values that are being
- * compared.
- * @return {number}
- * <dl>
- * <dt>-1</dt>
- * <dd>if the first primitive value must be placed before
- * the other one,</dd>
- * <dt>0</dt>
- * <dd>if the first primitive value may be placed before
- * the other one,</dd>
- * <dt>1</dt>
- * <dd>if the first primitive value must not be placed
- * before the other one.</dd>
- * </dl>
- * @see TSolution.oPrimitiveValues
- */
- cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) {
- /**
- * The difference between:
- * <ol>
- * <li>the difference in the number of terminal symbols
- * between the original source text and the one with the
- * first primitive value consolidated, and</li>
- * <li>the difference in the number of terminal symbols
- * between the original source text and the one with the
- * second primitive value consolidated.</li>
- * </ol>
- * @type {number}
- */
- var nDifference =
- oSolutionCandidate.oPrimitiveValues[sPrefixed0].nSaving -
- oSolutionCandidate.oPrimitiveValues[sPrefixed1].nSaving;
- return nDifference > 0 ? -1 : nDifference < 0 ? 1 : 0;
- },
- /**
- * Assigns an identifier name to a primitive value and calculates
- * whether instances of that primitive value are worth
- * consolidating.
- * @param {string} sPrefixed The prefixed representation String
- * of a primitive value that is being evaluated.
- */
- fEvaluatePrimitiveValue = function(sPrefixed) {
- var _,
- /**
- * The index of the last mangled name.
- * @type {number}
- */
- nIndex,
- /**
- * The representation String of the primitive value that is
- * being evaluated.
- * @type {string}
- */
- sName =
- sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length),
- /**
- * The number of source characters taken up by the
- * representation String of the primitive value that is
- * being evaluated.
- * @type {number}
- */
- nLengthOriginal = sName.length,
- /**
- * The number of source characters taken up by the
- * identifier name that could substitute the primitive
- * value that is being evaluated.
- * substituted.
- * @type {number}
- */
- nLengthSubstitution,
- /**
- * The number of source characters taken up by by the
- * representation String of the primitive value that is
- * being evaluated when it is represented by a string
- * literal.
- * @type {number}
- */
- nLengthString = oProcessor.make_string(sName).length;
- oSolutionCandidate.oPrimitiveValues[sPrefixed] =
- new TPrimitiveValue();
- do { // Find an identifier unused in this or any nested scope.
- nIndex = oScope.cname;
- oSolutionCandidate.oPrimitiveValues[sPrefixed].sName =
- oScope.next_mangled();
- } while (-1 !== oSourceElementsData.aIdentifiers.indexOf(
- oSolutionCandidate.oPrimitiveValues[sPrefixed].sName));
- nLengthSubstitution = oSolutionCandidate.oPrimitiveValues[
- sPrefixed].sName.length;
- if (0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC)) {
- // foo:null, or foo:null;
- oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
- nLengthSubstitution + nLengthOriginal +
- oWeights.N_VARIABLE_DECLARATION;
- // null vs foo
- oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
- oSourceElementsData.aCount[
- EPrimaryExpressionCategories.
- N_NULL_AND_BOOLEAN_LITERALS][sPrefixed] *
- (nLengthOriginal - nLengthSubstitution);
- } else {
- // foo:'fromCharCode';
- oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
- nLengthSubstitution + nLengthString +
- oWeights.N_VARIABLE_DECLARATION;
- // .fromCharCode vs [foo]
- if (oSourceElementsData.aCount[
- EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
- ].hasOwnProperty(sPrefixed)) {
- oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
- oSourceElementsData.aCount[
- EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
- ][sPrefixed] *
- (nLengthOriginal - nLengthSubstitution -
- oWeights.N_PROPERTY_ACCESSOR);
- }
- // 'fromCharCode' vs foo
- if (oSourceElementsData.aCount[
- EPrimaryExpressionCategories.N_STRING_LITERALS
- ].hasOwnProperty(sPrefixed)) {
- oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
- oSourceElementsData.aCount[
- EPrimaryExpressionCategories.N_STRING_LITERALS
- ][sPrefixed] *
- (nLengthString - nLengthSubstitution);
- }
- }
- if (oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving >
- 0) {
- oSolutionCandidate.nSavings +=
- oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving;
- } else {
- oScope.cname = nIndex; // Free the identifier name.
- }
- },
- /**
- * Adds a variable declaration to an existing variable statement.
- * @param {!Array} aVariableDeclaration A variable declaration
- * with an initialiser.
- */
- cAddVariableDeclaration = function(aVariableDeclaration) {
- (/** @type {!Array} */ oSourceElements[nFrom][1]).unshift(
- aVariableDeclaration);
- };
- if (nFrom > nTo) {
- return;
- }
- // If the range is a closure, reuse the closure.
- if (nFrom === nTo &&
- 'stat' === oSourceElements[nFrom][0] &&
- 'call' === oSourceElements[nFrom][1][0] &&
- 'function' === oSourceElements[nFrom][1][1][0]) {
- fExamineSyntacticCodeUnit(oSourceElements[nFrom][1][1]);
- return;
- }
- // Create a list of all derived primitive values within the range.
- for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
- aSourceElementsData[nPosition].aPrimitiveValues.forEach(
- cAugmentList(oSourceElementsData.aPrimitiveValues));
- }
- if (0 === oSourceElementsData.aPrimitiveValues.length) {
- return;
- }
- for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
- // Add the number of occurrences to the total count.
- fAddOccurrences(nPosition);
- // Add identifiers of this or any nested scope to the list.
- aSourceElementsData[nPosition].aIdentifiers.forEach(
- cAugmentList(oSourceElementsData.aIdentifiers));
- }
- // Distribute identifier names among derived primitive values.
- do { // If there was any progress, find a better distribution.
- oSolutionBest = oSolutionCandidate;
- if (Object.keys(oSolutionCandidate.oPrimitiveValues).length > 0) {
- // Sort primitive values descending by their worthwhileness.
- oSourceElementsData.aPrimitiveValues.sort(cSortPrimitiveValues);
- }
- oSolutionCandidate = new TSolution();
- oSourceElementsData.aPrimitiveValues.forEach(
- fEvaluatePrimitiveValue);
- oScope.cname = nIndex;
- } while (oSolutionCandidate.nSavings > oSolutionBest.nSavings);
- // Take the necessity of adding a variable statement into account.
- if ('var' !== oSourceElements[nFrom][0]) {
- oSolutionBest.nSavings -= oWeights.N_VARIABLE_STATEMENT_AFFIXATION;
- }
- if (bEnclose) {
- // Take the necessity of forming a closure into account.
- oSolutionBest.nSavings -= oWeights.N_CLOSURE;
- }
- if (oSolutionBest.nSavings > 0) {
- // Create variable declarations suitable for UglifyJS.
- Object.keys(oSolutionBest.oPrimitiveValues).forEach(
- cAugmentVariableDeclarations);
- // Rewrite expressions that contain worthwhile primitive values.
- for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
- oWalker = oProcessor.ast_walker();
- oSourceElements[nPosition] =
- oWalker.with_walkers(
- oWalkersTransformers,
- cContext(oWalker, oSourceElements[nPosition]));
- }
- if ('var' === oSourceElements[nFrom][0]) { // Reuse the statement.
- (/** @type {!Array.<!Array>} */ aVariableDeclarations.reverse(
- )).forEach(cAddVariableDeclaration);
- } else { // Add a variable statement.
- Array.prototype.splice.call(
- oSourceElements,
- nFrom,
- 0,
- ['var', aVariableDeclarations]);
- nTo += 1;
- }
- if (bEnclose) {
- // Add a closure.
- Array.prototype.splice.call(
- oSourceElements,
- nFrom,
- 0,
- ['stat', ['call', ['function', null, [], []], []]]);
- // Copy source elements into the closure.
- for (nPosition = nTo + 1; nPosition > nFrom; nPosition -= 1) {
- Array.prototype.unshift.call(
- oSourceElements[nFrom][1][1][3],
- oSourceElements[nPosition]);
- }
- // Remove source elements outside the closure.
- Array.prototype.splice.call(
- oSourceElements,
- nFrom + 1,
- nTo - nFrom + 1);
- }
- }
- if (bEnclose) {
- // Restore the availability of identifier names.
- oScope.cname = nIndex;
- }
- };
- oSourceElements = (/** @type {!TSyntacticCodeUnit} */
- oSyntacticCodeUnit[bIsGlobal ? 1 : 3]);
- if (0 === oSourceElements.length) {
- return;
- }
- oScope = bIsGlobal ? oSyntacticCodeUnit.scope : oSourceElements.scope;
- // Skip a Directive Prologue.
- while (nAfterDirectivePrologue < oSourceElements.length &&
- 'directive' === oSourceElements[nAfterDirectivePrologue][0]) {
- nAfterDirectivePrologue += 1;
- aSourceElementsData.push(null);
- }
- if (oSourceElements.length === nAfterDirectivePrologue) {
- return;
- }
- for (nPosition = nAfterDirectivePrologue;
- nPosition < oSourceElements.length;
- nPosition += 1) {
- oSourceElementData = new TSourceElementsData();
- oWalker = oProcessor.ast_walker();
- // Classify a source element.
- // Find its derived primitive values and count their occurrences.
- // Find all identifiers used (including nested scopes).
- oWalker.with_walkers(
- oWalkers.oSurveySourceElement,
- cContext(oWalker, oSourceElements[nPosition]));
- // Establish whether the scope is still wholly examinable.
- bIsWhollyExaminable = bIsWhollyExaminable &&
- ESourceElementCategories.N_WITH !== oSourceElementData.nCategory &&
- ESourceElementCategories.N_EVAL !== oSourceElementData.nCategory;
- aSourceElementsData.push(oSourceElementData);
- }
- if (bIsWhollyExaminable) { // Examine the whole scope.
- fExamineSourceElements(
- nAfterDirectivePrologue,
- oSourceElements.length - 1,
- false);
- } else { // Examine unexcluded ranges of source elements.
- for (nPosition = oSourceElements.length - 1;
- nPosition >= nAfterDirectivePrologue;
- nPosition -= 1) {
- oSourceElementData = (/** @type {!TSourceElementsData} */
- aSourceElementsData[nPosition]);
- if (ESourceElementCategories.N_OTHER ===
- oSourceElementData.nCategory) {
- if ('undefined' === typeof nTo) {
- nTo = nPosition; // Indicate the end of a range.
- }
- // Examine the range if it immediately follows a Directive Prologue.
- if (nPosition === nAfterDirectivePrologue) {
- fExamineSourceElements(nPosition, nTo, true);
- }
- } else {
- if ('undefined' !== typeof nTo) {
- // Examine the range that immediately follows this source element.
- fExamineSourceElements(nPosition + 1, nTo, true);
- nTo = void 0; // Obliterate the range.
- }
- // Examine nested functions.
- oWalker = oProcessor.ast_walker();
- oWalker.with_walkers(
- oWalkers.oExamineFunctions,
- cContext(oWalker, oSourceElements[nPosition]));
- }
- }
- }
- }(oAbstractSyntaxTree = oProcessor.ast_add_scope(oAbstractSyntaxTree)));
- return oAbstractSyntaxTree;
- };
- /*jshint sub:false */
- /* Local Variables: */
- /* mode: js */
- /* coding: utf-8 */
- /* indent-tabs-mode: nil */
- /* tab-width: 2 */
- /* End: */
- /* vim: set ft=javascript fenc=utf-8 et ts=2 sts=2 sw=2: */
- /* :mode=javascript:noTabs=true:tabSize=2:indentSize=2:deepIndent=true: */
|