bin/r.js in requirejs-rails-0.7.2 vs bin/r.js in requirejs-rails-0.7.3

- old
+ new

@@ -1,7 +1,7 @@ /** - * @license r.js 1.0.7 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license r.js 1.0.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ /* @@ -9,20 +9,20 @@ * in either a Java/Rhino or Node environment. It is modified by the top-level * dist.js file to inject other files to completely enable this file. It is * the shell of the r.js file. */ -/*jslint strict: false, evil: true, nomen: false */ +/*jslint evil: true, nomen: true */ /*global readFile: true, process: false, Packages: false, print: false, -console: false, java: false, module: false */ +console: false, java: false, module: false, requirejsVars */ var requirejs, require, define; (function (console, args, readFileFunc) { var fileName, env, fs, vm, path, exec, rhinoContext, dir, nodeRequire, nodeDefine, exists, reqMain, loadedOptimizedLib, - version = '1.0.7', + version = '1.0.8', jsSuffixRegExp = /\.js$/, commandOption = '', useLibLoaded = {}, //Used by jslib/rhino/args.js rhinoArgs = args, @@ -100,21 +100,21 @@ fileName = process.argv[3]; } } /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 1.0.7 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 1.0.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ /*jslint strict: false, plusplus: false, sub: true */ /*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ -(function () { +(function (undefined) { //Change this version number for each release. - var version = "1.0.7", + var version = "1.0.8", commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /require\(\s*["']([^'"\s]+)["']\s*\)/g, currDirRegExp = /^\.\//, jsSuffixRegExp = /\.js$/, ostring = Object.prototype.toString, @@ -524,22 +524,23 @@ cb = manager.callback, map = manager.map, fullName = map.fullName, args = manager.deps, listeners = manager.listeners, + execCb = config.requireExecCb || req.execCb, cjsModule; //Call the callback to define the module, if necessary. if (cb && isFunction(cb)) { if (config.catchError.define) { try { - ret = req.execCb(fullName, manager.callback, args, defined[fullName]); + ret = execCb(fullName, manager.callback, args, defined[fullName]); } catch (e) { err = e; } } else { - ret = req.execCb(fullName, manager.callback, args, defined[fullName]); + ret = execCb(fullName, manager.callback, args, defined[fullName]); } if (fullName) { //If setting exports via "module" is in play, //favor that over return value and exports. After that, @@ -1275,11 +1276,11 @@ if (map.prefix) { callPlugin(map.prefix, manager); } else { //Regular dependency. if (!urlFetched[url] && !loaded[fullName]) { - req.load(context, fullName, url); + (config.requireLoad || req.load)(context, fullName, url); //Mark the URL as fetched, but only if it is //not an empty: URL, used by the optimizer. //In that case we need to be sure to call //load() for each module that is mapped to @@ -1579,11 +1580,12 @@ //Normalize module name if have a base relative module name to work from. moduleName = normalize(moduleName, relModuleMap && relModuleMap.fullName); //If a colon is in the URL, it indicates a protocol is used and it is just - //an URL to a file, or if it starts with a slash or ends with .js, it is just a plain file. + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. //The slash is important for protocol-less URLs as well as full paths. if (req.jsExtRegExp.test(moduleName)) { //Just a plain path, not module name lookup, so just return it. //Add extension if it is included. This is a bit wonky, only non-.js things pass //an extension, this method probably needs to be reworked. @@ -1615,11 +1617,11 @@ } } //Join the path parts together, then figure out if baseUrl is needed. url = syms.join("/") + (ext || ".js"); - url = (url.charAt(0) === '/' || url.match(/^\w+:/) ? "" : config.baseUrl) + url; + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? "" : config.baseUrl) + url; } return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) : url; @@ -1979,11 +1981,19 @@ //addEventListener support, which fire the onload event for a //script right after the script execution. See: //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution //UNFORTUNATELY Opera implements attachEvent but does not follow the script //script execution mode. - if (node.attachEvent && !isOpera) { + if (node.attachEvent && + // check if node.attachEvent is artificially added by custom script or + // natively supported by browser + // read https://github.com/jrburke/requirejs/issues/187 + // if we can NOT find [native code] then it must NOT natively supported. + // in IE8, node.attachEvent does not have toString() + // TODO: a better way to check interactive mode + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code]') < 0) && + !isOpera) { //Probably IE. IE (at least 6-8) do not fire //script onload right after executing the script, so //we cannot tie the anonymous define call to a name. //However, IE reports the script as being in "interactive" //readyState at the time of the define call. @@ -2245,11 +2255,11 @@ return '(function (require, requirejs, define) { ' + contents + '\n}(requirejsVars.require, requirejsVars.requirejs, requirejsVars.define));'; }; - req.load = function (context, moduleName, url) { + requirejsVars.nodeLoad = req.load = function (context, moduleName, url) { var contents, err; //Indicate a the module is in process of loading. context.scriptCount += 1; @@ -2291,10 +2301,13 @@ req.exec = function (text) { /*jslint evil: true */ text = req.makeNodeWrapper(text); return eval(text); }; + + //Hold on to the original execCb to use in useLib calls. + requirejsVars.nodeRequireExecCb = require.execCb; }()); } //Support a default file name to execute. Useful for hosted envs @@ -2356,10 +2369,44 @@ } }); }()); if(env === 'node') { /** + * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/requirejs for details + */ + +/*jslint strict: false */ +/*global define: false, load: false */ + +//Needed so that rhino/assert can return a stub for uglify's consolidator.js +define('node/assert', ['assert'], function (assert) { + return assert; +}); + +} + +if(env === 'rhino') { +/** + * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/requirejs for details + */ + +/*jslint strict: false */ +/*global define: false, load: false */ + +//Just a stub for use with uglify's consolidator.js +define('rhino/assert', function () { + return {}; +}); + +} + +if(env === 'node') { +/** * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3147,11 +3194,2614 @@ //Just a blank file to use when building the optimizer with the optimizer, //so that the build does not attempt to inline some env modules, //like Node's fs and path. -define('uglifyjs/parse-js', ["require", "exports", "module"], function(require, exports, module) { +define('uglifyjs/consolidator', ["require", "exports", "module", "env!env/assert", "./parse-js", "./process"], function(require, exports, module, assert) { + +/** + * @preserve Copyright 2012 Robert Gust-Bardon <http://robert.gust-bardon.org/>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * @fileoverview Enhances <a href="https://github.com/mishoo/UglifyJS/" + * >UglifyJS</a> with consolidation of null, Boolean, and String values. + * <p>Also known as aliasing, this feature has been deprecated in <a href= + * "http://closure-compiler.googlecode.com/">the Closure Compiler</a> since its + * initial release, where it is unavailable from the <abbr title= + * "command line interface">CLI</a>. The Closure Compiler allows one to log and + * influence this process. In contrast, this implementation does not introduce + * any variable declarations in global code and derives String values from + * identifier names used as property accessors.</p> + * <p>Consolidating literals may worsen the data compression ratio when an <a + * href="http://tools.ietf.org/html/rfc2616#section-3.5">encoding + * transformation</a> is applied. For instance, <a href= + * "http://code.jquery.com/jquery-1.7.1.js">jQuery 1.7.1</a> takes 248235 bytes. + * Building it with <a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5"> + * UglifyJS v1.2.5</a> results in 93647 bytes (37.73% of the original) which are + * then compressed to 33154 bytes (13.36% of the original) using <a href= + * "http://linux.die.net/man/1/gzip">gzip(1)</a>. Building it with the same + * version of UglifyJS 1.2.5 patched with the implementation of consolidation + * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison + * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes + * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned + * 33154 bytes).</p> + * <p>Written in <a href="http://es5.github.com/#x4.2.2">the strict variant</a> + * of <a href="http://es5.github.com/">ECMA-262 5.1 Edition</a>. Encoded in <a + * href="http://tools.ietf.org/html/rfc3629">UTF-8</a>. Follows <a href= + * "http://google-styleguide.googlecode.com/svn-history/r76/trunk/javascriptguide.xml" + * >Revision 2.28 of the Google JavaScript Style Guide</a> (except for the + * discouraged use of the {@code function} tag and the {@code namespace} tag). + * 100% typed for the <a href= + * "http://closure-compiler.googlecode.com/files/compiler-20120123.tar.gz" + * >Closure Compiler Version 1741</a>.</p> + * <p>Should you find this software useful, please consider <a href= + * "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JZLW72X8FD4WG" + * >a donation</a>.</p> + * @author follow.me@RGustBardon (Robert Gust-Bardon) + * @supported Tested with: + * <ul> + * <li><a href="http://nodejs.org/dist/v0.6.10/">Node v0.6.10</a>,</li> + * <li><a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">UglifyJS + * v1.2.5</a>.</li> + * </ul> + */ + +/*global console:false, exports:true, module:false, require:false */ +/*jshint sub:true */ +/** + * Consolidates null, Boolean, and String values found inside an <abbr title= + * "abstract syntax tree">AST</abbr>. + * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object + * representing an <abbr title="abstract syntax tree">AST</abbr>. + * @return {!TSyntacticCodeUnit} An array-like object representing an <abbr + * title="abstract syntax tree">AST</abbr> with its null, Boolean, and + * String values consolidated. + */ +// TODO(user) Consolidation of mathematical values found in numeric literals. +// TODO(user) Unconsolidation. +// TODO(user) Consolidation of ECMA-262 6th Edition programs. +// TODO(user) Rewrite in ECMA-262 6th Edition. +exports['ast_consolidate'] = function(oAbstractSyntaxTree) { + 'use strict'; + /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true, + latedef:true, newcap:true, noarge:true, noempty:true, nonew:true, + onevar:true, plusplus:true, regexp:true, undef:true, strict:true, + sub:false, trailing:true */ + + var _, + /** + * A record consisting of data about one or more source elements. + * @constructor + * @nosideeffects + */ + TSourceElementsData = function() { + /** + * The category of the elements. + * @type {number} + * @see ESourceElementCategories + */ + this.nCategory = ESourceElementCategories.N_OTHER; + /** + * The number of occurrences (within the elements) of each primitive + * value that could be consolidated. + * @type {!Array.<!Object.<string, number>>} + */ + this.aCount = []; + this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {}; + this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {}; + this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] = + {}; + /** + * Identifier names found within the elements. + * @type {!Array.<string>} + */ + this.aIdentifiers = []; + /** + * Prefixed representation Strings of each primitive value that could be + * consolidated within the elements. + * @type {!Array.<string>} + */ + this.aPrimitiveValues = []; + }, + /** + * A record consisting of data about a primitive value that could be + * consolidated. + * @constructor + * @nosideeffects + */ + TPrimitiveValue = function() { + /** + * The difference in the number of terminal symbols between the original + * source text and the one with the primitive value consolidated. If the + * difference is positive, the primitive value is considered worthwhile. + * @type {number} + */ + this.nSaving = 0; + /** + * An identifier name of the variable that will be declared and assigned + * the primitive value if the primitive value is consolidated. + * @type {string} + */ + this.sName = ''; + }, + /** + * A record consisting of data on what to consolidate within the range of + * source elements that is currently being considered. + * @constructor + * @nosideeffects + */ + TSolution = function() { + /** + * An object whose keys are prefixed representation Strings of each + * primitive value that could be consolidated within the elements and + * whose values are corresponding data about those primitive values. + * @type {!Object.<string, {nSaving: number, sName: string}>} + * @see TPrimitiveValue + */ + this.oPrimitiveValues = {}; + /** + * The difference in the number of terminal symbols between the original + * source text and the one with all the worthwhile primitive values + * consolidated. + * @type {number} + * @see TPrimitiveValue#nSaving + */ + this.nSavings = 0; + }, + /** + * The processor of <abbr title="abstract syntax tree">AST</abbr>s found + * in UglifyJS. + * @namespace + * @type {!TProcessor} + */ + oProcessor = (/** @type {!TProcessor} */ require('./process')), + /** + * A record consisting of a number of constants that represent the + * difference in the number of terminal symbols between a source text with + * a modified syntactic code unit and the original one. + * @namespace + * @type {!Object.<string, number>} + */ + oWeights = { + /** + * The difference in the number of punctuators required by the bracket + * notation and the dot notation. + * <p><code>'[]'.length - '.'.length</code></p> + * @const + * @type {number} + */ + N_PROPERTY_ACCESSOR: 1, + /** + * The number of punctuators required by a variable declaration with an + * initialiser. + * <p><code>':'.length + ';'.length</code></p> + * @const + * @type {number} + */ + N_VARIABLE_DECLARATION: 2, + /** + * The number of terminal symbols required to introduce a variable + * statement (excluding its variable declaration list). + * <p><code>'var '.length</code></p> + * @const + * @type {number} + */ + N_VARIABLE_STATEMENT_AFFIXATION: 4, + /** + * The number of terminal symbols needed to enclose source elements + * within a function call with no argument values to a function with an + * empty parameter list. + * <p><code>'(function(){}());'.length</code></p> + * @const + * @type {number} + */ + N_CLOSURE: 17 + }, + /** + * Categories of primary expressions from which primitive values that + * could be consolidated are derivable. + * @namespace + * @enum {number} + */ + EPrimaryExpressionCategories = { + /** + * Identifier names used as property accessors. + * @type {number} + */ + N_IDENTIFIER_NAMES: 0, + /** + * String literals. + * @type {number} + */ + N_STRING_LITERALS: 1, + /** + * Null and Boolean literals. + * @type {number} + */ + N_NULL_AND_BOOLEAN_LITERALS: 2 + }, + /** + * Prefixes of primitive values that could be consolidated. + * The String values of the prefixes must have same number of characters. + * The prefixes must not be used in any properties defined in any version + * of <a href= + * "http://www.ecma-international.org/publications/standards/Ecma-262.htm" + * >ECMA-262</a>. + * @namespace + * @enum {string} + */ + EValuePrefixes = { + /** + * Identifies String values. + * @type {string} + */ + S_STRING: '#S', + /** + * Identifies null and Boolean values. + * @type {string} + */ + S_SYMBOLIC: '#O' + }, + /** + * Categories of source elements in terms of their appropriateness of + * having their primitive values consolidated. + * @namespace + * @enum {number} + */ + ESourceElementCategories = { + /** + * Identifies a source element that includes the <a href= + * "http://es5.github.com/#x12.10">{@code with}</a> statement. + * @type {number} + */ + N_WITH: 0, + /** + * Identifies a source element that includes the <a href= + * "http://es5.github.com/#x15.1.2.1">{@code eval}</a> identifier name. + * @type {number} + */ + N_EVAL: 1, + /** + * Identifies a source element that must be excluded from the process + * unless its whole scope is examined. + * @type {number} + */ + N_EXCLUDABLE: 2, + /** + * Identifies source elements not posing any problems. + * @type {number} + */ + N_OTHER: 3 + }, + /** + * The list of literals (other than the String ones) whose primitive + * values can be consolidated. + * @const + * @type {!Array.<string>} + */ + A_OTHER_SUBSTITUTABLE_LITERALS = [ + 'null', // The null literal. + 'false', // The Boolean literal {@code false}. + 'true' // The Boolean literal {@code true}. + ]; + + (/** + * Consolidates all worthwhile primitive values in a syntactic code unit. + * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object + * representing the branch of the abstract syntax tree representing the + * syntactic code unit along with its scope. + * @see TPrimitiveValue#nSaving + */ + function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) { + var _, + /** + * Indicates whether the syntactic code unit represents global code. + * @type {boolean} + */ + bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0], + /** + * Indicates whether the whole scope is being examined. + * @type {boolean} + */ + bIsWhollyExaminable = !bIsGlobal, + /** + * An array-like object representing source elements that constitute a + * syntactic code unit. + * @type {!TSyntacticCodeUnit} + */ + oSourceElements, + /** + * A record consisting of data about the source element that is + * currently being examined. + * @type {!TSourceElementsData} + */ + oSourceElementData, + /** + * The scope of the syntactic code unit. + * @type {!TScope} + */ + oScope, + /** + * An instance of an object that allows the traversal of an <abbr + * title="abstract syntax tree">AST</abbr>. + * @type {!TWalker} + */ + oWalker, + /** + * An object encompassing collections of functions used during the + * traversal of an <abbr title="abstract syntax tree">AST</abbr>. + * @namespace + * @type {!Object.<string, !Object.<string, function(...[*])>>} + */ + oWalkers = { + /** + * A collection of functions used during the surveyance of source + * elements. + * @namespace + * @type {!Object.<string, function(...[*])>} + */ + oSurveySourceElement: { + /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys. + /** + * Classifies the source element as excludable if it does not + * contain a {@code with} statement or the {@code eval} identifier + * name. Adds the identifier of the function and its formal + * parameters to the list of identifier names found. + * @param {string} sIdentifier The identifier of the function. + * @param {!Array.<string>} aFormalParameterList Formal parameters. + * @param {!TSyntacticCodeUnit} oFunctionBody Function code. + */ + 'defun': function( + sIdentifier, + aFormalParameterList, + oFunctionBody) { + fClassifyAsExcludable(); + fAddIdentifier(sIdentifier); + aFormalParameterList.forEach(fAddIdentifier); + }, + /** + * Increments the count of the number of occurrences of the String + * value that is equivalent to the sequence of terminal symbols + * that constitute the encountered identifier name. + * @param {!TSyntacticCodeUnit} oExpression The nonterminal + * MemberExpression. + * @param {string} sIdentifierName The identifier name used as the + * property accessor. + * @return {!Array} The encountered branch of an <abbr title= + * "abstract syntax tree">AST</abbr> with its nonterminal + * MemberExpression traversed. + */ + 'dot': function(oExpression, sIdentifierName) { + fCountPrimaryExpression( + EPrimaryExpressionCategories.N_IDENTIFIER_NAMES, + EValuePrefixes.S_STRING + sIdentifierName); + return ['dot', oWalker.walk(oExpression), sIdentifierName]; + }, + /** + * Adds the optional identifier of the function and its formal + * parameters to the list of identifier names found. + * @param {?string} sIdentifier The optional identifier of the + * function. + * @param {!Array.<string>} aFormalParameterList Formal parameters. + * @param {!TSyntacticCodeUnit} oFunctionBody Function code. + */ + 'function': function( + sIdentifier, + aFormalParameterList, + oFunctionBody) { + if ('string' === typeof sIdentifier) { + fAddIdentifier(sIdentifier); + } + aFormalParameterList.forEach(fAddIdentifier); + }, + /** + * Either increments the count of the number of occurrences of the + * encountered null or Boolean value or classifies a source element + * as containing the {@code eval} identifier name. + * @param {string} sIdentifier The identifier encountered. + */ + 'name': function(sIdentifier) { + if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) { + fCountPrimaryExpression( + EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS, + EValuePrefixes.S_SYMBOLIC + sIdentifier); + } else { + if ('eval' === sIdentifier) { + oSourceElementData.nCategory = + ESourceElementCategories.N_EVAL; + } + fAddIdentifier(sIdentifier); + } + }, + /** + * Classifies the source element as excludable if it does not + * contain a {@code with} statement or the {@code eval} identifier + * name. + * @param {TSyntacticCodeUnit} oExpression The expression whose + * value is to be returned. + */ + 'return': function(oExpression) { + fClassifyAsExcludable(); + }, + /** + * Increments the count of the number of occurrences of the + * encountered String value. + * @param {string} sStringValue The String value of the string + * literal encountered. + */ + 'string': function(sStringValue) { + if (sStringValue.length > 0) { + fCountPrimaryExpression( + EPrimaryExpressionCategories.N_STRING_LITERALS, + EValuePrefixes.S_STRING + sStringValue); + } + }, + /** + * Adds the identifier reserved for an exception to the list of + * identifier names found. + * @param {!TSyntacticCodeUnit} oTry A block of code in which an + * exception can occur. + * @param {Array} aCatch The identifier reserved for an exception + * and a block of code to handle the exception. + * @param {TSyntacticCodeUnit} oFinally An optional block of code + * to be evaluated regardless of whether an exception occurs. + */ + 'try': function(oTry, aCatch, oFinally) { + if (Array.isArray(aCatch)) { + fAddIdentifier(aCatch[0]); + } + }, + /** + * Classifies the source element as excludable if it does not + * contain a {@code with} statement or the {@code eval} identifier + * name. Adds the identifier of each declared variable to the list + * of identifier names found. + * @param {!Array.<!Array>} aVariableDeclarationList Variable + * declarations. + */ + 'var': function(aVariableDeclarationList) { + fClassifyAsExcludable(); + aVariableDeclarationList.forEach(fAddVariable); + }, + /** + * Classifies a source element as containing the {@code with} + * statement. + * @param {!TSyntacticCodeUnit} oExpression An expression whose + * value is to be converted to a value of type Object and + * become the binding object of a new object environment + * record of a new lexical environment in which the statement + * is to be executed. + * @param {!TSyntacticCodeUnit} oStatement The statement to be + * executed in the augmented lexical environment. + * @return {!Array} An empty array to stop the traversal. + */ + 'with': function(oExpression, oStatement) { + oSourceElementData.nCategory = ESourceElementCategories.N_WITH; + return []; + } + /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys. + }, + /** + * A collection of functions used while looking for nested functions. + * @namespace + * @type {!Object.<string, function(...[*])>} + */ + oExamineFunctions: { + /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys. + /** + * Orders an examination of a nested function declaration. + * @this {!TSyntacticCodeUnit} An array-like object representing + * the branch of an <abbr title="abstract syntax tree" + * >AST</abbr> representing the syntactic code unit along with + * its scope. + * @return {!Array} An empty array to stop the traversal. + */ + 'defun': function() { + fExamineSyntacticCodeUnit(this); + return []; + }, + /** + * Orders an examination of a nested function expression. + * @this {!TSyntacticCodeUnit} An array-like object representing + * the branch of an <abbr title="abstract syntax tree" + * >AST</abbr> representing the syntactic code unit along with + * its scope. + * @return {!Array} An empty array to stop the traversal. + */ + 'function': function() { + fExamineSyntacticCodeUnit(this); + return []; + } + /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys. + } + }, + /** + * Records containing data about source elements. + * @type {Array.<TSourceElementsData>} + */ + aSourceElementsData = [], + /** + * The index (in the source text order) of the source element + * immediately following a <a href="http://es5.github.com/#x14.1" + * >Directive Prologue</a>. + * @type {number} + */ + nAfterDirectivePrologue = 0, + /** + * The index (in the source text order) of the source element that is + * currently being considered. + * @type {number} + */ + nPosition, + /** + * The index (in the source text order) of the source element that is + * the last element of the range of source elements that is currently + * being considered. + * @type {(undefined|number)} + */ + nTo, + /** + * Initiates the traversal of a source element. + * @param {!TWalker} oWalker An instance of an object that allows the + * traversal of an abstract syntax tree. + * @param {!TSyntacticCodeUnit} oSourceElement A source element from + * which the traversal should commence. + * @return {function(): !TSyntacticCodeUnit} A function that is able to + * initiate the traversal from a given source element. + */ + cContext = function(oWalker, oSourceElement) { + /** + * @return {!TSyntacticCodeUnit} A function that is able to + * initiate the traversal from a given source element. + */ + var fLambda = function() { + return oWalker.walk(oSourceElement); + }; + + return fLambda; + }, + /** + * Classifies the source element as excludable if it does not + * contain a {@code with} statement or the {@code eval} identifier + * name. + */ + fClassifyAsExcludable = function() { + if (oSourceElementData.nCategory === + ESourceElementCategories.N_OTHER) { + oSourceElementData.nCategory = + ESourceElementCategories.N_EXCLUDABLE; + } + }, + /** + * Adds an identifier to the list of identifier names found. + * @param {string} sIdentifier The identifier to be added. + */ + fAddIdentifier = function(sIdentifier) { + if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) { + oSourceElementData.aIdentifiers.push(sIdentifier); + } + }, + /** + * Adds the identifier of a variable to the list of identifier names + * found. + * @param {!Array} aVariableDeclaration A variable declaration. + */ + fAddVariable = function(aVariableDeclaration) { + fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]); + }, + /** + * Increments the count of the number of occurrences of the prefixed + * String representation attributed to the primary expression. + * @param {number} nCategory The category of the primary expression. + * @param {string} sName The prefixed String representation attributed + * to the primary expression. + */ + fCountPrimaryExpression = function(nCategory, sName) { + if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) { + oSourceElementData.aCount[nCategory][sName] = 0; + if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) { + oSourceElementData.aPrimitiveValues.push(sName); + } + } + oSourceElementData.aCount[nCategory][sName] += 1; + }, + /** + * Consolidates all worthwhile primitive values in a range of source + * elements. + * @param {number} nFrom The index (in the source text order) of the + * source element that is the first element of the range. + * @param {number} nTo The index (in the source text order) of the + * source element that is the last element of the range. + * @param {boolean} bEnclose Indicates whether the range should be + * enclosed within a function call with no argument values to a + * function with an empty parameter list if any primitive values + * are consolidated. + * @see TPrimitiveValue#nSaving + */ + fExamineSourceElements = function(nFrom, nTo, bEnclose) { + var _, + /** + * The index of the last mangled name. + * @type {number} + */ + nIndex = oScope.cname, + /** + * The index of the source element that is currently being + * considered. + * @type {number} + */ + nPosition, + /** + * A collection of functions used during the consolidation of + * primitive values and identifier names used as property + * accessors. + * @namespace + * @type {!Object.<string, function(...[*])>} + */ + oWalkersTransformers = { + /** + * If the String value that is equivalent to the sequence of + * terminal symbols that constitute the encountered identifier + * name is worthwhile, a syntactic conversion from the dot + * notation to the bracket notation ensues with that sequence + * being substituted by an identifier name to which the value + * is assigned. + * Applies to property accessors that use the dot notation. + * @param {!TSyntacticCodeUnit} oExpression The nonterminal + * MemberExpression. + * @param {string} sIdentifierName The identifier name used as + * the property accessor. + * @return {!Array} A syntactic code unit that is equivalent to + * the one encountered. + * @see TPrimitiveValue#nSaving + */ + 'dot': function(oExpression, sIdentifierName) { + /** + * The prefixed String value that is equivalent to the + * sequence of terminal symbols that constitute the + * encountered identifier name. + * @type {string} + */ + var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName; + + return oSolutionBest.oPrimitiveValues.hasOwnProperty( + sPrefixed) && + oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ? + ['sub', + oWalker.walk(oExpression), + ['name', + oSolutionBest.oPrimitiveValues[sPrefixed].sName]] : + ['dot', oWalker.walk(oExpression), sIdentifierName]; + }, + /** + * If the encountered identifier is a null or Boolean literal + * and its value is worthwhile, the identifier is substituted + * by an identifier name to which that value is assigned. + * Applies to identifier names. + * @param {string} sIdentifier The identifier encountered. + * @return {!Array} A syntactic code unit that is equivalent to + * the one encountered. + * @see TPrimitiveValue#nSaving + */ + 'name': function(sIdentifier) { + /** + * The prefixed representation String of the identifier. + * @type {string} + */ + var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier; + + return [ + 'name', + oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) && + oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ? + oSolutionBest.oPrimitiveValues[sPrefixed].sName : + sIdentifier + ]; + }, + /** + * If the encountered String value is worthwhile, it is + * substituted by an identifier name to which that value is + * assigned. + * Applies to String values. + * @param {string} sStringValue The String value of the string + * literal encountered. + * @return {!Array} A syntactic code unit that is equivalent to + * the one encountered. + * @see TPrimitiveValue#nSaving + */ + 'string': function(sStringValue) { + /** + * The prefixed representation String of the primitive value + * of the literal. + * @type {string} + */ + var sPrefixed = + EValuePrefixes.S_STRING + sStringValue; + + return oSolutionBest.oPrimitiveValues.hasOwnProperty( + sPrefixed) && + oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ? + ['name', + oSolutionBest.oPrimitiveValues[sPrefixed].sName] : + ['string', sStringValue]; + } + }, + /** + * Such data on what to consolidate within the range of source + * elements that is currently being considered that lead to the + * greatest known reduction of the number of the terminal symbols + * in comparison to the original source text. + * @type {!TSolution} + */ + oSolutionBest = new TSolution(), + /** + * Data representing an ongoing attempt to find a better + * reduction of the number of the terminal symbols in comparison + * to the original source text than the best one that is + * currently known. + * @type {!TSolution} + * @see oSolutionBest + */ + oSolutionCandidate = new TSolution(), + /** + * A record consisting of data about the range of source elements + * that is currently being examined. + * @type {!TSourceElementsData} + */ + oSourceElementsData = new TSourceElementsData(), + /** + * Variable declarations for each primitive value that is to be + * consolidated within the elements. + * @type {!Array.<!Array>} + */ + aVariableDeclarations = [], + /** + * Augments a list with a prefixed representation String. + * @param {!Array.<string>} aList A list that is to be augmented. + * @return {function(string)} A function that augments a list + * with a prefixed representation String. + */ + cAugmentList = function(aList) { + /** + * @param {string} sPrefixed Prefixed representation String of + * a primitive value that could be consolidated within the + * elements. + */ + var fLambda = function(sPrefixed) { + if (-1 === aList.indexOf(sPrefixed)) { + aList.push(sPrefixed); + } + }; + + return fLambda; + }, + /** + * Adds the number of occurrences of a primitive value of a given + * category that could be consolidated in the source element with + * a given index to the count of occurrences of that primitive + * value within the range of source elements that is currently + * being considered. + * @param {number} nPosition The index (in the source text order) + * of a source element. + * @param {number} nCategory The category of the primary + * expression from which the primitive value is derived. + * @return {function(string)} A function that performs the + * addition. + * @see cAddOccurrencesInCategory + */ + cAddOccurrences = function(nPosition, nCategory) { + /** + * @param {string} sPrefixed The prefixed representation String + * of a primitive value. + */ + var fLambda = function(sPrefixed) { + if (!oSourceElementsData.aCount[nCategory].hasOwnProperty( + sPrefixed)) { + oSourceElementsData.aCount[nCategory][sPrefixed] = 0; + } + oSourceElementsData.aCount[nCategory][sPrefixed] += + aSourceElementsData[nPosition].aCount[nCategory][ + sPrefixed]; + }; + + return fLambda; + }, + /** + * Adds the number of occurrences of each primitive value of a + * given category that could be consolidated in the source + * element with a given index to the count of occurrences of that + * primitive values within the range of source elements that is + * currently being considered. + * @param {number} nPosition The index (in the source text order) + * of a source element. + * @return {function(number)} A function that performs the + * addition. + * @see fAddOccurrences + */ + cAddOccurrencesInCategory = function(nPosition) { + /** + * @param {number} nCategory The category of the primary + * expression from which the primitive value is derived. + */ + var fLambda = function(nCategory) { + Object.keys( + aSourceElementsData[nPosition].aCount[nCategory] + ).forEach(cAddOccurrences(nPosition, nCategory)); + }; + + return fLambda; + }, + /** + * Adds the number of occurrences of each primitive value that + * could be consolidated in the source element with a given index + * to the count of occurrences of that primitive values within + * the range of source elements that is currently being + * considered. + * @param {number} nPosition The index (in the source text order) + * of a source element. + */ + fAddOccurrences = function(nPosition) { + Object.keys(aSourceElementsData[nPosition].aCount).forEach( + cAddOccurrencesInCategory(nPosition)); + }, + /** + * Creates a variable declaration for a primitive value if that + * primitive value is to be consolidated within the elements. + * @param {string} sPrefixed Prefixed representation String of a + * primitive value that could be consolidated within the + * elements. + * @see aVariableDeclarations + */ + cAugmentVariableDeclarations = function(sPrefixed) { + if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) { + aVariableDeclarations.push([ + oSolutionBest.oPrimitiveValues[sPrefixed].sName, + [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ? + 'name' : 'string', + sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)] + ]); + } + }, + /** + * Sorts primitive values with regard to the difference in the + * number of terminal symbols between the original source text + * and the one with those primitive values consolidated. + * @param {string} sPrefixed0 The prefixed representation String + * of the first of the two primitive values that are being + * compared. + * @param {string} sPrefixed1 The prefixed representation String + * of the second of the two primitive values that are being + * compared. + * @return {number} + * <dl> + * <dt>-1</dt> + * <dd>if the first primitive value must be placed before + * the other one,</dd> + * <dt>0</dt> + * <dd>if the first primitive value may be placed before + * the other one,</dd> + * <dt>1</dt> + * <dd>if the first primitive value must not be placed + * before the other one.</dd> + * </dl> + * @see TSolution.oPrimitiveValues + */ + cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) { + /** + * The difference between: + * <ol> + * <li>the difference in the number of terminal symbols + * between the original source text and the one with the + * first primitive value consolidated, and</li> + * <li>the difference in the number of terminal symbols + * between the original source text and the one with the + * second primitive value consolidated.</li> + * </ol> + * @type {number} + */ + var nDifference = + oSolutionCandidate.oPrimitiveValues[sPrefixed0].nSaving - + oSolutionCandidate.oPrimitiveValues[sPrefixed1].nSaving; + + return nDifference > 0 ? -1 : nDifference < 0 ? 1 : 0; + }, + /** + * Assigns an identifier name to a primitive value and calculates + * whether instances of that primitive value are worth + * consolidating. + * @param {string} sPrefixed The prefixed representation String + * of a primitive value that is being evaluated. + */ + fEvaluatePrimitiveValue = function(sPrefixed) { + var _, + /** + * The index of the last mangled name. + * @type {number} + */ + nIndex, + /** + * The representation String of the primitive value that is + * being evaluated. + * @type {string} + */ + sName = + sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length), + /** + * The number of source characters taken up by the + * representation String of the primitive value that is + * being evaluated. + * @type {number} + */ + nLengthOriginal = sName.length, + /** + * The number of source characters taken up by the + * identifier name that could substitute the primitive + * value that is being evaluated. + * substituted. + * @type {number} + */ + nLengthSubstitution, + /** + * The number of source characters taken up by by the + * representation String of the primitive value that is + * being evaluated when it is represented by a string + * literal. + * @type {number} + */ + nLengthString = oProcessor.make_string(sName).length; + + oSolutionCandidate.oPrimitiveValues[sPrefixed] = + new TPrimitiveValue(); + do { // Find an identifier unused in this or any nested scope. + nIndex = oScope.cname; + oSolutionCandidate.oPrimitiveValues[sPrefixed].sName = + oScope.next_mangled(); + } while (-1 !== oSourceElementsData.aIdentifiers.indexOf( + oSolutionCandidate.oPrimitiveValues[sPrefixed].sName)); + nLengthSubstitution = oSolutionCandidate.oPrimitiveValues[ + sPrefixed].sName.length; + if (0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC)) { + // foo:null, or foo:null; + oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -= + nLengthSubstitution + nLengthOriginal + + oWeights.N_VARIABLE_DECLARATION; + // null vs foo + oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving += + oSourceElementsData.aCount[ + EPrimaryExpressionCategories. + N_NULL_AND_BOOLEAN_LITERALS][sPrefixed] * + (nLengthOriginal - nLengthSubstitution); + } else { + // foo:'fromCharCode'; + oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -= + nLengthSubstitution + nLengthString + + oWeights.N_VARIABLE_DECLARATION; + // .fromCharCode vs [foo] + if (oSourceElementsData.aCount[ + EPrimaryExpressionCategories.N_IDENTIFIER_NAMES + ].hasOwnProperty(sPrefixed)) { + oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving += + oSourceElementsData.aCount[ + EPrimaryExpressionCategories.N_IDENTIFIER_NAMES + ][sPrefixed] * + (nLengthOriginal - nLengthSubstitution - + oWeights.N_PROPERTY_ACCESSOR); + } + // 'fromCharCode' vs foo + if (oSourceElementsData.aCount[ + EPrimaryExpressionCategories.N_STRING_LITERALS + ].hasOwnProperty(sPrefixed)) { + oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving += + oSourceElementsData.aCount[ + EPrimaryExpressionCategories.N_STRING_LITERALS + ][sPrefixed] * + (nLengthString - nLengthSubstitution); + } + } + if (oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving > + 0) { + oSolutionCandidate.nSavings += + oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving; + } else { + oScope.cname = nIndex; // Free the identifier name. + } + }, + /** + * Adds a variable declaration to an existing variable statement. + * @param {!Array} aVariableDeclaration A variable declaration + * with an initialiser. + */ + cAddVariableDeclaration = function(aVariableDeclaration) { + (/** @type {!Array} */ oSourceElements[nFrom][1]).unshift( + aVariableDeclaration); + }; + + if (nFrom > nTo) { + return; + } + // If the range is a closure, reuse the closure. + if (nFrom === nTo && + 'stat' === oSourceElements[nFrom][0] && + 'call' === oSourceElements[nFrom][1][0] && + 'function' === oSourceElements[nFrom][1][1][0]) { + fExamineSyntacticCodeUnit(oSourceElements[nFrom][1][1]); + return; + } + // Create a list of all derived primitive values within the range. + for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) { + aSourceElementsData[nPosition].aPrimitiveValues.forEach( + cAugmentList(oSourceElementsData.aPrimitiveValues)); + } + if (0 === oSourceElementsData.aPrimitiveValues.length) { + return; + } + for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) { + // Add the number of occurrences to the total count. + fAddOccurrences(nPosition); + // Add identifiers of this or any nested scope to the list. + aSourceElementsData[nPosition].aIdentifiers.forEach( + cAugmentList(oSourceElementsData.aIdentifiers)); + } + // Distribute identifier names among derived primitive values. + do { // If there was any progress, find a better distribution. + oSolutionBest = oSolutionCandidate; + if (Object.keys(oSolutionCandidate.oPrimitiveValues).length > 0) { + // Sort primitive values descending by their worthwhileness. + oSourceElementsData.aPrimitiveValues.sort(cSortPrimitiveValues); + } + oSolutionCandidate = new TSolution(); + oSourceElementsData.aPrimitiveValues.forEach( + fEvaluatePrimitiveValue); + oScope.cname = nIndex; + } while (oSolutionCandidate.nSavings > oSolutionBest.nSavings); + // Take the necessity of adding a variable statement into account. + if ('var' !== oSourceElements[nFrom][0]) { + oSolutionBest.nSavings -= oWeights.N_VARIABLE_STATEMENT_AFFIXATION; + } + if (bEnclose) { + // Take the necessity of forming a closure into account. + oSolutionBest.nSavings -= oWeights.N_CLOSURE; + } + if (oSolutionBest.nSavings > 0) { + // Create variable declarations suitable for UglifyJS. + Object.keys(oSolutionBest.oPrimitiveValues).forEach( + cAugmentVariableDeclarations); + // Rewrite expressions that contain worthwhile primitive values. + for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) { + oWalker = oProcessor.ast_walker(); + oSourceElements[nPosition] = + oWalker.with_walkers( + oWalkersTransformers, + cContext(oWalker, oSourceElements[nPosition])); + } + if ('var' === oSourceElements[nFrom][0]) { // Reuse the statement. + (/** @type {!Array.<!Array>} */ aVariableDeclarations.reverse( + )).forEach(cAddVariableDeclaration); + } else { // Add a variable statement. + Array.prototype.splice.call( + oSourceElements, + nFrom, + 0, + ['var', aVariableDeclarations]); + nTo += 1; + } + if (bEnclose) { + // Add a closure. + Array.prototype.splice.call( + oSourceElements, + nFrom, + 0, + ['stat', ['call', ['function', null, [], []], []]]); + // Copy source elements into the closure. + for (nPosition = nTo + 1; nPosition > nFrom; nPosition -= 1) { + Array.prototype.unshift.call( + oSourceElements[nFrom][1][1][3], + oSourceElements[nPosition]); + } + // Remove source elements outside the closure. + Array.prototype.splice.call( + oSourceElements, + nFrom + 1, + nTo - nFrom + 1); + } + } + if (bEnclose) { + // Restore the availability of identifier names. + oScope.cname = nIndex; + } + }; + + oSourceElements = (/** @type {!TSyntacticCodeUnit} */ + oSyntacticCodeUnit[bIsGlobal ? 1 : 3]); + if (0 === oSourceElements.length) { + return; + } + oScope = bIsGlobal ? oSyntacticCodeUnit.scope : oSourceElements.scope; + // Skip a Directive Prologue. + while (nAfterDirectivePrologue < oSourceElements.length && + '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('env!env/assert')), + /** + * The parser of ECMA-262 found in UglifyJS. + * @namespace + * @type {!TParser} + */ + oParser = (/** @type {!TParser} */ require('./parse-js')), + /** + * The processor of <abbr title="abstract syntax tree">AST</abbr>s + * found in UglifyJS. + * @namespace + * @type {!TProcessor} + */ + oProcessor = (/** @type {!TProcessor} */ require('./process')), + /** + * An instance of an object that allows the traversal of an <abbr + * title="abstract syntax tree">AST</abbr>. + * @type {!TWalker} + */ + oWalker, + /** + * A collection of functions for the removal of the scope information + * during the traversal of an <abbr title="abstract syntax tree" + * >AST</abbr>. + * @namespace + * @type {!Object.<string, function(...[*])>} + */ + 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.<string>} 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.<string>} 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 <abbr title= + * "abstract syntax tree">AST</abbr> obtained after consolidation. + * @type {!TSyntacticCodeUnit} + */ + oSyntacticCodeUnitActual = + exports.ast_consolidate(oParser.parse(oUnitTest.sInput)), + /** + * An array-like object representing the expected <abbr title= + * "abstract syntax tree">AST</abbr>. + * @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: */ + +}); +define('uglifyjs/parse-js', ["exports"], function(exports) { + /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. This version is suitable for Node.js. With minimal changes (the @@ -3215,10 +5865,11 @@ "break", "case", "catch", "const", "continue", + "debugger", "default", "delete", "do", "else", "finally", @@ -3243,11 +5894,10 @@ "abstract", "boolean", "byte", "char", "class", - "debugger", "double", "enum", "export", "extends", "final", @@ -3341,11 +5991,11 @@ "||" ]); 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("[]{}(),;:")); var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy")); @@ -3636,14 +6286,14 @@ return token("comment2", text, true); }); }; 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; } else { if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX"); @@ -3651,10 +6301,14 @@ if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); name += ch; 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; }; function read_regexp(regexp) { return with_eof_error("Unterminated regular expression", function(){ @@ -4059,12 +6713,15 @@ var init = null; if (!is("punc", ";")) { init = is("keyword", "var") ? (next(), var_(true)) : expression(true, true); - if (is("operator", "in")) + if (is("operator", "in")) { + if (init[0] == "var" && init[1].length > 1) + croak("Only one variable declaration allowed in for..in loop"); return for_in(init); + } } return regular_for(init); }; function regular_for(init) { @@ -4538,10 +7195,14 @@ return walk([ "call", [ "name", ctor[1] ], args]); } } }, "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", "" ]]; } if (expr[0] == "name") { @@ -4704,10 +7365,13 @@ return [ this[0], walk(expr), MAP(args, walk) ]; }, "function": function(name, args, body) { return [ this[0], name, args.slice(), MAP(body, walk) ]; }, + "debugger": function() { + return [ this[0] ]; + }, "defun": function(name, args, body) { return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "if": function(conditional, t, e) { return [ this[0], walk(conditional), walk(t), walk(e) ]; @@ -4847,16 +7511,17 @@ this.level = 0; } }; 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; }; })(); @@ -5306,11 +7971,13 @@ var val = evaluate(expr), ast; switch (typeof val) { case "string": ast = [ "string", val ]; break; case "number": ast = [ "num", val ]; break; case "boolean": ast = [ "name", String(val) ]; break; - default: throw new Error("Can't handle constant of type: " + (typeof val)); + default: + if (val === null) { ast = [ "atom", "null" ]; break; } + throw new Error("Can't handle constant of type: " + (typeof val)); } return yes.call(expr, ast, val); } catch(ex) { if (ex === $NOT_CONSTANT) { if (expr[0] == "binary" @@ -5375,21 +8042,19 @@ var t = walk(fi[2]); if (!aborts(t)) continue; var conditional = walk(fi[1]); - var e_body = statements.slice(i + 1); + var e_body = redo_if(statements.slice(i + 1)); var e = e_body.length == 1 ? e_body[0] : [ "block", e_body ]; - var ret = statements.slice(0, i).concat([ [ + return statements.slice(0, i).concat([ [ fi[0], // "if" conditional, // conditional t, // then e // else ] ]); - - return redo_if(ret); } return statements; }; @@ -5750,10 +8415,20 @@ }, function() { return make_real_if(c, t, e); }); }; + function abort_else(c, t, e) { + var ret = [ [ "if", negate(c), e ] ]; + if (t[0] == "block") { + if (t[1]) ret = ret.concat(t[1]); + } else { + ret.push(t); + } + return walk([ "block", ret ]); + }; + function make_real_if(c, t, e) { c = walk(c); t = walk(t); e = walk(e); @@ -5783,13 +8458,14 @@ if (t[0] == "if" && empty(t[3]) && empty(e)) { ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ])); } else if (t[0] == "stat") { if (e) { - if (e[0] == "stat") { + if (e[0] == "stat") ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]); - } + else if (aborts(e)) + ret = abort_else(c, t, e); } else { ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]); } } @@ -5805,17 +8481,11 @@ ret.push(e); } ret = walk([ "block", ret ]); } else if (t && aborts(e)) { - ret = [ [ "if", negate(c), e ] ]; - if (t[0] == "block") { - if (t[1]) ret = ret.concat(t[1]); - } else { - ret.push(t); - } - ret = walk([ "block", ret ]); + ret = abort_else(c, t, e); } return ret; }; function _do_while(cond, body) { @@ -5943,11 +8613,10 @@ case "\\": return "\\\\"; case "\b": return "\\b"; 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 '"'; case "'": ++sq; return "'"; case "\0": return "\\0"; @@ -6104,10 +8773,11 @@ var make = w.walk; return w.with_walkers({ "string": encode_string, "num": make_num, "name": make_name, + "debugger": function(){ return "debugger" }, "toplevel": function(statements) { return make_block_statements(statements) .join(newline + newline); }, "splice": function(statements) { @@ -6187,11 +8857,11 @@ "dot": function(expr) { var out = make(expr), i = 1; 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++]); return out; }, @@ -6285,11 +8955,11 @@ var out = "{" + newline + with_indent(function(){ return MAP(props, function(p){ 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) { key = encode_string(key); } else if ((typeof key == "number" || !beautify && +key + "" == key) @@ -6362,18 +9032,18 @@ else break; } 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) { switch (node[0]) { case "with": @@ -6566,11 +9236,11 @@ exports.MAP = MAP; // keep this last! exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more; -});define('uglifyjs/index', ["require", "exports", "module", "./parse-js", "./process"], function(require, exports, module) { +});define('uglifyjs/index', ["require", "exports", "module", "./parse-js", "./process", "./consolidator"], function(require, exports, module) { //convienence function(src, [options]); function uglify(orig_code, options){ options || (options = {}); var jsp = uglify.parser; @@ -6583,10 +9253,11 @@ return final_code; }; uglify.parser = require("./parse-js"); uglify.uglify = require("./process"); +uglify.consolidator = require("./consolidator"); module.exports = uglify });/** * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. @@ -7815,10 +10486,11 @@ function (lang, logger, envOptimize, file, parse, pragma, uglify) { var optimize, cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/g, + cssCommentImportRegExp = /\/\*[^\*]*@import[^\*]*\*\//g, cssUrlRegExp = /\url\(\s*([^\)]+)\s*\)?/g; /** * If an URL from a CSS url value contains start/end quotes, remove them. * This is not done in the regexp, since my regexp fu is not that strong, @@ -7838,28 +10510,34 @@ return url; } /** * Inlines nested stylesheets that have @import calls in them. - * @param {String} fileName - * @param {String} fileContents - * @param {String} [cssImportIgnore] + * @param {String} fileName the file name + * @param {String} fileContents the file contents + * @param {String} cssImportIgnore comma delimited string of files to ignore + * @param {Object} included an object used to track the files already imported */ - function flattenCss(fileName, fileContents, cssImportIgnore) { + function flattenCss(fileName, fileContents, cssImportIgnore, included) { //Find the last slash in the name. fileName = fileName.replace(lang.backSlashRegExp, "/"); var endIndex = fileName.lastIndexOf("/"), //Make a file path based on the last slash. //If no slash, so must be just a file name. Use empty string then. - filePath = (endIndex !== -1) ? fileName.substring(0, endIndex + 1) : ""; + filePath = (endIndex !== -1) ? fileName.substring(0, endIndex + 1) : "", + //store a list of merged files + importList = []; + //First make a pass by removing an commented out @import calls. + fileContents = fileContents.replace(cssCommentImportRegExp, ''); + //Make sure we have a delimited ignore list to make matching faster if (cssImportIgnore && cssImportIgnore.charAt(cssImportIgnore.length - 1) !== ",") { cssImportIgnore += ","; } - return fileContents.replace(cssImportRegExp, function (fullMatch, urlStart, importFileName, urlEnd, mediaTypes) { + fileContents = fileContents.replace(cssImportRegExp, function (fullMatch, urlStart, importFileName, urlEnd, mediaTypes) { //Only process media type "all" or empty media type rules. if (mediaTypes && ((mediaTypes.replace(/^\s\s*/, '').replace(/\s\s*$/, '')) !== "all")) { return fullMatch; } @@ -7877,15 +10555,26 @@ //if a relative path, then tack on the filePath. //If it is not a relative path, then the readFile below will fail, //and we will just skip that import. var fullImportFileName = importFileName.charAt(0) === "/" ? importFileName : filePath + importFileName, importContents = file.readFile(fullImportFileName), i, - importEndIndex, importPath, fixedUrlMatch, colonIndex, parts; + importEndIndex, importPath, fixedUrlMatch, colonIndex, parts, flat; + //Skip the file if it has already been included. + if (included[fullImportFileName]) { + return ''; + } + included[fullImportFileName] = true; + //Make sure to flatten any nested imports. - importContents = flattenCss(fullImportFileName, importContents); + flat = flattenCss(fullImportFileName, importContents, cssImportIgnore, included); + importContents = flat.fileContents; + if (flat.importList.length) { + importList.push.apply(importList, flat.importList); + } + //Make the full import path importEndIndex = importFileName.lastIndexOf("/"); //Make a file path based on the last slash. //If no slash, so must be just a file name. Use empty string then. @@ -7923,16 +10612,22 @@ } return "url(" + parts.join("/") + ")"; }); + importList.push(fullImportFileName); return importContents; } catch (e) { - logger.trace(fileName + "\n Cannot inline css import, skipping: " + importFileName); + logger.warn(fileName + "\n Cannot inline css import, skipping: " + importFileName); return fullMatch; } }); + + return { + importList : importList, + fileContents : fileContents + }; } optimize = { licenseCommentRegExp: /\/\*[\s\S]*?\*\//g, @@ -7940,25 +10635,26 @@ * Optimizes a file that contains JavaScript content. Optionally collects * plugin resources mentioned in a file, and then passes the content * through an minifier if one is specified via config.optimize. * * @param {String} fileName the name of the file to optimize + * @param {String} fileContents the contents to optimize. If this is + * a null value, then fileName will be used to read the fileContents. * @param {String} outFileName the name of the file to use for the * saved optimized content. * @param {Object} config the build config object. - * @param {String} [moduleName] the module name to use for the file. - * Used for plugin resource collection. * @param {Array} [pluginCollector] storage for any plugin resources * found. */ - jsFile: function (fileName, outFileName, config, moduleName, pluginCollector) { + jsFile: function (fileName, fileContents, outFileName, config, pluginCollector) { var parts = (config.optimize + "").split('.'), optimizerName = parts[0], - keepLines = parts[1] === 'keepLines', - fileContents; + keepLines = parts[1] === 'keepLines'; - fileContents = file.readFile(fileName); + if (!fileContents) { + fileContents = file.readFile(fileName); + } fileContents = optimize.js(fileName, fileContents, optimizerName, keepLines, config, pluginCollector); file.saveUtf8File(outFileName, fileContents); @@ -8024,25 +10720,29 @@ * @param {String} outFileName the path to save the optimized file. * @param {Object} config the config object with the optimizeCss and * cssImportIgnore options. */ cssFile: function (fileName, outFileName, config) { + //Read in the file. Make sure we have a JS string. var originalFileContents = file.readFile(fileName), - fileContents = flattenCss(fileName, originalFileContents, config.cssImportIgnore), - startIndex, endIndex; + flat = flattenCss(fileName, originalFileContents, config.cssImportIgnore, {}), + fileContents = flat.fileContents, + startIndex, endIndex, buildText; //Do comment removal. try { - startIndex = -1; - //Get rid of comments. - while ((startIndex = fileContents.indexOf("/*")) !== -1) { - endIndex = fileContents.indexOf("*/", startIndex + 2); - if (endIndex === -1) { - throw "Improper comment in CSS file: " + fileName; + if (config.optimizeCss.indexOf(".keepComments") === -1) { + startIndex = -1; + //Get rid of comments. + while ((startIndex = fileContents.indexOf("/*")) !== -1) { + endIndex = fileContents.indexOf("*/", startIndex + 2); + if (endIndex === -1) { + throw "Improper comment in CSS file: " + fileName; + } + fileContents = fileContents.substring(0, startIndex) + fileContents.substring(endIndex + 2, fileContents.length); } - fileContents = fileContents.substring(0, startIndex) + fileContents.substring(endIndex + 2, fileContents.length); } //Get rid of newlines. if (config.optimizeCss.indexOf(".keepLines") === -1) { fileContents = fileContents.replace(/[\r\n]/g, ""); fileContents = fileContents.replace(/\s+/g, " "); @@ -8057,31 +10757,41 @@ fileContents = originalFileContents; logger.error("Could not optimized CSS file: " + fileName + ", error: " + e); } file.saveUtf8File(outFileName, fileContents); + + //text output to stdout and/or written to build.txt file + buildText = "\n"+ outFileName.replace(config.dir, "") +"\n----------------\n"; + flat.importList.push(fileName); + buildText += flat.importList.map(function(path){ + return path.replace(config.dir, ""); + }).join("\n"); + return buildText +"\n"; }, /** * Optimizes CSS files, inlining @import calls, stripping comments, and * optionally removes line returns. * @param {String} startDir the path to the top level directory * @param {Object} config the config object with the optimizeCss and * cssImportIgnore options. */ css: function (startDir, config) { + var buildText = "", + i, fileName, fileList; if (config.optimizeCss.indexOf("standard") !== -1) { - var i, fileName, - fileList = file.getFilteredFileList(startDir, /\.css$/, true); + fileList = file.getFilteredFileList(startDir, /\.css$/, true); if (fileList) { for (i = 0; i < fileList.length; i++) { fileName = fileList[i]; logger.trace("Optimizing (" + config.optimizeCss + ") CSS file: " + fileName); - optimize.cssFile(fileName, fileName, config); + buildText += optimize.cssFile(fileName, fileName, config); } } } + return buildText; }, optimizers: { uglify: function (fileName, fileContents, keepLines, config) { var parser = uglify.parser, @@ -8700,11 +11410,11 @@ buildPaths, fileName, fileNames, prop, paths, i, baseConfig, config, modules, builtModule, srcPath, buildContext, destPath, moduleName, moduleMap, parentModuleMap, context, - resources, resource, pluginProcessed = {}, plugin; + resources, resource, pluginProcessed = {}, plugin, fileContents; //Can now run the patches to require.js to allow it to be used for //build generation. Do it here instead of at the top of the module //because we want normal require behavior to load the build tool //then want to switch to build mode. @@ -8729,11 +11439,11 @@ if (config.appDir) { //All the paths should be inside the appDir, so just adjust //the paths to use the dirBaseUrl for (prop in paths) { if (paths.hasOwnProperty(prop)) { - buildPaths[prop] = paths[prop].replace(config.baseUrl, config.dirBaseUrl); + buildPaths[prop] = paths[prop].replace(config.appDir, config.dir); } } } else { //If no appDir, then make sure to copy the other paths to this directory. for (prop in paths) { @@ -8829,11 +11539,11 @@ //Run CSS optimizations before doing JS module tracing, to allow //things like text loader plugins loading CSS to get the optimized //CSS. if (config.optimizeCss && config.optimizeCss !== "none" && config.dir) { - optimize.css(config.dir, config); + buildFileContents += optimize.css(config.dir, config); } if (modules) { //For each module layer, call require to calculate dependencies. modules.forEach(function (module) { @@ -8903,22 +11613,33 @@ //Do other optimizations. if (config.out && !config.cssIn) { //Just need to worry about one JS file. fileName = config.modules[0]._buildPath; - optimize.jsFile(fileName, fileName, config); + optimize.jsFile(fileName, null, fileName, config); } else if (!config.cssIn) { //Normal optimizations across modules. //JS optimizations. fileNames = file.getFilteredFileList(config.dir, /\.js$/, true); for (i = 0; (fileName = fileNames[i]); i++) { //Generate the module name from the config.dir root. moduleName = fileName.replace(config.dir, ''); //Get rid of the extension moduleName = moduleName.substring(0, moduleName.length - 3); - optimize.jsFile(fileName, fileName, config, moduleName, pluginCollector); + + //Convert the file to transport format, but without a name + //inserted (by passing null for moduleName) since the files are + //standalone, one module per file. + fileContents = file.readFile(fileName); + fileContents = build.toTransport(config.anonDefRegExp, + config.namespaceWithDot, + null, + fileName, + fileContents); + + optimize.jsFile(fileName, fileContents, fileName, config, pluginCollector); } //Normalize all the plugin resources. context = require.s.contexts._; @@ -8982,11 +11703,11 @@ file.saveUtf8File(config.dir + "build.txt", buildFileContents); } //If just have one CSS file to optimize, do that here. if (config.cssIn) { - optimize.cssFile(config.cssIn, config.out, config); + buildFileContents += optimize.cssFile(config.cssIn, config.out, config); } //Print out what was built into which layers. if (buildFileContents) { logger.info(buildFileContents); @@ -9227,10 +11948,13 @@ } mainConfigFile = config.mainConfigFile || (buildFileConfig && buildFileConfig.mainConfigFile); if (mainConfigFile) { mainConfigFile = build.makeAbsPath(mainConfigFile, absFilePath); + if (!file.exists(mainConfigFile)) { + throw new Error(mainConfigFile + ' does not exist.'); + } try { mainConfig = parse.findConfig(mainConfigFile, file.readFile(mainConfigFile)); } catch (configError) { throw new Error('The config in mainConfigFile ' + mainConfigFile + @@ -9380,10 +12104,13 @@ //name for fileExclusionRegExp before 1.0.2. Support for backwards //compatibility file.exclusionRegExp = config.dirExclusionRegExp; } + //Remove things that may cause problems in the build. + delete config.jQuery; + return config; }; /** * finds the module being built/optimized with the given moduleName, @@ -9631,11 +12358,12 @@ //or if a named module and the name matches expectations. if (layer && (!namedModule || namedModule === moduleName)) { layer.modulesWithNames[moduleName] = true; } - var deps = null; + var deps = null, + finalName; //Look for CommonJS require calls inside the function if this is //an anonymous define call that just has a function registered. //Also look if a named define function but has a factory function //as the second arg that should be scanned for dependencies. @@ -9649,11 +12377,16 @@ } else { deps = []; } } - return start + namespace + "define('" + (namedModule || moduleName) + "'," + + finalName = namedModule || moduleName || ''; + if (finalName) { + finalName = "'" + (namedModule || moduleName) + "',"; + } + + return start + namespace + "define(" + finalName + (deps ? ('[' + deps.toString() + '],') : '') + (namedModule ? namedFuncStart.replace(build.leadingCommaRegExp, '') : suffix); }); }; @@ -9732,10 +12465,12 @@ loadLib(); useLibLoaded[contextName] = true; } var req = requirejs({ - context: contextName + context: contextName, + requireLoad: requirejsVars.nodeLoad, + requireExecCb: requirejsVars.nodeRequireExecCb }); req(['build'], function () { callback(req); });