/**
* @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:
*
* - the difference in the number of terminal symbols
* between the original source text and the one with the
* first primitive value consolidated, and
* - the difference in the number of terminal symbols
* between the original source text and the one with the
* second primitive value consolidated.
*
* @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: */