/** * @preserve Copyright 2012 Robert Gust-Bardon . * 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 UglifyJS with consolidation of null, Boolean, and String values. *

Also known as aliasing, this feature has been deprecated in the Closure Compiler since its * initial release, where it is unavailable from the CLI. 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.

*

Consolidating literals may worsen the data compression ratio when an encoding * transformation is applied. For instance, jQuery 1.7.1 takes 248235 bytes. * Building it with * UglifyJS v1.2.5 results in 93647 bytes (37.73% of the original) which are * then compressed to 33154 bytes (13.36% of the original) using gzip(1). 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).

*

Written in the strict variant * of ECMA-262 5.1 Edition. Encoded in UTF-8. Follows Revision 2.28 of the Google JavaScript Style Guide (except for the * discouraged use of the {@code function} tag and the {@code namespace} tag). * 100% typed for the Closure Compiler Version 1741.

*

Should you find this software useful, please consider a donation.

* @author follow.me@RGustBardon (Robert Gust-Bardon) * @supported Tested with: * */ /*global console:false, exports:true, module:false, require:false */ /*jshint sub:true */ /** * Consolidates null, Boolean, and String values found inside an AST. * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object * representing an AST. * @return {!TSyntacticCodeUnit} An array-like object representing an AST 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.>} */ 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.} */ this.aIdentifiers = []; /** * Prefixed representation Strings of each primitive value that could be * consolidated within the elements. * @type {!Array.} */ 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.} * @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 ASTs 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.} */ oWeights = { /** * The difference in the number of punctuators required by the bracket * notation and the dot notation. *

'[]'.length - '.'.length

* @const * @type {number} */ N_PROPERTY_ACCESSOR: 1, /** * The number of punctuators required by a variable declaration with an * initialiser. *

':'.length + ';'.length

* @const * @type {number} */ N_VARIABLE_DECLARATION: 2, /** * The number of terminal symbols required to introduce a variable * statement (excluding its variable declaration list). *

'var '.length

* @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. *

'(function(){}());'.length

* @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 ECMA-262. * @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 {@code with} statement. * @type {number} */ N_WITH: 0, /** * Identifies a source element that includes the {@code eval} 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.} */ 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 AST. * @type {!TWalker} */ oWalker, /** * An object encompassing collections of functions used during the * traversal of an AST. * @namespace * @type {!Object.>} */ oWalkers = { /** * A collection of functions used during the surveyance of source * elements. * @namespace * @type {!Object.} */ 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.} 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 AST 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.} 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.} 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.} */ 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 AST 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 AST 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.} */ aSourceElementsData = [], /** * The index (in the source text order) of the source element * immediately following a Directive Prologue. * @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.} */ 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.} */ aVariableDeclarations = [], /** * Augments a list with a prefixed representation String. * @param {!Array.} 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} *
*
-1
*
if the first primitive value must be placed before * the other one,
*
0
*
if the first primitive value may be placed before * the other one,
*
1
*
if the first primitive value must not be placed * before the other one.
*
* @see TSolution.oPrimitiveValues */ cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) { /** * The difference between: *
    *
  1. the difference in the number of terminal symbols * between the original source text and the one with the * first primitive value consolidated, and
  2. *
  3. the difference in the number of terminal symbols * between the original source text and the one with the * second primitive value consolidated.
  4. *
* @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.} */ 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 && 'stat' === oSourceElements[nAfterDirectivePrologue][0] && 'string' === oSourceElements[nAfterDirectivePrologue][1][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 */ if (require.main === module) { (function() { '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 _, /** * NodeJS module for unit testing. * @namespace * @type {!TAssert} * @see http://nodejs.org/docs/v0.6.10/api/all.html#assert */ oAssert = (/** @type {!TAssert} */ require('assert')), /** * The parser of ECMA-262 found in UglifyJS. * @namespace * @type {!TParser} */ oParser = (/** @type {!TParser} */ require('./parse-js')), /** * The processor of ASTs * found in UglifyJS. * @namespace * @type {!TProcessor} */ oProcessor = (/** @type {!TProcessor} */ require('./process')), /** * An instance of an object that allows the traversal of an AST. * @type {!TWalker} */ oWalker, /** * A collection of functions for the removal of the scope information * during the traversal of an AST. * @namespace * @type {!Object.} */ oWalkersPurifiers = { /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys. /** * Deletes the scope information from the branch of the abstract * syntax tree representing the encountered function declaration. * @param {string} sIdentifier The identifier of the function. * @param {!Array.} aFormalParameterList Formal parameters. * @param {!TSyntacticCodeUnit} oFunctionBody Function code. */ 'defun': function( sIdentifier, aFormalParameterList, oFunctionBody) { delete oFunctionBody.scope; }, /** * Deletes the scope information from the branch of the abstract * syntax tree representing the encountered function expression. * @param {?string} sIdentifier The optional identifier of the * function. * @param {!Array.} aFormalParameterList Formal parameters. * @param {!TSyntacticCodeUnit} oFunctionBody Function code. */ 'function': function( sIdentifier, aFormalParameterList, oFunctionBody) { delete oFunctionBody.scope; } /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys. }, /** * 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; }, /** * A record consisting of configuration for the code generation phase. * @type {!Object} */ oCodeGenerationOptions = { beautify: true }, /** * Tests whether consolidation of an ECMAScript program yields expected * results. * @param {{ * sTitle: string, * sInput: string, * sOutput: string * }} oUnitTest A record consisting of data about a unit test: its * name, an ECMAScript program, and, if consolidation is to take * place, the resulting ECMAScript program. */ cAssert = function(oUnitTest) { var _, /** * An array-like object representing the AST obtained after consolidation. * @type {!TSyntacticCodeUnit} */ oSyntacticCodeUnitActual = exports.ast_consolidate(oParser.parse(oUnitTest.sInput)), /** * An array-like object representing the expected AST. * @type {!TSyntacticCodeUnit} */ oSyntacticCodeUnitExpected = oParser.parse( oUnitTest.hasOwnProperty('sOutput') ? oUnitTest.sOutput : oUnitTest.sInput); delete oSyntacticCodeUnitActual.scope; oWalker = oProcessor.ast_walker(); oWalker.with_walkers( oWalkersPurifiers, cContext(oWalker, oSyntacticCodeUnitActual)); try { oAssert.deepEqual( oSyntacticCodeUnitActual, oSyntacticCodeUnitExpected); } catch (oException) { console.error( '########## A unit test has failed.\n' + oUnitTest.sTitle + '\n' + '##### actual code (' + oProcessor.gen_code(oSyntacticCodeUnitActual).length + ' bytes)\n' + oProcessor.gen_code( oSyntacticCodeUnitActual, oCodeGenerationOptions) + '\n' + '##### expected code (' + oProcessor.gen_code(oSyntacticCodeUnitExpected).length + ' bytes)\n' + oProcessor.gen_code( oSyntacticCodeUnitExpected, oCodeGenerationOptions)); } }; [ // 7.6.1 Reserved Words. { sTitle: 'Omission of keywords while choosing an identifier name.', sInput: '(function() {' + ' var a, b, c, d, e, f, g, h, i, j, k, l, m,' + ' n, o, p, q, r, s, t, u, v, w, x, y, z,' + ' A, B, C, D, E, F, G, H, I, J, K, L, M,' + ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' + ' $, _,' + ' aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am,' + ' an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az,' + ' aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM,' + ' aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ,' + ' a$, a_,' + ' ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm,' + ' bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz,' + ' bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM,' + ' bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ,' + ' b$, b_,' + ' ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm,' + ' cn, co, cp, cq, cr, cs, ct, cu, cv, cw, cx, cy, cz,' + ' cA, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM,' + ' cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ,' + ' c$, c_,' + ' da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm,' + ' dn, dq, dr, ds, dt, du, dv, dw, dx, dy, dz,' + ' dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM,' + ' dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ,' + ' d$, d_;' + ' void ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",' + ' "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];' + '}());', sOutput: '(function() {' + ' var dp =' + ' "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",' + ' a, b, c, d, e, f, g, h, i, j, k, l, m,' + ' n, o, p, q, r, s, t, u, v, w, x, y, z,' + ' A, B, C, D, E, F, G, H, I, J, K, L, M,' + ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' + ' $, _,' + ' aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am,' + ' an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az,' + ' aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM,' + ' aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ,' + ' a$, a_,' + ' ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm,' + ' bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz,' + ' bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM,' + ' bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ,' + ' b$, b_,' + ' ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm,' + ' cn, co, cp, cq, cr, cs, ct, cu, cv, cw, cx, cy, cz,' + ' cA, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM,' + ' cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ,' + ' c$, c_,' + ' da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm,' + ' dn, dq, dr, ds, dt, du, dv, dw, dx, dy, dz,' + ' dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM,' + ' dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ,' + ' d$, d_;' + ' void [dp, dp];' + '}());' }, // 7.8.1 Null Literals. { sTitle: 'Evaluation with regard to the null value.', sInput: '/*jshint evil:true */' + '(function() {' + ' var foo;' + ' void [null, null, null];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void [null, null];' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' var a = null, foo;' + ' void [a, a, a];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void [null, null];' + '}());' }, // 7.8.2 Boolean Literals. { sTitle: 'Evaluation with regard to the false value.', sInput: '/*jshint evil:true */' + '(function() {' + ' var foo;' + ' void [false, false, false];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void [false, false];' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' var a = false, foo;' + ' void [a, a, a];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void [false, false];' + '}());' }, { sTitle: 'Evaluation with regard to the true value.', sInput: '/*jshint evil:true */' + '(function() {' + ' var foo;' + ' void [true, true, true];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void [true, true];' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' var a = true, foo;' + ' void [a, a, a];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void [true, true];' + '}());' }, // 7.8.4 String Literals. { sTitle: 'Evaluation with regard to the String value of a string literal.', sInput: '(function() {' + ' var foo;' + ' void ["abcd", "abcd", "abc", "abc"];' + '}());', sOutput: '(function() {' + ' var a = "abcd", foo;' + ' void [a, a, "abc", "abc"];' + '}());' }, // 7.8.5 Regular Expression Literals. { sTitle: 'Preservation of the pattern of a regular expression literal.', sInput: 'void [/abcdefghijklmnopqrstuvwxyz/, /abcdefghijklmnopqrstuvwxyz/];' }, { sTitle: 'Preservation of the flags of a regular expression literal.', sInput: 'void [/(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim,' + ' /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim,' + ' /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim];' }, // 10.2 Lexical Environments. { sTitle: 'Preservation of identifier names in the same scope.', sInput: '/*jshint shadow:true */' + 'var a;' + 'function b(i) {' + '}' + 'for (var c; 0 === Math.random(););' + 'for (var d in {});' + 'void ["abcdefghijklmnopqrstuvwxyz"];' + 'void [b(a), b(c), b(d)];' + 'void [typeof e];' + 'i: for (; 0 === Math.random();) {' + ' if (42 === (new Date()).getMinutes()) {' + ' continue i;' + ' } else {' + ' break i;' + ' }' + '}' + 'try {' + '} catch (f) {' + '} finally {' + '}' + '(function g(h) {' + '}());' + 'void [{' + ' i: 42,' + ' "j": 42,' + ' \'k\': 42' + '}];' + 'void ["abcdefghijklmnopqrstuvwxyz"];', sOutput: '/*jshint shadow:true */' + 'var a;' + 'function b(i) {' + '}' + 'for (var c; 0 === Math.random(););' + 'for (var d in {});' + '(function() {' + ' var i = "abcdefghijklmnopqrstuvwxyz";' + ' void [i];' + ' void [b(a), b(c), b(d)];' + ' void [typeof e];' + ' i: for (; 0 === Math.random();) {' + ' if (42 === (new Date()).getMinutes()) {' + ' continue i;' + ' } else {' + ' break i;' + ' }' + ' }' + ' try {' + ' } catch (f) {' + ' } finally {' + ' }' + ' (function g(h) {' + ' }());' + ' void [{' + ' i: 42,' + ' "j": 42,' + ' \'k\': 42' + ' }];' + ' void [i];' + '}());' }, { sTitle: 'Preservation of identifier names in nested function code.', sInput: '(function() {' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' (function() {' + ' var a;' + ' for (var b; 0 === Math.random(););' + ' for (var c in {});' + ' void [typeof d];' + ' h: for (; 0 === Math.random();) {' + ' if (42 === (new Date()).getMinutes()) {' + ' continue h;' + ' } else {' + ' break h;' + ' }' + ' }' + ' try {' + ' } catch (e) {' + ' } finally {' + ' }' + ' (function f(g) {' + ' }());' + ' void [{' + ' h: 42,' + ' "i": 42,' + ' \'j\': 42' + ' }];' + ' }());' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + '}());', sOutput: '(function() {' + ' var h = "abcdefghijklmnopqrstuvwxyz";' + ' void [h];' + ' (function() {' + ' var a;' + ' for (var b; 0 === Math.random(););' + ' for (var c in {});' + ' void [typeof d];' + ' h: for (; 0 === Math.random();) {' + ' if (42 === (new Date()).getMinutes()) {' + ' continue h;' + ' } else {' + ' break h;' + ' }' + ' }' + ' try {' + ' } catch (e) {' + ' } finally {' + ' }' + ' (function f(g) {' + ' }());' + ' void [{' + ' h: 42,' + ' "i": 42,' + ' \'j\': 42' + ' }];' + ' }());' + ' void [h];' + '}());' }, { sTitle: 'Consolidation of a closure with other source elements.', sInput: '(function(foo) {' + '}("abcdefghijklmnopqrstuvwxyz"));' + 'void ["abcdefghijklmnopqrstuvwxyz"];', sOutput: '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' (function(foo) {' + ' })(a);' + ' void [a];' + '}());' }, { sTitle: 'Consolidation of function code instead of a sole closure.', sInput: '(function(foo, bar) {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"));', sOutput: '(function(foo, bar) {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"));' }, // 11.1.5 Object Initialiser. { sTitle: 'Preservation of property names of an object initialiser.', sInput: 'var foo = {' + ' abcdefghijklmnopqrstuvwxyz: 42,' + ' "zyxwvutsrqponmlkjihgfedcba": 42,' + ' \'mlkjihgfedcbanopqrstuvwxyz\': 42' + '};' + 'void [' + ' foo.abcdefghijklmnopqrstuvwxyz,' + ' "zyxwvutsrqponmlkjihgfedcba",' + ' \'mlkjihgfedcbanopqrstuvwxyz\'' + '];' }, { sTitle: 'Evaluation with regard to String values derived from identifier ' + 'names used as property accessors.', sInput: '(function() {' + ' var foo;' + ' void [' + ' Math.abcdefghij,' + ' Math.abcdefghij,' + ' Math.abcdefghi,' + ' Math.abcdefghi' + ' ];' + '}());', sOutput: '(function() {' + ' var a = "abcdefghij", foo;' + ' void [' + ' Math[a],' + ' Math[a],' + ' Math.abcdefghi,' + ' Math.abcdefghi' + ' ];' + '}());' }, // 11.2.1 Property Accessors. { sTitle: 'Preservation of identifiers in the nonterminal MemberExpression.', sInput: 'void [' + ' Math.E,' + ' Math.LN10,' + ' Math.LN2,' + ' Math.LOG2E,' + ' Math.LOG10E,' + ' Math.PI,' + ' Math.SQRT1_2,' + ' Math.SQRT2,' + ' Math.abs,' + ' Math.acos' + '];' }, // 12.2 Variable Statement. { sTitle: 'Preservation of the identifier of a variable that is being ' + 'declared in a variable statement.', sInput: '(function() {' + ' var abcdefghijklmnopqrstuvwxyz;' + ' void [abcdefghijklmnopqrstuvwxyz];' + '}());' }, { sTitle: 'Exclusion of a variable statement in global code.', sInput: 'void ["abcdefghijklmnopqrstuvwxyz"];' + 'var foo = "abcdefghijklmnopqrstuvwxyz",' + ' bar = "abcdefghijklmnopqrstuvwxyz";' + 'void ["abcdefghijklmnopqrstuvwxyz"];' }, { sTitle: 'Exclusion of a variable statement in function code that ' + 'contains a with statement.', sInput: '(function() {' + ' with ({});' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' var foo;' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + '}());' }, { sTitle: 'Exclusion of a variable statement in function code that ' + 'contains a direct call to the eval function.', sInput: '/*jshint evil:true */' + 'void [' + ' function() {' + ' eval("");' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' var foo;' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' }' + '];' }, { sTitle: 'Consolidation within a variable statement in global code.', sInput: 'var foo = function() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '};', sOutput: 'var foo = function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '};' }, { sTitle: 'Consolidation within a variable statement excluded in function ' + 'code due to the presence of a with statement.', sInput: '(function() {' + ' with ({});' + ' var foo = function() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' };' + '}());', sOutput: '(function() {' + ' with ({});' + ' var foo = function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' };' + '}());' }, { sTitle: 'Consolidation within a variable statement excluded in function ' + 'code due to the presence of a direct call to the eval function.', sInput: '/*jshint evil:true */' + '(function() {' + ' eval("");' + ' var foo = function() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' };' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' eval("");' + ' var foo = function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' };' + '}());' }, { sTitle: 'Inclusion of a variable statement in function code that ' + 'contains no with statement and no direct call to the eval ' + 'function.', sInput: '(function() {' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' var foo;' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + '}());', sOutput: '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a];' + ' var foo;' + ' void [a];' + '}());' }, { sTitle: 'Ignorance with regard to a variable statement in global code.', sInput: 'var foo = "abcdefghijklmnopqrstuvwxyz";' + 'void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];', sOutput: 'var foo = "abcdefghijklmnopqrstuvwxyz";' + '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' }, // 12.4 Expression Statement. { sTitle: 'Preservation of identifiers in an expression statement.', sInput: 'void [typeof abcdefghijklmnopqrstuvwxyz,' + ' typeof abcdefghijklmnopqrstuvwxyz];' }, // 12.6.3 The {@code for} Statement. { sTitle: 'Preservation of identifiers in the variable declaration list of ' + 'a for statement.', sInput: 'for (var abcdefghijklmnopqrstuvwxyz; 0 === Math.random(););' + 'for (var abcdefghijklmnopqrstuvwxyz; 0 === Math.random(););' }, // 12.6.4 The {@code for-in} Statement. { sTitle: 'Preservation of identifiers in the variable declaration list of ' + 'a for-in statement.', sInput: 'for (var abcdefghijklmnopqrstuvwxyz in {});' + 'for (var abcdefghijklmnopqrstuvwxyz in {});' }, // 12.7 The {@code continue} Statement. { sTitle: 'Preservation of the identifier in a continue statement.', sInput: 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' + ' continue abcdefghijklmnopqrstuvwxyz;' + '}' + 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' + ' continue abcdefghijklmnopqrstuvwxyz;' + '}' }, // 12.8 The {@code break} Statement. { sTitle: 'Preservation of the identifier in a break statement.', sInput: 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' + ' break abcdefghijklmnopqrstuvwxyz;' + '}' + 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' + ' break abcdefghijklmnopqrstuvwxyz;' + '}' }, // 12.9 The {@code return} Statement. { sTitle: 'Exclusion of a return statement in function code that contains ' + 'a with statement.', sInput: '(function() {' + ' with ({});' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' if (0 === Math.random()) {' + ' return;' + ' } else {' + ' }' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + '}());' }, { sTitle: 'Exclusion of a return statement in function code that contains ' + 'a direct call to the eval function.', sInput: '/*jshint evil:true */' + '(function() {' + ' eval("");' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' if (0 === Math.random()) {' + ' return;' + ' } else {' + ' }' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + '}());' }, { sTitle: 'Consolidation within a return statement excluded in function ' + 'code due to the presence of a with statement.', sInput: '(function() {' + ' with ({});' + ' return function() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' };' + '}());', sOutput: '(function() {' + ' with ({});' + ' return function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' };' + '}());' }, { sTitle: 'Consolidation within a return statement excluded in function ' + 'code due to the presence of a direct call to the eval function.', sInput: '/*jshint evil:true */' + '(function() {' + ' eval("");' + ' return function() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' };' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' eval("");' + ' return function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' };' + '}());' }, { sTitle: 'Inclusion of a return statement in function code that contains ' + 'no with statement and no direct call to the eval function.', sInput: '(function() {' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + ' if (0 === Math.random()) {' + ' return;' + ' } else {' + ' }' + ' void ["abcdefghijklmnopqrstuvwxyz"];' + '}());', sOutput: '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a];' + ' if (0 === Math.random()) {' + ' return;' + ' } else {' + ' }' + ' void [a];' + '}());' }, // 12.10 The {@code with} Statement. { sTitle: 'Preservation of the statement in a with statement.', sInput: 'with ({}) {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}' }, { sTitle: 'Exclusion of a with statement in the same syntactic code unit.', sInput: 'void ["abcdefghijklmnopqrstuvwxyz"];' + 'with ({' + ' foo: "abcdefghijklmnopqrstuvwxyz",' + ' bar: "abcdefghijklmnopqrstuvwxyz"' + '}) {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}' + 'void ["abcdefghijklmnopqrstuvwxyz"];' }, { sTitle: 'Exclusion of a with statement in nested function code.', sInput: 'void ["abcdefghijklmnopqrstuvwxyz"];' + '(function() {' + ' with ({' + ' foo: "abcdefghijklmnopqrstuvwxyz",' + ' bar: "abcdefghijklmnopqrstuvwxyz"' + ' }) {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' }' + '}());' + 'void ["abcdefghijklmnopqrstuvwxyz"];' }, // 12.12 Labelled Statements. { sTitle: 'Preservation of the label of a labelled statement.', sInput: 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random(););' + 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random(););' }, // 12.14 The {@code try} Statement. { sTitle: 'Preservation of the identifier in the catch clause of a try' + 'statement.', sInput: 'try {' + '} catch (abcdefghijklmnopqrstuvwxyz) {' + '} finally {' + '}' + 'try {' + '} catch (abcdefghijklmnopqrstuvwxyz) {' + '} finally {' + '}' }, // 13 Function Definition. { sTitle: 'Preservation of the identifier of a function declaration.', sInput: 'function abcdefghijklmnopqrstuvwxyz() {' + '}' + 'void [abcdefghijklmnopqrstuvwxyz];' }, { sTitle: 'Preservation of the identifier of a function expression.', sInput: 'void [' + ' function abcdefghijklmnopqrstuvwxyz() {' + ' },' + ' function abcdefghijklmnopqrstuvwxyz() {' + ' }' + '];' }, { sTitle: 'Preservation of a formal parameter of a function declaration.', sInput: 'function foo(abcdefghijklmnopqrstuvwxyz) {' + '}' + 'function bar(abcdefghijklmnopqrstuvwxyz) {' + '}' }, { sTitle: 'Preservation of a formal parameter in a function expression.', sInput: 'void [' + ' function(abcdefghijklmnopqrstuvwxyz) {' + ' },' + ' function(abcdefghijklmnopqrstuvwxyz) {' + ' }' + '];' }, { sTitle: 'Exclusion of a function declaration.', sInput: 'void ["abcdefghijklmnopqrstuvwxyz"];' + 'function foo() {' + '}' + 'void ["abcdefghijklmnopqrstuvwxyz"];' }, { sTitle: 'Consolidation within a function declaration.', sInput: 'function foo() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}', sOutput: 'function foo() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}' }, // 14 Program. { sTitle: 'Preservation of a program without source elements.', sInput: '' }, // 14.1 Directive Prologues and the Use Strict Directive. { sTitle: 'Preservation of a Directive Prologue in global code.', sInput: '"abcdefghijklmnopqrstuvwxyz";' + '\'zyxwvutsrqponmlkjihgfedcba\';' }, { sTitle: 'Preservation of a Directive Prologue in a function declaration.', sInput: 'function foo() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' \'zyxwvutsrqponmlkjihgfedcba\';' + '}' }, { sTitle: 'Preservation of a Directive Prologue in a function expression.', sInput: 'void [' + ' function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' \'zyxwvutsrqponmlkjihgfedcba\';' + ' }' + '];' }, { sTitle: 'Ignorance with regard to a Directive Prologue in global code.', sInput: '"abcdefghijklmnopqrstuvwxyz";' + 'void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];', sOutput: '"abcdefghijklmnopqrstuvwxyz";' + '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' }, { sTitle: 'Ignorance with regard to a Directive Prologue in a function' + 'declaration.', sInput: 'function foo() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}', sOutput: 'function foo() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}' }, { sTitle: 'Ignorance with regard to a Directive Prologue in a function' + 'expression.', sInput: '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}());', sOutput: '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' }, // 15.1 The Global Object. { sTitle: 'Preservation of a property of the global object.', sInput: 'void [undefined, undefined, undefined, undefined, undefined];' }, // 15.1.2.1.1 Direct Call to Eval. { sTitle: 'Exclusion of a direct call to the eval function in the same ' + 'syntactic code unit.', sInput: '/*jshint evil:true */' + 'void ["abcdefghijklmnopqrstuvwxyz"];' + 'eval("");' + 'void ["abcdefghijklmnopqrstuvwxyz"];' }, { sTitle: 'Exclusion of a direct call to the eval function in nested ' + 'function code.', sInput: '/*jshint evil:true */' + 'void ["abcdefghijklmnopqrstuvwxyz"];' + '(function() {' + ' eval("");' + '}());' + 'void ["abcdefghijklmnopqrstuvwxyz"];' }, { sTitle: 'Consolidation within a direct call to the eval function.', sInput: '/*jshint evil:true */' + 'eval(function() {' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}());', sOutput: '/*jshint evil:true */' + 'eval(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' }, // Consolidation proper. { sTitle: 'No consolidation if it does not result in a reduction of the ' + 'number of source characters.', sInput: '(function() {' + ' var foo;' + ' void ["ab", "ab", "abc", "abc"];' + '}());' }, { sTitle: 'Identification of a range of source elements at the beginning ' + 'of global code.', sInput: '/*jshint evil:true */' + '"abcdefghijklmnopqrstuvwxyz";' + 'void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + 'eval("");', sOutput: '/*jshint evil:true */' + '"abcdefghijklmnopqrstuvwxyz";' + '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' + 'eval("");' }, { sTitle: 'Identification of a range of source elements in the middle of ' + 'global code.', sInput: '/*jshint evil:true */' + '"abcdefghijklmnopqrstuvwxyz";' + 'eval("");' + 'void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + 'eval("");', sOutput: '/*jshint evil:true */' + '"abcdefghijklmnopqrstuvwxyz";' + 'eval("");' + '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' + 'eval("");' }, { sTitle: 'Identification of a range of source elements at the end of ' + 'global code.', sInput: '/*jshint evil:true */' + '"abcdefghijklmnopqrstuvwxyz";' + 'eval("");' + 'void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];', sOutput: '/*jshint evil:true */' + '"abcdefghijklmnopqrstuvwxyz";' + 'eval("");' + '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' }, { sTitle: 'Identification of a range of source elements at the beginning ' + 'of function code.', sInput: '/*jshint evil:true */' + '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' eval("");' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' (function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' }());' + ' eval("");' + '}());' }, { sTitle: 'Identification of a range of source elements in the middle of ' + 'function code.', sInput: '/*jshint evil:true */' + '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' eval("");' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + ' eval("");' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' eval("");' + ' (function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' }());' + ' eval("");' + '}());' }, { sTitle: 'Identification of a range of source elements at the end of ' + 'function code.', sInput: '/*jshint evil:true */' + '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' eval("");' + ' void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' "abcdefghijklmnopqrstuvwxyz";' + ' eval("");' + ' (function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + ' }());' + '}());' }, { sTitle: 'Evaluation with regard to String values of String literals and ' + 'String values derived from identifier names used as property' + 'accessors.', sInput: '(function() {' + ' var foo;' + ' void ["abcdefg", Math.abcdefg, "abcdef", Math.abcdef];' + '}());', sOutput: '(function() {' + ' var a = "abcdefg", foo;' + ' void [a, Math[a], "abcdef", Math.abcdef];' + '}());' }, { sTitle: 'Evaluation with regard to the necessity of adding a variable ' + 'statement.', sInput: '/*jshint evil:true */' + '(function() {' + ' void ["abcdefgh", "abcdefgh"];' + '}());' + 'eval("");' + '(function() {' + ' void ["abcdefg", "abcdefg"];' + '}());' + 'eval("");' + '(function() {' + ' var foo;' + ' void ["abcd", "abcd"];' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' var a = "abcdefgh";' + ' void [a, a];' + '}());' + 'eval("");' + '(function() {' + ' void ["abcdefg", "abcdefg"];' + '}());' + 'eval("");' + '(function() {' + ' var a = "abcd", foo;' + ' void [a, a];' + '}());' }, { sTitle: 'Evaluation with regard to the necessity of enclosing source ' + 'elements.', sInput: '/*jshint evil:true */' + 'void ["abcdefghijklmnopqrstuvwxy", "abcdefghijklmnopqrstuvwxy"];' + 'eval("");' + 'void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' + 'eval("");' + '(function() {' + ' void ["abcdefgh", "abcdefgh"];' + '}());' + '(function() {' + ' void ["abcdefghijklmnopqrstuvwxy",' + ' "abcdefghijklmnopqrstuvwxy"];' + ' eval("");' + ' void ["abcdefghijklmnopqrstuvwx",' + ' "abcdefghijklmnopqrstuvwx"];' + ' eval("");' + ' (function() {' + ' void ["abcdefgh", "abcdefgh"];' + ' }());' + '}());', sOutput: '/*jshint evil:true */' + '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxy";' + ' void [a, a];' + '}());' + 'eval("");' + 'void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' + 'eval("");' + '(function() {' + ' var a = "abcdefgh";' + ' void [a, a];' + '}());' + '(function() {' + ' (function() {' + ' var a = "abcdefghijklmnopqrstuvwxy";' + ' void [a, a];' + ' }());' + ' eval("");' + ' void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' + ' eval("");' + ' (function() {' + ' var a = "abcdefgh";' + ' void [a, a];' + ' }());' + '}());' }, { sTitle: 'Employment of a closure while consolidating in global code.', sInput: 'void ["abcdefghijklmnopqrstuvwxyz",' + ' "abcdefghijklmnopqrstuvwxyz"];', sOutput: '(function() {' + ' var a = "abcdefghijklmnopqrstuvwxyz";' + ' void [a, a];' + '}());' }, { sTitle: 'Assignment of a shorter identifier to a value whose ' + 'consolidation results in a greater reduction of the number of ' + 'source characters.', sInput: '(function() {' + ' var b, c, d, e, f, g, h, i, j, k, l, m,' + ' n, o, p, q, r, s, t, u, v, w, x, y, z,' + ' A, B, C, D, E, F, G, H, I, J, K, L, M,' + ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' + ' $, _;' + ' void ["abcde", "abcde", "edcba", "edcba", "edcba"];' + '}());', sOutput: '(function() {' + ' var a = "edcba",' + ' b, c, d, e, f, g, h, i, j, k, l, m,' + ' n, o, p, q, r, s, t, u, v, w, x, y, z,' + ' A, B, C, D, E, F, G, H, I, J, K, L, M,' + ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' + ' $, _;' + ' void ["abcde", "abcde", a, a, a];' + '}());' } ].forEach(cAssert); }()); } /* 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: */