diff --git a/lib/uglify.js b/lib/uglify.js
index d48f10d..2273169 100644
--- a/lib/uglify.js
+++ b/lib/uglify.js
@@ -48,7 +48,2606 @@
};
}
return this.require.define;
-}).call(this)({"parse-js": function(exports, require, module) {/***********************************************************************
+}).call(this)({"consolidator": function(exports, require, module) {/**
+ * @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: */
+
+}, "parse-js": function(exports, require, module) {/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
@@ -241,7 +2840,7 @@ var OPERATORS = array_to_hash([
var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
-var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
+var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{(,.;:"));
var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
@@ -536,10 +3135,10 @@ function tokenizer($TEXT) {
};
function read_name() {
- var backslash = false, name = "", ch;
+ var backslash = false, name = "", ch, escaped = false, hex;
while ((ch = peek()) != null) {
if (!backslash) {
- if (ch == "\\") backslash = true, next();
+ if (ch == "\\") escaped = backslash = true, next();
else if (is_identifier_char(ch)) name += next();
else break;
}
@@ -551,6 +3150,10 @@ function tokenizer($TEXT) {
backslash = false;
}
}
+ if (HOP(KEYWORDS, name) && escaped) {
+ hex = name.charCodeAt(0).toString(16).toUpperCase();
+ name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
+ }
return name;
};
@@ -1677,12 +4280,13 @@ function Scope(parent) {
};
var base54 = (function(){
- var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
+ var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
return function(num) {
- var ret = "";
+ var ret = "", base = 54;
do {
- ret = DIGITS.charAt(num % 54) + ret;
- num = Math.floor(num / 54);
+ ret += DIGITS.charAt(num % base);
+ num = Math.floor(num / base);
+ base = 64;
} while (num > 0);
return ret;
};
@@ -2778,7 +5382,6 @@ function make_string(str, ascii_only) {
case "\f": return "\\f";
case "\n": return "\\n";
case "\r": return "\\r";
- case "\t": return "\\t";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
case '"': ++dq; return '"';
@@ -3023,7 +5626,7 @@ function gen_code(ast, options) {
if (expr[0] == "num") {
if (!/\./.test(expr[1]))
out += ".";
- } else if (needs_parens(expr))
+ } else if (expr[0] != "function" && needs_parens(expr))
out = "(" + out + ")";
while (i < arguments.length)
out += "." + make_name(arguments[i++]);
@@ -3121,7 +5724,7 @@ function gen_code(ast, options) {
if (p.length == 3) {
// getter/setter. The name is in p[0], the arg.list in p[1][2], the
// body in p[1][3] and type ("get" / "set") in p[2].
- return indent(make_function(p[0], p[1][2], p[1][3], p[2]));
+ return indent(make_function(p[0], p[1][2], p[1][3], p[2], true));
}
var key = p[0], val = parenthesize(p[1], "seq");
if (options.quote_keys) {
@@ -3198,14 +5801,14 @@ function gen_code(ast, options) {
return make(th);
};
- function make_function(name, args, body, keyword) {
+ function make_function(name, args, body, keyword, no_parens) {
var out = keyword || "function";
if (name) {
out += " " + make_name(name);
}
out += "(" + add_commas(MAP(args, make_name)) + ")";
out = add_spaces([ out, make_block(body) ]);
- return needs_parens(this) ? "(" + out + ")" : out;
+ return (!no_parens && needs_parens(this)) ? "(" + out + ")" : out;
};
function must_has_semicolon(node) {
@@ -3448,6 +6051,10 @@ function ast_squeeze_more(ast) {
}
},
"call": function(expr, args) {
+ if (expr[0] == "dot" && expr[1][0] == "string" && args.length == 1
+ && (args[0][1] > 0 && expr[2] == "substring" || expr[2] == "substr")) {
+ return [ "call", [ "dot", expr[1], "slice"], args];
+ }
if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
// foo.toString() ==> foo+""
return [ "binary", "+", expr[1], [ "string", "" ]];
diff --git a/vendor/uglifyjs b/vendor/uglifyjs
index f5f3870..b969f45 160000
--- a/vendor/uglifyjs
+++ b/vendor/uglifyjs
@@ -1 +1 @@
-Subproject commit f5f3870bab31ce49b94d580c26c4d5bc68262c4b
+Subproject commit b969f455672847885c26b22f53051ed1bd7747ad