diff --git a/build/r.js b/build/r.js index 309ceaf3..4254eb3e 100644 --- a/build/r.js +++ b/build/r.js @@ -1,5 +1,5 @@ /** - * @license r.js 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license r.js 2.1.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -20,7 +20,7 @@ var requirejs, require, define, xpcUtil; (function (console, args, readFileFunc) { var fileName, env, fs, vm, path, exec, rhinoContext, dir, nodeRequire, nodeDefine, exists, reqMain, loadedOptimizedLib, existsForNode, Cc, Ci, - version = '2.1.9', + version = '2.1.14', jsSuffixRegExp = /\.js$/, commandOption = '', useLibLoaded = {}, @@ -51,36 +51,6 @@ var requirejs, require, define, xpcUtil; return false; }; - } else if (typeof Packages !== 'undefined') { - env = 'rhino'; - - fileName = args[0]; - - if (fileName && fileName.indexOf('-') === 0) { - commandOption = fileName.substring(1); - fileName = args[1]; - } - - //Set up execution context. - rhinoContext = Packages.org.mozilla.javascript.ContextFactory.getGlobal().enterContext(); - - exec = function (string, name) { - return rhinoContext.evaluateString(this, string, name, 0, null); - }; - - exists = function (fileName) { - return (new java.io.File(fileName)).exists(); - }; - - //Define a console.log for easier logging. Don't - //get fancy though. - if (typeof console === 'undefined') { - console = { - log: function () { - print.apply(undefined, arguments); - } - }; - } } else if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { env = 'node'; @@ -121,6 +91,36 @@ var requirejs, require, define, xpcUtil; commandOption = fileName.substring(1); fileName = process.argv[3]; } + } else if (typeof Packages !== 'undefined') { + env = 'rhino'; + + fileName = args[0]; + + if (fileName && fileName.indexOf('-') === 0) { + commandOption = fileName.substring(1); + fileName = args[1]; + } + + //Set up execution context. + rhinoContext = Packages.org.mozilla.javascript.ContextFactory.getGlobal().enterContext(); + + exec = function (string, name) { + return rhinoContext.evaluateString(this, string, name, 0, null); + }; + + exists = function (fileName) { + return (new java.io.File(fileName)).exists(); + }; + + //Define a console.log for easier logging. Don't + //get fancy though. + if (typeof console === 'undefined') { + console = { + log: function () { + print.apply(undefined, arguments); + } + }; + } } else if (typeof Components !== 'undefined' && Components.classes && Components.interfaces) { env = 'xpconnect'; @@ -238,7 +238,7 @@ var requirejs, require, define, xpcUtil; } /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -251,7 +251,7 @@ var requirejs, require, define, xpcUtil; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.1.9', + version = '2.1.14', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, @@ -347,7 +347,10 @@ var requirejs, require, define, xpcUtil; if (source) { eachProp(source, function (value, prop) { if (force || !hasProp(target, prop)) { - if (deepStringMixin && typeof value !== 'string') { + if (deepStringMixin && typeof value === 'object' && value && + !isArray(value) && !isFunction(value) && + !(value instanceof RegExp)) { + if (!target[prop]) { target[prop] = {}; } @@ -377,7 +380,7 @@ var requirejs, require, define, xpcUtil; throw err; } - //Allow getting a global that expressed in + //Allow getting a global that is expressed in //dot notation, like 'a.b.c'. function getGlobal(value) { if (!value) { @@ -416,7 +419,7 @@ var requirejs, require, define, xpcUtil; if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { - //Do not overwrite and existing requirejs instance. + //Do not overwrite an existing requirejs instance. return; } cfg = requirejs; @@ -440,6 +443,7 @@ var requirejs, require, define, xpcUtil; waitSeconds: 7, baseUrl: './', paths: {}, + bundles: {}, pkgs: {}, shim: {}, config: {} @@ -453,6 +457,7 @@ var requirejs, require, define, xpcUtil; defQueue = [], defined = {}, urlFetched = {}, + bundlesMap = {}, requireCounter = 1, unnormalizedCounter = 1; @@ -467,20 +472,19 @@ var requirejs, require, define, xpcUtil; */ function trimDots(ary) { var i, part; - for (i = 0; ary[i]; i += 1) { + for (i = 0; i < ary.length; i++) { part = ary[i]; if (part === '.') { ary.splice(i, 1); i -= 1; } else if (part === '..') { - if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { - //End of the line. Keep at least one non-dot - //path segment at the front so it can be mapped - //correctly to disk. Otherwise, there is likely - //no path mapping for a path starting with '..'. - //This can still fail, but catches the most reasonable - //uses of .. - break; + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') { + continue; } else if (i > 0) { ary.splice(i - 1, 2); i -= 2; @@ -500,54 +504,45 @@ var requirejs, require, define, xpcUtil; * @returns {String} normalized name */ function normalize(name, baseName, applyMap) { - var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, - foundMap, foundI, foundStarMap, starI, - baseParts = baseName && baseName.split('/'), - normalizedBaseParts = baseParts, + var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, + foundMap, foundI, foundStarMap, starI, normalizedBaseParts, + baseParts = (baseName && baseName.split('/')), map = config.map, starMap = map && map['*']; //Adjust any relative paths. - if (name && name.charAt(0) === '.') { - //If have a base name, try to normalize against it, - //otherwise, assume it is a top-level require that will - //be relative to baseUrl in the end. - if (baseName) { - if (getOwn(config.pkgs, baseName)) { - //If the baseName is a package name, then just treat it as one - //name to concat the name with. - normalizedBaseParts = baseParts = [baseName]; - } else { - //Convert baseName to array, and lop off the last part, - //so that . matches that 'directory' and not name of the baseName's - //module. For instance, baseName of 'one/two/three', maps to - //'one/two/three.js', but we want the directory, 'one/two' for - //this normalization. - normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); - } + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; - name = normalizedBaseParts.concat(name.split('/')); - trimDots(name); + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } - //Some use of packages may use a . path to reference the - //'main' module name, so normalize for that. - pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); - name = name.join('/'); - if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { - name = pkgName; - } - } else if (name.indexOf('./') === 0) { - // No baseName, so this is ID is resolved relative - // to baseUrl, pull off the leading dot. - name = name.substring(2); + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); } + + trimDots(name); + name = name.join('/'); } //Apply map config if available. if (applyMap && map && (baseParts || starMap)) { nameParts = name.split('/'); - for (i = nameParts.length; i > 0; i -= 1) { + outerLoop: for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join('/'); if (baseParts) { @@ -564,16 +559,12 @@ var requirejs, require, define, xpcUtil; //Match, update name to the new value. foundMap = mapValue; foundI = i; - break; + break outerLoop; } } } } - if (foundMap) { - break; - } - //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. @@ -594,7 +585,11 @@ var requirejs, require, define, xpcUtil; } } - return name; + // If the name points to a package's name, use + // the package main instead. + pkgMain = getOwn(config.pkgs, name); + + return pkgMain ? pkgMain : name; } function removeScript(name) { @@ -616,7 +611,13 @@ var requirejs, require, define, xpcUtil; //retry pathConfig.shift(); context.require.undef(id); - context.require([id]); + + //Custom require that does not do map translation, since + //ID is "absolute", already mapped/resolved. + context.makeRequire(null, { + skipMap: true + })([id]); + return true; } } @@ -682,7 +683,16 @@ var requirejs, require, define, xpcUtil; return normalize(name, parentName, applyMap); }); } else { - normalizedName = normalize(name, parentName, applyMap); + // If nested plugin references, then do not try to + // normalize, as it will not normalize correctly. This + // places a restriction on resourceIds, and the longer + // term solution is not to normalize until plugins are + // loaded and all normalizations to allow for async + // loading of a loader plugin. But for now, fixes the + // common uses. Details in #1131 + normalizedName = name.indexOf('!') === -1 ? + normalize(name, parentName, applyMap) : + name; } } else { //A regular module. @@ -787,7 +797,7 @@ var requirejs, require, define, xpcUtil; //local var ref to defQueue, so cannot just reassign the one //on context. apsp.apply(defQueue, - [defQueue.length - 1, 0].concat(globalDefQueue)); + [defQueue.length, 0].concat(globalDefQueue)); globalDefQueue = []; } } @@ -804,7 +814,7 @@ var requirejs, require, define, xpcUtil; mod.usingExports = true; if (mod.map.isDefine) { if (mod.exports) { - return mod.exports; + return (defined[mod.map.id] = mod.exports); } else { return (mod.exports = defined[mod.map.id] = {}); } @@ -818,15 +828,9 @@ var requirejs, require, define, xpcUtil; id: mod.map.id, uri: mod.map.url, config: function () { - var c, - pkg = getOwn(config.pkgs, mod.map.id); - // For packages, only support config targeted - // at the main module. - c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) : - getOwn(config.config, mod.map.id); - return c || {}; + return getOwn(config.config, mod.map.id) || {}; }, - exports: defined[mod.map.id] + exports: mod.exports || (mod.exports = {}) }); } } @@ -867,7 +871,7 @@ var requirejs, require, define, xpcUtil; } function checkLoaded() { - var map, modId, err, usingPathFallback, + var err, usingPathFallback, waitInterval = config.waitSeconds * 1000, //It is possible to disable the wait interval by using waitSeconds of 0. expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), @@ -885,8 +889,8 @@ var requirejs, require, define, xpcUtil; //Figure out the state of all the modules. eachProp(enabledRegistry, function (mod) { - map = mod.map; - modId = map.id; + var map = mod.map, + modId = map.id; //Skip things that are not enabled or in error state. if (!mod.enabled) { @@ -1109,17 +1113,14 @@ var requirejs, require, define, xpcUtil; exports = context.execCb(id, factory, depExports, exports); } - if (this.map.isDefine) { - //If setting exports via 'module' is in play, - //favor that over return value and exports. After that, - //favor a non-undefined return value over exports use. + // Favor return value over exports. If node/cjs in play, + // then will not have a return value anyway. Favor + // module.exports assignment over exports object. + if (this.map.isDefine && exports === undefined) { cjsModule = this.module; - if (cjsModule && - cjsModule.exports !== undefined && - //Make sure it is not already the exports value - cjsModule.exports !== this.exports) { + if (cjsModule) { exports = cjsModule.exports; - } else if (exports === undefined && this.usingExports) { + } else if (this.usingExports) { //exports already set the defined value. exports = this.exports; } @@ -1179,6 +1180,7 @@ var requirejs, require, define, xpcUtil; on(pluginMap, 'defined', bind(this, function (plugin) { var load, normalizedMap, normalizedMod, + bundleId = getOwn(bundlesMap, this.map.id), name = this.map.name, parentName = this.map.parentMap ? this.map.parentMap.name : null, localRequire = context.makeRequire(map.parentMap, { @@ -1224,6 +1226,14 @@ var requirejs, require, define, xpcUtil; return; } + //If a paths config, then just load that file instead to + //resolve the plugin, as it is built into that paths layer. + if (bundleId) { + this.map.url = context.nameToUrl(bundleId); + this.load(); + return; + } + load = bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true @@ -1488,31 +1498,38 @@ var requirejs, require, define, xpcUtil; } } - //Save off the paths and packages since they require special processing, + //Save off the paths since they require special processing, //they are additive. - var pkgs = config.pkgs, - shim = config.shim, + var shim = config.shim, objs = { paths: true, + bundles: true, config: true, map: true }; eachProp(cfg, function (value, prop) { if (objs[prop]) { - if (prop === 'map') { - if (!config.map) { - config.map = {}; - } - mixin(config[prop], value, true, true); - } else { - mixin(config[prop], value, true); + if (!config[prop]) { + config[prop] = {}; } + mixin(config[prop], value, true, true); } else { config[prop] = value; } }); + //Reverse map the bundles + if (cfg.bundles) { + eachProp(cfg.bundles, function (value, prop) { + each(value, function (v) { + if (v !== prop) { + bundlesMap[v] = prop; + } + }); + }); + } + //Merge shim if (cfg.shim) { eachProp(cfg.shim, function (value, id) { @@ -1533,29 +1550,25 @@ var requirejs, require, define, xpcUtil; //Adjust packages if necessary. if (cfg.packages) { each(cfg.packages, function (pkgObj) { - var location; + var location, name; pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + + name = pkgObj.name; location = pkgObj.location; + if (location) { + config.paths[name] = pkgObj.location; + } - //Create a brand new object on pkgs, since currentPackages can - //be passed in again, and config.pkgs is the internal transformed - //state for all package configs. - pkgs[pkgObj.name] = { - name: pkgObj.name, - location: location || pkgObj.name, - //Remove leading dot in main, so main paths are normalized, - //and remove any trailing .js, since different package - //envs have different conventions: some use a module name, - //some use a file name. - main: (pkgObj.main || 'main') - .replace(currDirRegExp, '') - .replace(jsSuffixRegExp, '') - }; + //Save pointer to main module ID for pkg name. + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, ''); }); - - //Done with modifications, assing packages back to context config - config.pkgs = pkgs; } //If there are any "waiting to execute" modules in the registry, @@ -1708,6 +1721,15 @@ var requirejs, require, define, xpcUtil; delete urlFetched[map.url]; delete undefEvents[id]; + //Clean queued defines too. Go backwards + //in array so that the splices do not + //mess up the iteration. + eachReverse(defQueue, function(args, i) { + if(args[0] === id) { + defQueue.splice(i, 1); + } + }); + if (mod) { //Hold on to listeners in case the //module will be attempted to be reloaded @@ -1727,7 +1749,7 @@ var requirejs, require, define, xpcUtil; /** * Called to enable a module if it is still in the registry * awaiting enablement. A second arg, parent, the parent module, - * is passed in for context, when this method is overriden by + * is passed in for context, when this method is overridden by * the optimizer. Not shown here to keep code compact. */ enable: function (depMap) { @@ -1801,8 +1823,19 @@ var requirejs, require, define, xpcUtil; * internal API, not a public one. Use toUrl for the public API. */ nameToUrl: function (moduleName, ext, skipExt) { - var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, - parentPath; + var paths, syms, i, parentModule, url, + parentPath, bundleId, + pkgMain = getOwn(config.pkgs, moduleName); + + if (pkgMain) { + moduleName = pkgMain; + } + + bundleId = getOwn(bundlesMap, moduleName); + + if (bundleId) { + return context.nameToUrl(bundleId, ext, skipExt); + } //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, contains a query arg (i.e. ?) @@ -1816,7 +1849,6 @@ var requirejs, require, define, xpcUtil; } else { //A module that needs to be converted to a path. paths = config.paths; - pkgs = config.pkgs; syms = moduleName.split('/'); //For each module name segment, see if there is a path @@ -1824,7 +1856,7 @@ var requirejs, require, define, xpcUtil; //and work up from it. for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); - pkg = getOwn(pkgs, parentModule); + parentPath = getOwn(paths, parentModule); if (parentPath) { //If an array, it means there are a few choices, @@ -1834,16 +1866,6 @@ var requirejs, require, define, xpcUtil; } syms.splice(0, i, parentPath); break; - } else if (pkg) { - //If module name is just the package name, then looking - //for the main module. - if (moduleName === pkg.name) { - pkgPath = pkg.location + '/' + pkg.main; - } else { - pkgPath = pkg.location; - } - syms.splice(0, i, pkgPath); - break; } } @@ -2302,7 +2324,7 @@ var requirejs, require, define, xpcUtil; if (env === 'browser') { /** - * @license RequireJS rhino Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS rhino Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -2331,7 +2353,7 @@ var requirejs, require, define, xpcUtil; }()); } else if (env === 'rhino') { /** - * @license RequireJS rhino Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS rhino Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -2355,7 +2377,7 @@ var requirejs, require, define, xpcUtil; require.nodeRequire = nodeRequire; /** - * @license RequireJS node Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS node Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -2437,7 +2459,7 @@ var requirejs, require, define, xpcUtil; //Break any cycles by requiring it normally, but this will //finish synchronously - require([moduleName]); + context.require([moduleName]); //The above calls are sync, so can do the next thing safely. ret = context.defined[moduleName]; @@ -2515,7 +2537,7 @@ var requirejs, require, define, xpcUtil; err.originalError = e; err.moduleName = originalName; err.requireModules = [moduleName]; - return context.onError(err); + throw err; } }); } @@ -2534,7 +2556,7 @@ var requirejs, require, define, xpcUtil; } else if (env === 'xpconnect') { /** - * @license RequireJS xpconnect Copyright (c) 2013, The Dojo Foundation All Rights Reserved. + * @license RequireJS xpconnect Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -2569,7 +2591,7 @@ var requirejs, require, define, xpcUtil; */ function loadLib() { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -2586,10 +2608,10 @@ var requirejs, require, define, xpcUtil; var pathRegExp = /(\/|^)env\/|\{env\}/, env = 'unknown'; - if (typeof Packages !== 'undefined') { - env = 'rhino'; - } else if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { + if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { env = 'node'; + } else if (typeof Packages !== 'undefined') { + env = 'rhino'; } else if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') || (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) { env = 'browser'; @@ -2621,25 +2643,36 @@ var requirejs, require, define, xpcUtil; }); } }); -}());/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. +}()); +/** + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ /*jslint plusplus: true */ -/*global define */ +/*global define, java */ define('lang', function () { 'use strict'; - var lang, + var lang, isJavaObj, hasOwn = Object.prototype.hasOwnProperty; function hasProp(obj, prop) { return hasOwn.call(obj, prop); } + isJavaObj = function () { + return false; + }; + + if (typeof java !== 'undefined' && java.lang && java.lang.Object) { + isJavaObj = function (obj) { + return obj instanceof java.lang.Object; + }; + } + lang = { backSlashRegExp: /\\/g, ostring: Object.prototype.toString, @@ -2701,6 +2734,31 @@ define('lang', function () { return dest; // Object }, + /** + * Does a deep mix of source into dest, where source values override + * dest values if a winner is needed. + * @param {Object} dest destination object that receives the mixed + * values. + * @param {Object} source source object contributing properties to mix + * in. + * @return {[Object]} returns dest object with the modification. + */ + deepMix: function(dest, source) { + lang.eachProp(source, function (value, prop) { + if (typeof value === 'object' && value && + !lang.isArray(value) && !lang.isFunction(value) && + !(value instanceof RegExp)) { + + if (!dest[prop]) { + dest[prop] = {}; + } + lang.deepMix(dest[prop], value); + } else { + dest[prop] = value; + } + }); + return dest; + }, /** * Does a type of deep copy. Do not give it anything fancy, best @@ -2725,7 +2783,7 @@ define('lang', function () { type = typeof obj; if (obj === null || obj === undefined || type === 'boolean' || type === 'string' || type === 'number' || lang.isFunction(obj) || - lang.isRegExp(obj)) { + lang.isRegExp(obj)|| isJavaObj(obj)) { return obj; } @@ -2804,7 +2862,7 @@ define('lang', function () { return lang; }); /** - * prim 0.0.1 Copyright (c) 2012-2013, The Dojo Foundation All Rights Reserved. + * prim 0.0.1 Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/requirejs/prim for details */ @@ -2999,7 +3057,7 @@ var prim; }()); if(env === 'browser') { /** - * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3016,7 +3074,7 @@ define('browser/assert', function () { if(env === 'node') { /** - * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3033,7 +3091,7 @@ define('node/assert', ['assert'], function (assert) { if(env === 'rhino') { /** - * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3050,7 +3108,7 @@ define('rhino/assert', function () { if(env === 'xpconnect') { /** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3067,7 +3125,7 @@ define('xpconnect/assert', function () { if(env === 'browser') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3084,7 +3142,7 @@ define('browser/args', function () { if(env === 'node') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3108,7 +3166,7 @@ define('node/args', function () { if(env === 'rhino') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3133,7 +3191,7 @@ define('rhino/args', function () { if(env === 'xpconnect') { /** - * @license Copyright (c) 2013, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3158,7 +3216,7 @@ define('xpconnect/args', function () { if(env === 'browser') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3178,7 +3236,7 @@ define('browser/load', ['./file'], function (file) { if(env === 'node') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3199,7 +3257,7 @@ define('node/load', ['fs'], function (fs) { if(env === 'rhino') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3215,7 +3273,7 @@ define('rhino/load', function () { if(env === 'xpconnect') { /** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3231,7 +3289,7 @@ define('xpconnect/load', function () { if(env === 'browser') { /** - * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3409,7 +3467,7 @@ define('browser/file', ['prim'], function (prim) { if(env === 'node') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -3672,7 +3730,7 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { //summary: deletes a file or directory if it exists. var files, i, stat; if (file.exists(fileName)) { - stat = fs.statSync(fileName); + stat = fs.lstatSync(fileName); if (stat.isDirectory()) { files = fs.readdirSync(fileName); for (i = 0; i < files.length; i++) { @@ -3697,7 +3755,7 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { for (i = 0; i < dirFileArray.length; i++) { fileName = dirFileArray[i]; filePath = path.join(startDir, fileName); - stat = fs.statSync(filePath); + stat = fs.lstatSync(filePath); if (stat.isDirectory()) { file.deleteEmptyDirs(filePath); } @@ -3719,7 +3777,7 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { if(env === 'rhino') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4013,7 +4071,7 @@ define('rhino/file', ['prim'], function (prim) { if(env === 'xpconnect') { /** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4333,7 +4391,7 @@ define('xpconnect/quit', function () { if(env === 'browser') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4353,7 +4411,7 @@ define('browser/print', function () { if(env === 'node') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4373,7 +4431,7 @@ define('node/print', function () { if(env === 'rhino') { /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4389,7 +4447,7 @@ define('rhino/print', function () { if(env === 'xpconnect') { /** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2013-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4403,7 +4461,7 @@ define('xpconnect/print', function () { } /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -4465,6 +4523,9 @@ define('logger', ['env!env/print'], function (print) { //like Node's fs and path. /* + Copyright (C) 2013 Ariya Hidayat + Copyright (C) 2013 Thaddee Tyl + Copyright (C) 2013 Mathias Bynens Copyright (C) 2012 Ariya Hidayat Copyright (C) 2012 Mathias Bynens Copyright (C) 2012 Joost-Wim Boekesteijn @@ -4496,11 +4557,13 @@ define('logger', ['env!env/print'], function (print) { /*jslint bitwise:true plusplus:true */ /*global esprima:true, define:true, exports:true, window: true, -throwError: true, createLiteral: true, generateStatement: true, +throwErrorTolerant: true, +throwError: true, generateStatement: true, peek: true, parseAssignmentExpression: true, parseBlock: true, parseExpression: true, parseFunctionDeclaration: true, parseFunctionExpression: true, parseFunctionSourceElements: true, parseVariableIdentifier: true, parseLeftHandSideExpression: true, +parseUnaryExpression: true, parseStatement: true, parseSourceElement: true */ (function (root, factory) { @@ -4508,6 +4571,8 @@ parseStatement: true, parseSourceElement: true */ // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, // Rhino, and plain browser loading. + + /* istanbul ignore next */ if (typeof define === 'function' && define.amd) { define('esprima', ['exports'], factory); } else if (typeof exports !== 'undefined') { @@ -4520,17 +4585,20 @@ parseStatement: true, parseSourceElement: true */ var Token, TokenName, + FnExprTokens, Syntax, PropertyKind, Messages, Regex, + SyntaxTreeDelegate, source, strict, index, lineNumber, lineStart, length, - buffer, + delegate, + lookahead, state, extra; @@ -4542,7 +4610,8 @@ parseStatement: true, parseSourceElement: true */ NullLiteral: 5, NumericLiteral: 6, Punctuator: 7, - StringLiteral: 8 + StringLiteral: 8, + RegularExpression: 9 }; TokenName = {}; @@ -4554,6 +4623,18 @@ parseStatement: true, parseSourceElement: true */ TokenName[Token.NumericLiteral] = 'Numeric'; TokenName[Token.Punctuator] = 'Punctuator'; TokenName[Token.StringLiteral] = 'String'; + TokenName[Token.RegularExpression] = 'RegularExpression'; + + // A function following one of those tokens is an expression. + FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', + 'return', 'case', 'delete', 'throw', 'void', + // assignment operators + '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', + '&=', '|=', '^=', ',', + // binary/unary operators + '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', + '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', + '<=', '<', '>', '!=', '!==']; Syntax = { AssignmentExpression: 'AssignmentExpression', @@ -4643,8 +4724,8 @@ parseStatement: true, parseSourceElement: true */ // See also tools/generate-unicode-regex.py. Regex = { - NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'), - NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]') + NonAsciiIdentifierStart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]'), + NonAsciiIdentifierPart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]') }; // Ensure the condition is true, otherwise throw an error. @@ -4653,23 +4734,14 @@ parseStatement: true, parseSourceElement: true */ // Do NOT use this to enforce a certain condition on any user input. function assert(condition, message) { + /* istanbul ignore if */ if (!condition) { throw new Error('ASSERT: ' + message); } } - function sliceSource(from, to) { - return source.slice(from, to); - } - - if (typeof 'esprima'[0] === 'undefined') { - sliceSource = function sliceArraySource(from, to) { - return source.slice(from, to).join(''); - }; - } - function isDecimalDigit(ch) { - return '0123456789'.indexOf(ch) >= 0; + return (ch >= 48 && ch <= 57); // 0..9 } function isHexDigit(ch) { @@ -4684,39 +4756,39 @@ parseStatement: true, parseSourceElement: true */ // 7.2 White Space function isWhiteSpace(ch) { - return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') || - (ch === '\u000C') || (ch === '\u00A0') || - (ch.charCodeAt(0) >= 0x1680 && - '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0); + return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || + (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0); } // 7.3 Line Terminators function isLineTerminator(ch) { - return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029'); + return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029); } // 7.6 Identifier Names and Identifiers function isIdentifierStart(ch) { - return (ch === '$') || (ch === '_') || (ch === '\\') || - (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || - ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch)); + return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) + (ch >= 0x41 && ch <= 0x5A) || // A..Z + (ch >= 0x61 && ch <= 0x7A) || // a..z + (ch === 0x5C) || // \ (backslash) + ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch))); } function isIdentifierPart(ch) { - return (ch === '$') || (ch === '_') || (ch === '\\') || - (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || - ((ch >= '0') && (ch <= '9')) || - ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch)); + return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) + (ch >= 0x41 && ch <= 0x5A) || // A..Z + (ch >= 0x61 && ch <= 0x7A) || // a..z + (ch >= 0x30 && ch <= 0x39) || // 0..9 + (ch === 0x5C) || // \ (backslash) + ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch))); } // 7.6.1.2 Future Reserved Words function isFutureReservedWord(id) { switch (id) { - - // Future reserved words. case 'class': case 'enum': case 'export': @@ -4724,15 +4796,13 @@ parseStatement: true, parseSourceElement: true */ case 'import': case 'super': return true; + default: + return false; } - - return false; } function isStrictModeReservedWord(id) { switch (id) { - - // Strict Mode reserved words. case 'implements': case 'interface': case 'package': @@ -4743,9 +4813,9 @@ parseStatement: true, parseSourceElement: true */ case 'yield': case 'let': return true; + default: + return false; } - - return false; } function isRestrictedWord(id) { @@ -4755,431 +4825,546 @@ parseStatement: true, parseSourceElement: true */ // 7.6.1.1 Keywords function isKeyword(id) { - var keyword = false; + if (strict && isStrictModeReservedWord(id)) { + return true; + } + + // 'const' is specialized as Keyword in V8. + // 'yield' and 'let' are for compatiblity with SpiderMonkey and ES.next. + // Some others are from future reserved words. + switch (id.length) { case 2: - keyword = (id === 'if') || (id === 'in') || (id === 'do'); - break; + return (id === 'if') || (id === 'in') || (id === 'do'); case 3: - keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try'); - break; + return (id === 'var') || (id === 'for') || (id === 'new') || + (id === 'try') || (id === 'let'); case 4: - keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with'); - break; + return (id === 'this') || (id === 'else') || (id === 'case') || + (id === 'void') || (id === 'with') || (id === 'enum'); case 5: - keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw'); - break; + return (id === 'while') || (id === 'break') || (id === 'catch') || + (id === 'throw') || (id === 'const') || (id === 'yield') || + (id === 'class') || (id === 'super'); case 6: - keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch'); - break; + return (id === 'return') || (id === 'typeof') || (id === 'delete') || + (id === 'switch') || (id === 'export') || (id === 'import'); case 7: - keyword = (id === 'default') || (id === 'finally'); - break; + return (id === 'default') || (id === 'finally') || (id === 'extends'); case 8: - keyword = (id === 'function') || (id === 'continue') || (id === 'debugger'); - break; + return (id === 'function') || (id === 'continue') || (id === 'debugger'); case 10: - keyword = (id === 'instanceof'); - break; + return (id === 'instanceof'); + default: + return false; } + } - if (keyword) { - return true; - } + // 7.4 Comments - switch (id) { - // Future reserved words. - // 'const' is specialized as Keyword in V8. - case 'const': - return true; + function addComment(type, value, start, end, loc) { + var comment, attacher; - // For compatiblity to SpiderMonkey and ES.next - case 'yield': - case 'let': - return true; - } + assert(typeof start === 'number', 'Comment must have valid position'); - if (strict && isStrictModeReservedWord(id)) { - return true; + // Because the way the actual token is scanned, often the comments + // (if any) are skipped twice during the lexical analysis. + // Thus, we need to skip adding a comment if the comment array already + // handled it. + if (state.lastCommentStart >= start) { + return; } + state.lastCommentStart = start; - return isFutureReservedWord(id); + comment = { + type: type, + value: value + }; + if (extra.range) { + comment.range = [start, end]; + } + if (extra.loc) { + comment.loc = loc; + } + extra.comments.push(comment); + if (extra.attachComment) { + extra.leadingComments.push(comment); + extra.trailingComments.push(comment); + } } - // 7.4 Comments - - function skipComment() { - var ch, blockComment, lineComment; + function skipSingleLineComment(offset) { + var start, loc, ch, comment; - blockComment = false; - lineComment = false; + start = index - offset; + loc = { + start: { + line: lineNumber, + column: index - lineStart - offset + } + }; while (index < length) { - ch = source[index]; - - if (lineComment) { - ch = source[index++]; - if (isLineTerminator(ch)) { - lineComment = false; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - ++lineNumber; - lineStart = index; - } - } else if (blockComment) { - if (isLineTerminator(ch)) { - if (ch === '\r' && source[index + 1] === '\n') { - ++index; - } - ++lineNumber; - ++index; - lineStart = index; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - ch = source[index++]; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - if (ch === '*') { - ch = source[index]; - if (ch === '/') { - ++index; - blockComment = false; - } - } - } - } else if (ch === '/') { - ch = source[index + 1]; - if (ch === '/') { - index += 2; - lineComment = true; - } else if (ch === '*') { - index += 2; - blockComment = true; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - break; + ch = source.charCodeAt(index); + ++index; + if (isLineTerminator(ch)) { + if (extra.comments) { + comment = source.slice(start + offset, index - 1); + loc.end = { + line: lineNumber, + column: index - lineStart - 1 + }; + addComment('Line', comment, start, index - 1, loc); } - } else if (isWhiteSpace(ch)) { - ++index; - } else if (isLineTerminator(ch)) { - ++index; - if (ch === '\r' && source[index] === '\n') { + if (ch === 13 && source.charCodeAt(index) === 10) { ++index; } ++lineNumber; lineStart = index; - } else { - break; + return; } } - } - - function scanHexEscape(prefix) { - var i, len, ch, code = 0; - len = (prefix === 'u') ? 4 : 2; - for (i = 0; i < len; ++i) { - if (index < length && isHexDigit(source[index])) { - ch = source[index++]; - code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); - } else { - return ''; - } + if (extra.comments) { + comment = source.slice(start + offset, index); + loc.end = { + line: lineNumber, + column: index - lineStart + }; + addComment('Line', comment, start, index, loc); } - return String.fromCharCode(code); } - function scanIdentifier() { - var ch, start, id, restore; + function skipMultiLineComment() { + var start, loc, ch, comment; - ch = source[index]; - if (!isIdentifierStart(ch)) { - return; + if (extra.comments) { + start = index - 2; + loc = { + start: { + line: lineNumber, + column: index - lineStart - 2 + } + }; } - start = index; - if (ch === '\\') { - ++index; - if (source[index] !== 'u') { - return; - } - ++index; - restore = index; - ch = scanHexEscape('u'); - if (ch) { - if (ch === '\\' || !isIdentifierStart(ch)) { - return; + while (index < length) { + ch = source.charCodeAt(index); + if (isLineTerminator(ch)) { + if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) { + ++index; } - id = ch; + ++lineNumber; + ++index; + lineStart = index; + if (index >= length) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + } else if (ch === 0x2A) { + // Block comment ends with '*/'. + if (source.charCodeAt(index + 1) === 0x2F) { + ++index; + ++index; + if (extra.comments) { + comment = source.slice(start + 2, index - 2); + loc.end = { + line: lineNumber, + column: index - lineStart + }; + addComment('Block', comment, start, index, loc); + } + return; + } + ++index; } else { - index = restore; - id = 'u'; + ++index; } - } else { - id = source[index++]; } + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + function skipComment() { + var ch, start; + + start = (index === 0); while (index < length) { - ch = source[index]; - if (!isIdentifierPart(ch)) { - break; - } - if (ch === '\\') { + ch = source.charCodeAt(index); + + if (isWhiteSpace(ch)) { ++index; - if (source[index] !== 'u') { - return; - } + } else if (isLineTerminator(ch)) { ++index; - restore = index; - ch = scanHexEscape('u'); - if (ch) { - if (ch === '\\' || !isIdentifierPart(ch)) { - return; - } - id += ch; + if (ch === 0x0D && source.charCodeAt(index) === 0x0A) { + ++index; + } + ++lineNumber; + lineStart = index; + start = true; + } else if (ch === 0x2F) { // U+002F is '/' + ch = source.charCodeAt(index + 1); + if (ch === 0x2F) { + ++index; + ++index; + skipSingleLineComment(2); + start = true; + } else if (ch === 0x2A) { // U+002A is '*' + ++index; + ++index; + skipMultiLineComment(); + } else { + break; + } + } else if (start && ch === 0x2D) { // U+002D is '-' + // U+003E is '>' + if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) { + // '-->' is a single-line comment + index += 3; + skipSingleLineComment(3); + } else { + break; + } + } else if (ch === 0x3C) { // U+003C is '<' + if (source.slice(index + 1, index + 4) === '!--') { + ++index; // `<` + ++index; // `!` + ++index; // `-` + ++index; // `-` + skipSingleLineComment(4); } else { - index = restore; - id += 'u'; + break; } } else { - id += source[index++]; + break; } } + } - // There is no keyword or literal with only one character. - // Thus, it must be an identifier. - if (id.length === 1) { - return { - type: Token.Identifier, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + function scanHexEscape(prefix) { + var i, len, ch, code = 0; + + len = (prefix === 'u') ? 4 : 2; + for (i = 0; i < len; ++i) { + if (index < length && isHexDigit(source[index])) { + ch = source[index++]; + code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); + } else { + return ''; + } } + return String.fromCharCode(code); + } - if (isKeyword(id)) { - return { - type: Token.Keyword, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + function getEscapedIdentifier() { + var ch, id; + + ch = source.charCodeAt(index++); + id = String.fromCharCode(ch); + + // '\u' (U+005C, U+0075) denotes an escaped character. + if (ch === 0x5C) { + if (source.charCodeAt(index) !== 0x75) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + ++index; + ch = scanHexEscape('u'); + if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + id = ch; } - // 7.8.1 Null Literals + while (index < length) { + ch = source.charCodeAt(index); + if (!isIdentifierPart(ch)) { + break; + } + ++index; + id += String.fromCharCode(ch); - if (id === 'null') { - return { - type: Token.NullLiteral, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + // '\u' (U+005C, U+0075) denotes an escaped character. + if (ch === 0x5C) { + id = id.substr(0, id.length - 1); + if (source.charCodeAt(index) !== 0x75) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + ++index; + ch = scanHexEscape('u'); + if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + id += ch; + } } - // 7.8.2 Boolean Literals + return id; + } - if (id === 'true' || id === 'false') { - return { - type: Token.BooleanLiteral, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + function getIdentifier() { + var start, ch; + + start = index++; + while (index < length) { + ch = source.charCodeAt(index); + if (ch === 0x5C) { + // Blackslash (U+005C) marks Unicode escape sequence. + index = start; + return getEscapedIdentifier(); + } + if (isIdentifierPart(ch)) { + ++index; + } else { + break; + } + } + + return source.slice(start, index); + } + + function scanIdentifier() { + var start, id, type; + + start = index; + + // Backslash (U+005C) starts an escaped character. + id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier(); + + // There is no keyword or literal with only one character. + // Thus, it must be an identifier. + if (id.length === 1) { + type = Token.Identifier; + } else if (isKeyword(id)) { + type = Token.Keyword; + } else if (id === 'null') { + type = Token.NullLiteral; + } else if (id === 'true' || id === 'false') { + type = Token.BooleanLiteral; + } else { + type = Token.Identifier; } return { - type: Token.Identifier, + type: type, value: id, lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } + // 7.7 Punctuators function scanPunctuator() { var start = index, + code = source.charCodeAt(index), + code2, ch1 = source[index], ch2, ch3, ch4; - // Check for most common single-character punctuators. - - if (ch1 === ';' || ch1 === '{' || ch1 === '}') { - ++index; - return { - type: Token.Punctuator, - value: ch1, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } + switch (code) { - if (ch1 === ',' || ch1 === '(' || ch1 === ')') { + // Check for most common single-character punctuators. + case 0x2E: // . dot + case 0x28: // ( open bracket + case 0x29: // ) close bracket + case 0x3B: // ; semicolon + case 0x2C: // , comma + case 0x7B: // { open curly brace + case 0x7D: // } close curly brace + case 0x5B: // [ + case 0x5D: // ] + case 0x3A: // : + case 0x3F: // ? + case 0x7E: // ~ ++index; + if (extra.tokenize) { + if (code === 0x28) { + extra.openParenToken = extra.tokens.length; + } else if (code === 0x7B) { + extra.openCurlyToken = extra.tokens.length; + } + } return { type: Token.Punctuator, - value: ch1, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - // Dot (.) can also start a floating-point number, hence the need - // to check the next character. - - ch2 = source[index + 1]; - if (ch1 === '.' && !isDecimalDigit(ch2)) { - return { - type: Token.Punctuator, - value: source[index++], + value: String.fromCharCode(code), lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; - } - // Peek more characters. + default: + code2 = source.charCodeAt(index + 1); - ch3 = source[index + 2]; - ch4 = source[index + 3]; + // '=' (U+003D) marks an assignment or comparison operator. + if (code2 === 0x3D) { + switch (code) { + case 0x2B: // + + case 0x2D: // - + case 0x2F: // / + case 0x3C: // < + case 0x3E: // > + case 0x5E: // ^ + case 0x7C: // | + case 0x25: // % + case 0x26: // & + case 0x2A: // * + index += 2; + return { + type: Token.Punctuator, + value: String.fromCharCode(code) + String.fromCharCode(code2), + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; - // 4-character punctuator: >>>= + case 0x21: // ! + case 0x3D: // = + index += 2; - if (ch1 === '>' && ch2 === '>' && ch3 === '>') { - if (ch4 === '=') { - index += 4; - return { - type: Token.Punctuator, - value: '>>>=', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + // !== and === + if (source.charCodeAt(index) === 0x3D) { + ++index; + } + return { + type: Token.Punctuator, + value: source.slice(start, index), + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } } } - // 3-character punctuators: === !== >>> <<= >>= + // 4-character punctuator: >>>= - if (ch1 === '=' && ch2 === '=' && ch3 === '=') { - index += 3; - return { - type: Token.Punctuator, - value: '===', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } + ch4 = source.substr(index, 4); - if (ch1 === '!' && ch2 === '=' && ch3 === '=') { - index += 3; + if (ch4 === '>>>=') { + index += 4; return { type: Token.Punctuator, - value: '!==', + value: ch4, lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } - if (ch1 === '>' && ch2 === '>' && ch3 === '>') { + // 3-character punctuators: === !== >>> <<= >>= + + ch3 = ch4.substr(0, 3); + + if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') { index += 3; return { type: Token.Punctuator, - value: '>>>', + value: ch3, lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } - if (ch1 === '<' && ch2 === '<' && ch3 === '=') { - index += 3; + // Other 2-character punctuators: ++ -- << >> && || + ch2 = ch3.substr(0, 2); + + if ((ch1 === ch2[1] && ('+-<>&|'.indexOf(ch1) >= 0)) || ch2 === '=>') { + index += 2; return { type: Token.Punctuator, - value: '<<=', + value: ch2, lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } - if (ch1 === '>' && ch2 === '>' && ch3 === '=') { - index += 3; + // 1-character punctuators: < > = ! + - * % & | ^ / + if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { + ++index; return { type: Token.Punctuator, - value: '>>=', + value: ch1, lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } - // 2-character punctuators: <= >= == != ++ -- << >> && || - // += -= *= %= &= |= ^= /= + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } - if (ch2 === '=') { - if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { - index += 2; - return { - type: Token.Punctuator, - value: ch1 + ch2, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - } + // 7.8.3 Numeric Literals - if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { - if ('+-<>&|'.indexOf(ch2) >= 0) { - index += 2; - return { - type: Token.Punctuator, - value: ch1 + ch2, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + function scanHexLiteral(start) { + var number = ''; + + while (index < length) { + if (!isHexDigit(source[index])) { + break; } + number += source[index++]; } - // The remaining 1-character punctuators. + if (number.length === 0) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } - if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) { - return { - type: Token.Punctuator, - value: source[index++], - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + if (isIdentifierStart(source.charCodeAt(index))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } + + return { + type: Token.NumericLiteral, + value: parseInt('0x' + number, 16), + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; } - // 7.8.3 Numeric Literals + function scanOctalLiteral(start) { + var number = '0' + source[index++]; + while (index < length) { + if (!isOctalDigit(source[index])) { + break; + } + number += source[index++]; + } + + if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + return { + type: Token.NumericLiteral, + value: parseInt(number, 8), + octal: true, + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } function scanNumericLiteral() { var number, start, ch; ch = source[index]; - assert(isDecimalDigit(ch) || (ch === '.'), + assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), 'Numeric literal must start with a decimal digit or a decimal point'); start = index; @@ -5192,83 +5377,31 @@ parseStatement: true, parseSourceElement: true */ // Octal number starts with '0'. if (number === '0') { if (ch === 'x' || ch === 'X') { - number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isHexDigit(ch)) { - break; - } - number += source[index++]; - } + ++index; + return scanHexLiteral(start); + } + if (isOctalDigit(ch)) { + return scanOctalLiteral(start); + } - if (number.length <= 2) { - // only 0x - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } + // decimal number starts with '0' such as '09' is illegal. + if (ch && isDecimalDigit(ch.charCodeAt(0))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + } - if (index < length) { - ch = source[index]; - if (isIdentifierStart(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - return { - type: Token.NumericLiteral, - value: parseInt(number, 16), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } else if (isOctalDigit(ch)) { - number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isOctalDigit(ch)) { - break; - } - number += source[index++]; - } - - if (index < length) { - ch = source[index]; - if (isIdentifierStart(ch) || isDecimalDigit(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - return { - type: Token.NumericLiteral, - value: parseInt(number, 8), - octal: true, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - // decimal number starts with '0' such as '09' is illegal. - if (isDecimalDigit(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - - while (index < length) { - ch = source[index]; - if (!isDecimalDigit(ch)) { - break; - } + while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } + ch = source[index]; } if (ch === '.') { number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isDecimalDigit(ch)) { - break; - } + while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } + ch = source[index]; } if (ch === 'e' || ch === 'E') { @@ -5278,31 +5411,17 @@ parseStatement: true, parseSourceElement: true */ if (ch === '+' || ch === '-') { number += source[index++]; } - - ch = source[index]; - if (isDecimalDigit(ch)) { - number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isDecimalDigit(ch)) { - break; - } + if (isDecimalDigit(source.charCodeAt(index))) { + while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } } else { - ch = 'character ' + ch; - if (index >= length) { - ch = ''; - } throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } - if (index < length) { - ch = source[index]; - if (isIdentifierStart(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } + if (isIdentifierStart(source.charCodeAt(index))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } return { @@ -5310,14 +5429,17 @@ parseStatement: true, parseSourceElement: true */ value: parseFloat(number), lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } // 7.8.4 String Literals function scanStringLiteral() { - var str = '', quote, start, ch, code, unescaped, restore, octal = false; + var str = '', quote, start, ch, code, unescaped, restore, octal = false, startLineNumber, startLineStart; + startLineNumber = lineNumber; + startLineStart = lineStart; quote = source[index]; assert((quote === '\'' || quote === '"'), @@ -5334,17 +5456,8 @@ parseStatement: true, parseSourceElement: true */ break; } else if (ch === '\\') { ch = source[index++]; - if (!isLineTerminator(ch)) { + if (!ch || !isLineTerminator(ch.charCodeAt(0))) { switch (ch) { - case 'n': - str += '\n'; - break; - case 'r': - str += '\r'; - break; - case 't': - str += '\t'; - break; case 'u': case 'x': restore = index; @@ -5356,6 +5469,15 @@ parseStatement: true, parseSourceElement: true */ str += ch; } break; + case 'n': + str += '\n'; + break; + case 'r': + str += '\r'; + break; + case 't': + str += '\t'; + break; case 'b': str += '\b'; break; @@ -5398,8 +5520,9 @@ parseStatement: true, parseSourceElement: true */ if (ch === '\r' && source[index] === '\n') { ++index; } + lineStart = index; } - } else if (isLineTerminator(ch)) { + } else if (isLineTerminator(ch.charCodeAt(0))) { break; } else { str += ch; @@ -5414,33 +5537,46 @@ parseStatement: true, parseSourceElement: true */ type: Token.StringLiteral, value: str, octal: octal, + startLineNumber: startLineNumber, + startLineStart: startLineStart, lineNumber: lineNumber, lineStart: lineStart, - range: [start, index] + start: start, + end: index }; } - function scanRegExp() { - var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false; + function testRegExp(pattern, flags) { + var value; + try { + value = new RegExp(pattern, flags); + } catch (e) { + throwError({}, Messages.InvalidRegExp); + } + return value; + } - buffer = null; - skipComment(); + function scanRegExpBody() { + var ch, str, classMarker, terminated, body; - start = index; ch = source[index]; assert(ch === '/', 'Regular expression literal must start with a slash'); str = source[index++]; + classMarker = false; + terminated = false; while (index < length) { ch = source[index++]; str += ch; if (ch === '\\') { ch = source[index++]; // ECMA-262 7.8.5 - if (isLineTerminator(ch)) { + if (isLineTerminator(ch.charCodeAt(0))) { throwError({}, Messages.UnterminatedRegExp); } str += ch; + } else if (isLineTerminator(ch.charCodeAt(0))) { + throwError({}, Messages.UnterminatedRegExp); } else if (classMarker) { if (ch === ']') { classMarker = false; @@ -5451,8 +5587,6 @@ parseStatement: true, parseSourceElement: true */ break; } else if (ch === '[') { classMarker = true; - } else if (isLineTerminator(ch)) { - throwError({}, Messages.UnterminatedRegExp); } } } @@ -5462,12 +5596,21 @@ parseStatement: true, parseSourceElement: true */ } // Exclude leading and trailing slash. - pattern = str.substr(1, str.length - 2); + body = str.substr(1, str.length - 2); + return { + value: body, + literal: str + }; + } + function scanRegExpFlags() { + var ch, str, flags, restore; + + str = ''; flags = ''; while (index < length) { ch = source[index]; - if (!isIdentifierPart(ch)) { + if (!isIdentifierPart(ch.charCodeAt(0))) { break; } @@ -5480,8 +5623,7 @@ parseStatement: true, parseSourceElement: true */ ch = scanHexEscape('u'); if (ch) { flags += ch; - str += '\\u'; - for (; restore < index; ++restore) { + for (str += '\\u'; restore < index; ++restore) { str += source[restore]; } } else { @@ -5489,8 +5631,10 @@ parseStatement: true, parseSourceElement: true */ flags += 'u'; str += '\\u'; } + throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL'); } else { str += '\\'; + throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL'); } } else { flags += ch; @@ -5498,17 +5642,82 @@ parseStatement: true, parseSourceElement: true */ } } - try { - value = new RegExp(pattern, flags); - } catch (e) { - throwError({}, Messages.InvalidRegExp); + return { + value: flags, + literal: str + }; + } + + function scanRegExp() { + var start, body, flags, pattern, value; + + lookahead = null; + skipComment(); + start = index; + + body = scanRegExpBody(); + flags = scanRegExpFlags(); + value = testRegExp(body.value, flags.value); + + if (extra.tokenize) { + return { + type: Token.RegularExpression, + value: value, + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; } return { - literal: str, + literal: body.literal + flags.literal, value: value, - range: [start, index] + start: start, + end: index + }; + } + + function collectRegex() { + var pos, loc, regex, token; + + skipComment(); + + pos = index; + loc = { + start: { + line: lineNumber, + column: index - lineStart + } + }; + + regex = scanRegExp(); + loc.end = { + line: lineNumber, + column: index - lineStart }; + + /* istanbul ignore next */ + if (!extra.tokenize) { + // Pop the previous token, which is likely '/' or '/=' + if (extra.tokens.length > 0) { + token = extra.tokens[extra.tokens.length - 1]; + if (token.range[0] === pos && token.type === 'Punctuator') { + if (token.value === '/' || token.value === '/=') { + extra.tokens.pop(); + } + } + } + + extra.tokens.push({ + type: 'RegularExpression', + value: regex.literal, + range: [pos, index], + loc: loc + }); + } + + return regex; } function isIdentifierName(token) { @@ -5518,8 +5727,71 @@ parseStatement: true, parseSourceElement: true */ token.type === Token.NullLiteral; } + function advanceSlash() { + var prevToken, + checkToken; + // Using the following algorithm: + // https://github.com/mozilla/sweet.js/wiki/design + prevToken = extra.tokens[extra.tokens.length - 1]; + if (!prevToken) { + // Nothing before that: it cannot be a division. + return collectRegex(); + } + if (prevToken.type === 'Punctuator') { + if (prevToken.value === ']') { + return scanPunctuator(); + } + if (prevToken.value === ')') { + checkToken = extra.tokens[extra.openParenToken - 1]; + if (checkToken && + checkToken.type === 'Keyword' && + (checkToken.value === 'if' || + checkToken.value === 'while' || + checkToken.value === 'for' || + checkToken.value === 'with')) { + return collectRegex(); + } + return scanPunctuator(); + } + if (prevToken.value === '}') { + // Dividing a function by anything makes little sense, + // but we have to check for that. + if (extra.tokens[extra.openCurlyToken - 3] && + extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') { + // Anonymous function. + checkToken = extra.tokens[extra.openCurlyToken - 4]; + if (!checkToken) { + return scanPunctuator(); + } + } else if (extra.tokens[extra.openCurlyToken - 4] && + extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') { + // Named function. + checkToken = extra.tokens[extra.openCurlyToken - 5]; + if (!checkToken) { + return collectRegex(); + } + } else { + return scanPunctuator(); + } + // checkToken determines whether the function is + // a declaration or an expression. + if (FnExprTokens.indexOf(checkToken.value) >= 0) { + // It is an expression. + return scanPunctuator(); + } + // It is a declaration. + return collectRegex(); + } + return collectRegex(); + } + if (prevToken.type === 'Keyword') { + return collectRegex(); + } + return scanPunctuator(); + } + function advance() { - var ch, token; + var ch; skipComment(); @@ -5528,908 +5800,1261 @@ parseStatement: true, parseSourceElement: true */ type: Token.EOF, lineNumber: lineNumber, lineStart: lineStart, - range: [index, index] + start: index, + end: index }; } - token = scanPunctuator(); - if (typeof token !== 'undefined') { - return token; + ch = source.charCodeAt(index); + + if (isIdentifierStart(ch)) { + return scanIdentifier(); } - ch = source[index]; + // Very common: ( and ) and ; + if (ch === 0x28 || ch === 0x29 || ch === 0x3B) { + return scanPunctuator(); + } - if (ch === '\'' || ch === '"') { + // String literal starts with single quote (U+0027) or double quote (U+0022). + if (ch === 0x27 || ch === 0x22) { return scanStringLiteral(); } - if (ch === '.' || isDecimalDigit(ch)) { + + // Dot (.) U+002E can also start a floating-point number, hence the need + // to check the next character. + if (ch === 0x2E) { + if (isDecimalDigit(source.charCodeAt(index + 1))) { + return scanNumericLiteral(); + } + return scanPunctuator(); + } + + if (isDecimalDigit(ch)) { return scanNumericLiteral(); } - token = scanIdentifier(); - if (typeof token !== 'undefined') { - return token; + // Slash (/) U+002F can also start a regex. + if (extra.tokenize && ch === 0x2F) { + return advanceSlash(); } - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + return scanPunctuator(); } - function lex() { - var token; + function collectToken() { + var loc, token, range, value; + + skipComment(); + loc = { + start: { + line: lineNumber, + column: index - lineStart + } + }; + + token = advance(); + loc.end = { + line: lineNumber, + column: index - lineStart + }; - if (buffer) { - index = buffer.range[1]; - lineNumber = buffer.lineNumber; - lineStart = buffer.lineStart; - token = buffer; - buffer = null; - return token; + if (token.type !== Token.EOF) { + value = source.slice(token.start, token.end); + extra.tokens.push({ + type: TokenName[token.type], + value: value, + range: [token.start, token.end], + loc: loc + }); } - buffer = null; - return advance(); + return token; } - function lookahead() { - var pos, line, start; + function lex() { + var token; - if (buffer !== null) { - return buffer; - } + token = lookahead; + index = token.end; + lineNumber = token.lineNumber; + lineStart = token.lineStart; - pos = index; - line = lineNumber; - start = lineStart; - buffer = advance(); - index = pos; - lineNumber = line; - lineStart = start; + lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); - return buffer; - } + index = token.end; + lineNumber = token.lineNumber; + lineStart = token.lineStart; - // Return true if there is a line terminator before the next token. + return token; + } - function peekLineTerminator() { - var pos, line, start, found; + function peek() { + var pos, line, start; pos = index; line = lineNumber; start = lineStart; - skipComment(); - found = lineNumber !== line; + lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); index = pos; lineNumber = line; lineStart = start; + } - return found; + function Position(line, column) { + this.line = line; + this.column = column; } - // Throw an exception + function SourceLocation(startLine, startColumn, line, column) { + this.start = new Position(startLine, startColumn); + this.end = new Position(line, column); + } - function throwError(token, messageFormat) { - var error, - args = Array.prototype.slice.call(arguments, 2), - msg = messageFormat.replace( - /%(\d)/g, - function (whole, index) { - return args[index] || ''; - } - ); + SyntaxTreeDelegate = { - if (typeof token.lineNumber === 'number') { - error = new Error('Line ' + token.lineNumber + ': ' + msg); - error.index = token.range[0]; - error.lineNumber = token.lineNumber; - error.column = token.range[0] - lineStart + 1; - } else { - error = new Error('Line ' + lineNumber + ': ' + msg); - error.index = index; - error.lineNumber = lineNumber; - error.column = index - lineStart + 1; - } + name: 'SyntaxTree', - throw error; - } + processComment: function (node) { + var lastChild, trailingComments; - function throwErrorTolerant() { - try { - throwError.apply(null, arguments); - } catch (e) { - if (extra.errors) { - extra.errors.push(e); - } else { - throw e; + if (node.type === Syntax.Program) { + if (node.body.length > 0) { + return; + } } - } - } + if (extra.trailingComments.length > 0) { + if (extra.trailingComments[0].range[0] >= node.range[1]) { + trailingComments = extra.trailingComments; + extra.trailingComments = []; + } else { + extra.trailingComments.length = 0; + } + } else { + if (extra.bottomRightStack.length > 0 && + extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments && + extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments[0].range[0] >= node.range[1]) { + trailingComments = extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments; + delete extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments; + } + } - // Throw an exception because of the token. + // Eating the stack. + while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) { + lastChild = extra.bottomRightStack.pop(); + } - function throwUnexpected(token) { - if (token.type === Token.EOF) { - throwError(token, Messages.UnexpectedEOS); - } + if (lastChild) { + if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) { + node.leadingComments = lastChild.leadingComments; + delete lastChild.leadingComments; + } + } else if (extra.leadingComments.length > 0 && extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) { + node.leadingComments = extra.leadingComments; + extra.leadingComments = []; + } - if (token.type === Token.NumericLiteral) { - throwError(token, Messages.UnexpectedNumber); - } - if (token.type === Token.StringLiteral) { - throwError(token, Messages.UnexpectedString); - } + if (trailingComments) { + node.trailingComments = trailingComments; + } - if (token.type === Token.Identifier) { - throwError(token, Messages.UnexpectedIdentifier); - } + extra.bottomRightStack.push(node); + }, - if (token.type === Token.Keyword) { - if (isFutureReservedWord(token.value)) { - throwError(token, Messages.UnexpectedReserved); - } else if (strict && isStrictModeReservedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictReservedWord); - return; + markEnd: function (node, startToken) { + if (extra.range) { + node.range = [startToken.start, index]; + } + if (extra.loc) { + node.loc = new SourceLocation( + startToken.startLineNumber === undefined ? startToken.lineNumber : startToken.startLineNumber, + startToken.start - (startToken.startLineStart === undefined ? startToken.lineStart : startToken.startLineStart), + lineNumber, + index - lineStart + ); + this.postProcess(node); } - throwError(token, Messages.UnexpectedToken, token.value); - } - // BooleanLiteral, NullLiteral, or Punctuator. - throwError(token, Messages.UnexpectedToken, token.value); - } - - // Expect the next token to match the specified punctuator. - // If not, an exception will be thrown. - - function expect(value) { - var token = lex(); - if (token.type !== Token.Punctuator || token.value !== value) { - throwUnexpected(token); - } - } - - // Expect the next token to match the specified keyword. - // If not, an exception will be thrown. + if (extra.attachComment) { + this.processComment(node); + } + return node; + }, - function expectKeyword(keyword) { - var token = lex(); - if (token.type !== Token.Keyword || token.value !== keyword) { - throwUnexpected(token); - } - } + postProcess: function (node) { + if (extra.source) { + node.loc.source = extra.source; + } + return node; + }, - // Return true if the next token matches the specified punctuator. + createArrayExpression: function (elements) { + return { + type: Syntax.ArrayExpression, + elements: elements + }; + }, - function match(value) { - var token = lookahead(); - return token.type === Token.Punctuator && token.value === value; - } + createAssignmentExpression: function (operator, left, right) { + return { + type: Syntax.AssignmentExpression, + operator: operator, + left: left, + right: right + }; + }, - // Return true if the next token matches the specified keyword + createBinaryExpression: function (operator, left, right) { + var type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : + Syntax.BinaryExpression; + return { + type: type, + operator: operator, + left: left, + right: right + }; + }, - function matchKeyword(keyword) { - var token = lookahead(); - return token.type === Token.Keyword && token.value === keyword; - } + createBlockStatement: function (body) { + return { + type: Syntax.BlockStatement, + body: body + }; + }, - // Return true if the next token is an assignment operator + createBreakStatement: function (label) { + return { + type: Syntax.BreakStatement, + label: label + }; + }, - function matchAssign() { - var token = lookahead(), - op = token.value; + createCallExpression: function (callee, args) { + return { + type: Syntax.CallExpression, + callee: callee, + 'arguments': args + }; + }, - if (token.type !== Token.Punctuator) { - return false; - } - return op === '=' || - op === '*=' || - op === '/=' || - op === '%=' || - op === '+=' || - op === '-=' || - op === '<<=' || - op === '>>=' || - op === '>>>=' || - op === '&=' || - op === '^=' || - op === '|='; - } + createCatchClause: function (param, body) { + return { + type: Syntax.CatchClause, + param: param, + body: body + }; + }, - function consumeSemicolon() { - var token, line; + createConditionalExpression: function (test, consequent, alternate) { + return { + type: Syntax.ConditionalExpression, + test: test, + consequent: consequent, + alternate: alternate + }; + }, - // Catch the very common case first. - if (source[index] === ';') { - lex(); - return; - } + createContinueStatement: function (label) { + return { + type: Syntax.ContinueStatement, + label: label + }; + }, - line = lineNumber; - skipComment(); - if (lineNumber !== line) { - return; - } + createDebuggerStatement: function () { + return { + type: Syntax.DebuggerStatement + }; + }, - if (match(';')) { - lex(); - return; - } + createDoWhileStatement: function (body, test) { + return { + type: Syntax.DoWhileStatement, + body: body, + test: test + }; + }, - token = lookahead(); - if (token.type !== Token.EOF && !match('}')) { - throwUnexpected(token); - } - } + createEmptyStatement: function () { + return { + type: Syntax.EmptyStatement + }; + }, - // Return true if provided expression is LeftHandSideExpression + createExpressionStatement: function (expression) { + return { + type: Syntax.ExpressionStatement, + expression: expression + }; + }, - function isLeftHandSide(expr) { - return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression; - } + createForStatement: function (init, test, update, body) { + return { + type: Syntax.ForStatement, + init: init, + test: test, + update: update, + body: body + }; + }, - // 11.1.4 Array Initialiser + createForInStatement: function (left, right, body) { + return { + type: Syntax.ForInStatement, + left: left, + right: right, + body: body, + each: false + }; + }, - function parseArrayInitialiser() { - var elements = []; + createFunctionDeclaration: function (id, params, defaults, body) { + return { + type: Syntax.FunctionDeclaration, + id: id, + params: params, + defaults: defaults, + body: body, + rest: null, + generator: false, + expression: false + }; + }, - expect('['); + createFunctionExpression: function (id, params, defaults, body) { + return { + type: Syntax.FunctionExpression, + id: id, + params: params, + defaults: defaults, + body: body, + rest: null, + generator: false, + expression: false + }; + }, - while (!match(']')) { - if (match(',')) { - lex(); - elements.push(null); - } else { - elements.push(parseAssignmentExpression()); + createIdentifier: function (name) { + return { + type: Syntax.Identifier, + name: name + }; + }, - if (!match(']')) { - expect(','); - } - } - } + createIfStatement: function (test, consequent, alternate) { + return { + type: Syntax.IfStatement, + test: test, + consequent: consequent, + alternate: alternate + }; + }, - expect(']'); + createLabeledStatement: function (label, body) { + return { + type: Syntax.LabeledStatement, + label: label, + body: body + }; + }, - return { - type: Syntax.ArrayExpression, - elements: elements - }; - } + createLiteral: function (token) { + return { + type: Syntax.Literal, + value: token.value, + raw: source.slice(token.start, token.end) + }; + }, - // 11.1.5 Object Initialiser + createMemberExpression: function (accessor, object, property) { + return { + type: Syntax.MemberExpression, + computed: accessor === '[', + object: object, + property: property + }; + }, - function parsePropertyFunction(param, first) { - var previousStrict, body; + createNewExpression: function (callee, args) { + return { + type: Syntax.NewExpression, + callee: callee, + 'arguments': args + }; + }, - previousStrict = strict; - body = parseFunctionSourceElements(); - if (first && strict && isRestrictedWord(param[0].name)) { - throwErrorTolerant(first, Messages.StrictParamName); - } - strict = previousStrict; + createObjectExpression: function (properties) { + return { + type: Syntax.ObjectExpression, + properties: properties + }; + }, - return { - type: Syntax.FunctionExpression, - id: null, - params: param, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; - } + createPostfixExpression: function (operator, argument) { + return { + type: Syntax.UpdateExpression, + operator: operator, + argument: argument, + prefix: false + }; + }, - function parseObjectPropertyKey() { - var token = lex(); + createProgram: function (body) { + return { + type: Syntax.Program, + body: body + }; + }, - // Note: This function is called only from parseObjectProperty(), where - // EOF and Punctuator tokens are already filtered out. + createProperty: function (kind, key, value) { + return { + type: Syntax.Property, + key: key, + value: value, + kind: kind + }; + }, - if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) { - if (strict && token.octal) { - throwErrorTolerant(token, Messages.StrictOctalLiteral); - } - return createLiteral(token); - } + createReturnStatement: function (argument) { + return { + type: Syntax.ReturnStatement, + argument: argument + }; + }, - return { - type: Syntax.Identifier, - name: token.value - }; - } + createSequenceExpression: function (expressions) { + return { + type: Syntax.SequenceExpression, + expressions: expressions + }; + }, - function parseObjectProperty() { - var token, key, id, param; + createSwitchCase: function (test, consequent) { + return { + type: Syntax.SwitchCase, + test: test, + consequent: consequent + }; + }, - token = lookahead(); + createSwitchStatement: function (discriminant, cases) { + return { + type: Syntax.SwitchStatement, + discriminant: discriminant, + cases: cases + }; + }, - if (token.type === Token.Identifier) { + createThisExpression: function () { + return { + type: Syntax.ThisExpression + }; + }, - id = parseObjectPropertyKey(); + createThrowStatement: function (argument) { + return { + type: Syntax.ThrowStatement, + argument: argument + }; + }, - // Property Assignment: Getter and Setter. + createTryStatement: function (block, guardedHandlers, handlers, finalizer) { + return { + type: Syntax.TryStatement, + block: block, + guardedHandlers: guardedHandlers, + handlers: handlers, + finalizer: finalizer + }; + }, - if (token.value === 'get' && !match(':')) { - key = parseObjectPropertyKey(); - expect('('); - expect(')'); - return { - type: Syntax.Property, - key: key, - value: parsePropertyFunction([]), - kind: 'get' - }; - } else if (token.value === 'set' && !match(':')) { - key = parseObjectPropertyKey(); - expect('('); - token = lookahead(); - if (token.type !== Token.Identifier) { - expect(')'); - throwErrorTolerant(token, Messages.UnexpectedToken, token.value); - return { - type: Syntax.Property, - key: key, - value: parsePropertyFunction([]), - kind: 'set' - }; - } else { - param = [ parseVariableIdentifier() ]; - expect(')'); - return { - type: Syntax.Property, - key: key, - value: parsePropertyFunction(param, token), - kind: 'set' - }; - } - } else { - expect(':'); + createUnaryExpression: function (operator, argument) { + if (operator === '++' || operator === '--') { return { - type: Syntax.Property, - key: id, - value: parseAssignmentExpression(), - kind: 'init' + type: Syntax.UpdateExpression, + operator: operator, + argument: argument, + prefix: true }; } - } else if (token.type === Token.EOF || token.type === Token.Punctuator) { - throwUnexpected(token); - } else { - key = parseObjectPropertyKey(); - expect(':'); return { - type: Syntax.Property, - key: key, - value: parseAssignmentExpression(), - kind: 'init' + type: Syntax.UnaryExpression, + operator: operator, + argument: argument, + prefix: true }; - } - } - - function parseObjectInitialiser() { - var properties = [], property, name, kind, map = {}, toString = String; - - expect('{'); + }, - while (!match('}')) { - property = parseObjectProperty(); + createVariableDeclaration: function (declarations, kind) { + return { + type: Syntax.VariableDeclaration, + declarations: declarations, + kind: kind + }; + }, - if (property.key.type === Syntax.Identifier) { - name = property.key.name; - } else { - name = toString(property.key.value); - } - kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set; - if (Object.prototype.hasOwnProperty.call(map, name)) { - if (map[name] === PropertyKind.Data) { - if (strict && kind === PropertyKind.Data) { - throwErrorTolerant({}, Messages.StrictDuplicateProperty); - } else if (kind !== PropertyKind.Data) { - throwErrorTolerant({}, Messages.AccessorDataProperty); - } - } else { - if (kind === PropertyKind.Data) { - throwErrorTolerant({}, Messages.AccessorDataProperty); - } else if (map[name] & kind) { - throwErrorTolerant({}, Messages.AccessorGetSet); - } - } - map[name] |= kind; - } else { - map[name] = kind; - } + createVariableDeclarator: function (id, init) { + return { + type: Syntax.VariableDeclarator, + id: id, + init: init + }; + }, - properties.push(property); + createWhileStatement: function (test, body) { + return { + type: Syntax.WhileStatement, + test: test, + body: body + }; + }, - if (!match('}')) { - expect(','); - } + createWithStatement: function (object, body) { + return { + type: Syntax.WithStatement, + object: object, + body: body + }; } + }; - expect('}'); - - return { - type: Syntax.ObjectExpression, - properties: properties - }; - } - - // 11.1.6 The Grouping Operator - - function parseGroupExpression() { - var expr; - - expect('('); + // Return true if there is a line terminator before the next token. - expr = parseExpression(); + function peekLineTerminator() { + var pos, line, start, found; - expect(')'); + pos = index; + line = lineNumber; + start = lineStart; + skipComment(); + found = lineNumber !== line; + index = pos; + lineNumber = line; + lineStart = start; - return expr; + return found; } + // Throw an exception - // 11.1 Primary Expressions - - function parsePrimaryExpression() { - var token = lookahead(), - type = token.type; - - if (type === Token.Identifier) { - return { - type: Syntax.Identifier, - name: lex().value - }; - } + function throwError(token, messageFormat) { + var error, + args = Array.prototype.slice.call(arguments, 2), + msg = messageFormat.replace( + /%(\d)/g, + function (whole, index) { + assert(index < args.length, 'Message reference must be in range'); + return args[index]; + } + ); - if (type === Token.StringLiteral || type === Token.NumericLiteral) { - if (strict && token.octal) { - throwErrorTolerant(token, Messages.StrictOctalLiteral); - } - return createLiteral(lex()); + if (typeof token.lineNumber === 'number') { + error = new Error('Line ' + token.lineNumber + ': ' + msg); + error.index = token.start; + error.lineNumber = token.lineNumber; + error.column = token.start - lineStart + 1; + } else { + error = new Error('Line ' + lineNumber + ': ' + msg); + error.index = index; + error.lineNumber = lineNumber; + error.column = index - lineStart + 1; } - if (type === Token.Keyword) { - if (matchKeyword('this')) { - lex(); - return { - type: Syntax.ThisExpression - }; - } + error.description = msg; + throw error; + } - if (matchKeyword('function')) { - return parseFunctionExpression(); + function throwErrorTolerant() { + try { + throwError.apply(null, arguments); + } catch (e) { + if (extra.errors) { + extra.errors.push(e); + } else { + throw e; } } + } - if (type === Token.BooleanLiteral) { - lex(); - token.value = (token.value === 'true'); - return createLiteral(token); - } - if (type === Token.NullLiteral) { - lex(); - token.value = null; - return createLiteral(token); + // Throw an exception because of the token. + + function throwUnexpected(token) { + if (token.type === Token.EOF) { + throwError(token, Messages.UnexpectedEOS); } - if (match('[')) { - return parseArrayInitialiser(); + if (token.type === Token.NumericLiteral) { + throwError(token, Messages.UnexpectedNumber); } - if (match('{')) { - return parseObjectInitialiser(); + if (token.type === Token.StringLiteral) { + throwError(token, Messages.UnexpectedString); } - if (match('(')) { - return parseGroupExpression(); + if (token.type === Token.Identifier) { + throwError(token, Messages.UnexpectedIdentifier); } - if (match('/') || match('/=')) { - return createLiteral(scanRegExp()); + if (token.type === Token.Keyword) { + if (isFutureReservedWord(token.value)) { + throwError(token, Messages.UnexpectedReserved); + } else if (strict && isStrictModeReservedWord(token.value)) { + throwErrorTolerant(token, Messages.StrictReservedWord); + return; + } + throwError(token, Messages.UnexpectedToken, token.value); } - return throwUnexpected(lex()); + // BooleanLiteral, NullLiteral, or Punctuator. + throwError(token, Messages.UnexpectedToken, token.value); } - // 11.2 Left-Hand-Side Expressions - - function parseArguments() { - var args = []; - - expect('('); + // Expect the next token to match the specified punctuator. + // If not, an exception will be thrown. - if (!match(')')) { - while (index < length) { - args.push(parseAssignmentExpression()); - if (match(')')) { - break; - } - expect(','); - } + function expect(value) { + var token = lex(); + if (token.type !== Token.Punctuator || token.value !== value) { + throwUnexpected(token); } - - expect(')'); - - return args; } - function parseNonComputedProperty() { - var token = lex(); + // Expect the next token to match the specified keyword. + // If not, an exception will be thrown. - if (!isIdentifierName(token)) { + function expectKeyword(keyword) { + var token = lex(); + if (token.type !== Token.Keyword || token.value !== keyword) { throwUnexpected(token); } - - return { - type: Syntax.Identifier, - name: token.value - }; } - function parseNonComputedMember() { - expect('.'); + // Return true if the next token matches the specified punctuator. - return parseNonComputedProperty(); + function match(value) { + return lookahead.type === Token.Punctuator && lookahead.value === value; } - function parseComputedMember() { - var expr; + // Return true if the next token matches the specified keyword - expect('['); + function matchKeyword(keyword) { + return lookahead.type === Token.Keyword && lookahead.value === keyword; + } - expr = parseExpression(); + // Return true if the next token is an assignment operator - expect(']'); + function matchAssign() { + var op; - return expr; + if (lookahead.type !== Token.Punctuator) { + return false; + } + op = lookahead.value; + return op === '=' || + op === '*=' || + op === '/=' || + op === '%=' || + op === '+=' || + op === '-=' || + op === '<<=' || + op === '>>=' || + op === '>>>=' || + op === '&=' || + op === '^=' || + op === '|='; } - function parseNewExpression() { - var expr; - - expectKeyword('new'); - - expr = { - type: Syntax.NewExpression, - callee: parseLeftHandSideExpression(), - 'arguments': [] - }; + function consumeSemicolon() { + var line; - if (match('(')) { - expr['arguments'] = parseArguments(); + // Catch the very common case first: immediately a semicolon (U+003B). + if (source.charCodeAt(index) === 0x3B || match(';')) { + lex(); + return; } - return expr; + line = lineNumber; + skipComment(); + if (lineNumber !== line) { + return; + } + + if (lookahead.type !== Token.EOF && !match('}')) { + throwUnexpected(lookahead); + } } - function parseLeftHandSideExpressionAllowCall() { - var expr; + // Return true if provided expression is LeftHandSideExpression - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); + function isLeftHandSide(expr) { + return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression; + } - while (match('.') || match('[') || match('(')) { - if (match('(')) { - expr = { - type: Syntax.CallExpression, - callee: expr, - 'arguments': parseArguments() - }; - } else if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; + // 11.1.4 Array Initialiser + + function parseArrayInitialiser() { + var elements = [], startToken; + + startToken = lookahead; + expect('['); + + while (!match(']')) { + if (match(',')) { + lex(); + elements.push(null); } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; + elements.push(parseAssignmentExpression()); + + if (!match(']')) { + expect(','); + } } } - return expr; + lex(); + + return delegate.markEnd(delegate.createArrayExpression(elements), startToken); } + // 11.1.5 Object Initialiser - function parseLeftHandSideExpression() { - var expr; + function parsePropertyFunction(param, first) { + var previousStrict, body, startToken; - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); + previousStrict = strict; + startToken = lookahead; + body = parseFunctionSourceElements(); + if (first && strict && isRestrictedWord(param[0].name)) { + throwErrorTolerant(first, Messages.StrictParamName); + } + strict = previousStrict; + return delegate.markEnd(delegate.createFunctionExpression(null, param, [], body), startToken); + } - while (match('.') || match('[')) { - if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; + function parseObjectPropertyKey() { + var token, startToken; + + startToken = lookahead; + token = lex(); + + // Note: This function is called only from parseObjectProperty(), where + // EOF and Punctuator tokens are already filtered out. + + if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) { + if (strict && token.octal) { + throwErrorTolerant(token, Messages.StrictOctalLiteral); } + return delegate.markEnd(delegate.createLiteral(token), startToken); } - return expr; + return delegate.markEnd(delegate.createIdentifier(token.value), startToken); } - // 11.3 Postfix Expressions + function parseObjectProperty() { + var token, key, id, value, param, startToken; - function parsePostfixExpression() { - var expr = parseLeftHandSideExpressionAllowCall(), token; + token = lookahead; + startToken = lookahead; - token = lookahead(); - if (token.type !== Token.Punctuator) { - return expr; - } + if (token.type === Token.Identifier) { - if ((match('++') || match('--')) && !peekLineTerminator()) { - // 11.3.1, 11.3.2 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant({}, Messages.StrictLHSPostfix); + id = parseObjectPropertyKey(); + + // Property Assignment: Getter and Setter. + + if (token.value === 'get' && !match(':')) { + key = parseObjectPropertyKey(); + expect('('); + expect(')'); + value = parsePropertyFunction([]); + return delegate.markEnd(delegate.createProperty('get', key, value), startToken); } - if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + if (token.value === 'set' && !match(':')) { + key = parseObjectPropertyKey(); + expect('('); + token = lookahead; + if (token.type !== Token.Identifier) { + expect(')'); + throwErrorTolerant(token, Messages.UnexpectedToken, token.value); + value = parsePropertyFunction([]); + } else { + param = [ parseVariableIdentifier() ]; + expect(')'); + value = parsePropertyFunction(param, token); + } + return delegate.markEnd(delegate.createProperty('set', key, value), startToken); } - - expr = { - type: Syntax.UpdateExpression, - operator: lex().value, - argument: expr, - prefix: false - }; + expect(':'); + value = parseAssignmentExpression(); + return delegate.markEnd(delegate.createProperty('init', id, value), startToken); + } + if (token.type === Token.EOF || token.type === Token.Punctuator) { + throwUnexpected(token); + } else { + key = parseObjectPropertyKey(); + expect(':'); + value = parseAssignmentExpression(); + return delegate.markEnd(delegate.createProperty('init', key, value), startToken); } - - return expr; } - // 11.4 Unary Operators + function parseObjectInitialiser() { + var properties = [], property, name, key, kind, map = {}, toString = String, startToken; - function parseUnaryExpression() { - var token, expr; + startToken = lookahead; - token = lookahead(); - if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { - return parsePostfixExpression(); - } + expect('{'); - if (match('++') || match('--')) { - token = lex(); - expr = parseUnaryExpression(); - // 11.4.4, 11.4.5 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant({}, Messages.StrictLHSPrefix); - } + while (!match('}')) { + property = parseObjectProperty(); - if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + if (property.key.type === Syntax.Identifier) { + name = property.key.name; + } else { + name = toString(property.key.value); } + kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set; - expr = { - type: Syntax.UpdateExpression, - operator: token.value, - argument: expr, - prefix: true - }; - return expr; - } + key = '$' + name; + if (Object.prototype.hasOwnProperty.call(map, key)) { + if (map[key] === PropertyKind.Data) { + if (strict && kind === PropertyKind.Data) { + throwErrorTolerant({}, Messages.StrictDuplicateProperty); + } else if (kind !== PropertyKind.Data) { + throwErrorTolerant({}, Messages.AccessorDataProperty); + } + } else { + if (kind === PropertyKind.Data) { + throwErrorTolerant({}, Messages.AccessorDataProperty); + } else if (map[key] & kind) { + throwErrorTolerant({}, Messages.AccessorGetSet); + } + } + map[key] |= kind; + } else { + map[key] = kind; + } - if (match('+') || match('-') || match('~') || match('!')) { - expr = { - type: Syntax.UnaryExpression, - operator: lex().value, - argument: parseUnaryExpression(), - prefix: true - }; - return expr; - } + properties.push(property); - if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { - expr = { - type: Syntax.UnaryExpression, - operator: lex().value, - argument: parseUnaryExpression(), - prefix: true - }; - if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { - throwErrorTolerant({}, Messages.StrictDelete); + if (!match('}')) { + expect(','); } - return expr; } - return parsePostfixExpression(); + expect('}'); + + return delegate.markEnd(delegate.createObjectExpression(properties), startToken); } - // 11.5 Multiplicative Operators + // 11.1.6 The Grouping Operator + + function parseGroupExpression() { + var expr; - function parseMultiplicativeExpression() { - var expr = parseUnaryExpression(); + expect('('); - while (match('*') || match('/') || match('%')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseUnaryExpression() - }; - } + expr = parseExpression(); + + expect(')'); return expr; } - // 11.6 Additive Operators - function parseAdditiveExpression() { - var expr = parseMultiplicativeExpression(); + // 11.1 Primary Expressions - while (match('+') || match('-')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseMultiplicativeExpression() - }; + function parsePrimaryExpression() { + var type, token, expr, startToken; + + if (match('(')) { + return parseGroupExpression(); } - return expr; - } + if (match('[')) { + return parseArrayInitialiser(); + } - // 11.7 Bitwise Shift Operators + if (match('{')) { + return parseObjectInitialiser(); + } - function parseShiftExpression() { - var expr = parseAdditiveExpression(); + type = lookahead.type; + startToken = lookahead; - while (match('<<') || match('>>') || match('>>>')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseAdditiveExpression() - }; + if (type === Token.Identifier) { + expr = delegate.createIdentifier(lex().value); + } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { + if (strict && lookahead.octal) { + throwErrorTolerant(lookahead, Messages.StrictOctalLiteral); + } + expr = delegate.createLiteral(lex()); + } else if (type === Token.Keyword) { + if (matchKeyword('function')) { + return parseFunctionExpression(); + } + if (matchKeyword('this')) { + lex(); + expr = delegate.createThisExpression(); + } else { + throwUnexpected(lex()); + } + } else if (type === Token.BooleanLiteral) { + token = lex(); + token.value = (token.value === 'true'); + expr = delegate.createLiteral(token); + } else if (type === Token.NullLiteral) { + token = lex(); + token.value = null; + expr = delegate.createLiteral(token); + } else if (match('/') || match('/=')) { + if (typeof extra.tokens !== 'undefined') { + expr = delegate.createLiteral(collectRegex()); + } else { + expr = delegate.createLiteral(scanRegExp()); + } + peek(); + } else { + throwUnexpected(lex()); } - return expr; + return delegate.markEnd(expr, startToken); } - // 11.8 Relational Operators - function parseRelationalExpression() { - var expr, previousAllowIn; + // 11.2 Left-Hand-Side Expressions - previousAllowIn = state.allowIn; - state.allowIn = true; + function parseArguments() { + var args = []; - expr = parseShiftExpression(); + expect('('); - while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseShiftExpression() - }; + if (!match(')')) { + while (index < length) { + args.push(parseAssignmentExpression()); + if (match(')')) { + break; + } + expect(','); + } } - state.allowIn = previousAllowIn; - return expr; + expect(')'); + + return args; } - // 11.9 Equality Operators + function parseNonComputedProperty() { + var token, startToken; - function parseEqualityExpression() { - var expr = parseRelationalExpression(); + startToken = lookahead; + token = lex(); - while (match('==') || match('!=') || match('===') || match('!==')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseRelationalExpression() - }; + if (!isIdentifierName(token)) { + throwUnexpected(token); } - return expr; + return delegate.markEnd(delegate.createIdentifier(token.value), startToken); } - // 11.10 Binary Bitwise Operators + function parseNonComputedMember() { + expect('.'); - function parseBitwiseANDExpression() { - var expr = parseEqualityExpression(); + return parseNonComputedProperty(); + } - while (match('&')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '&', - left: expr, - right: parseEqualityExpression() - }; - } + function parseComputedMember() { + var expr; - return expr; - } + expect('['); - function parseBitwiseXORExpression() { - var expr = parseBitwiseANDExpression(); + expr = parseExpression(); - while (match('^')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '^', - left: expr, - right: parseBitwiseANDExpression() - }; - } + expect(']'); return expr; } - function parseBitwiseORExpression() { - var expr = parseBitwiseXORExpression(); + function parseNewExpression() { + var callee, args, startToken; - while (match('|')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '|', - left: expr, - right: parseBitwiseXORExpression() - }; - } + startToken = lookahead; + expectKeyword('new'); + callee = parseLeftHandSideExpression(); + args = match('(') ? parseArguments() : []; - return expr; + return delegate.markEnd(delegate.createNewExpression(callee, args), startToken); } - // 11.11 Binary Logical Operators + function parseLeftHandSideExpressionAllowCall() { + var previousAllowIn, expr, args, property, startToken; - function parseLogicalANDExpression() { - var expr = parseBitwiseORExpression(); + startToken = lookahead; - while (match('&&')) { - lex(); - expr = { - type: Syntax.LogicalExpression, - operator: '&&', - left: expr, - right: parseBitwiseORExpression() - }; + previousAllowIn = state.allowIn; + state.allowIn = true; + expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); + state.allowIn = previousAllowIn; + + for (;;) { + if (match('.')) { + property = parseNonComputedMember(); + expr = delegate.createMemberExpression('.', expr, property); + } else if (match('(')) { + args = parseArguments(); + expr = delegate.createCallExpression(expr, args); + } else if (match('[')) { + property = parseComputedMember(); + expr = delegate.createMemberExpression('[', expr, property); + } else { + break; + } + delegate.markEnd(expr, startToken); } return expr; } - function parseLogicalORExpression() { - var expr = parseLogicalANDExpression(); + function parseLeftHandSideExpression() { + var previousAllowIn, expr, property, startToken; + + startToken = lookahead; - while (match('||')) { - lex(); - expr = { - type: Syntax.LogicalExpression, - operator: '||', - left: expr, - right: parseLogicalANDExpression() - }; + previousAllowIn = state.allowIn; + expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); + state.allowIn = previousAllowIn; + + while (match('.') || match('[')) { + if (match('[')) { + property = parseComputedMember(); + expr = delegate.createMemberExpression('[', expr, property); + } else { + property = parseNonComputedMember(); + expr = delegate.createMemberExpression('.', expr, property); + } + delegate.markEnd(expr, startToken); + } + + return expr; + } + + // 11.3 Postfix Expressions + + function parsePostfixExpression() { + var expr, token, startToken = lookahead; + + expr = parseLeftHandSideExpressionAllowCall(); + + if (lookahead.type === Token.Punctuator) { + if ((match('++') || match('--')) && !peekLineTerminator()) { + // 11.3.1, 11.3.2 + if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { + throwErrorTolerant({}, Messages.StrictLHSPostfix); + } + + if (!isLeftHandSide(expr)) { + throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + } + + token = lex(); + expr = delegate.markEnd(delegate.createPostfixExpression(token.value, expr), startToken); + } + } + + return expr; + } + + // 11.4 Unary Operators + + function parseUnaryExpression() { + var token, expr, startToken; + + if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { + expr = parsePostfixExpression(); + } else if (match('++') || match('--')) { + startToken = lookahead; + token = lex(); + expr = parseUnaryExpression(); + // 11.4.4, 11.4.5 + if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { + throwErrorTolerant({}, Messages.StrictLHSPrefix); + } + + if (!isLeftHandSide(expr)) { + throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + } + + expr = delegate.createUnaryExpression(token.value, expr); + expr = delegate.markEnd(expr, startToken); + } else if (match('+') || match('-') || match('~') || match('!')) { + startToken = lookahead; + token = lex(); + expr = parseUnaryExpression(); + expr = delegate.createUnaryExpression(token.value, expr); + expr = delegate.markEnd(expr, startToken); + } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { + startToken = lookahead; + token = lex(); + expr = parseUnaryExpression(); + expr = delegate.createUnaryExpression(token.value, expr); + expr = delegate.markEnd(expr, startToken); + if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { + throwErrorTolerant({}, Messages.StrictDelete); + } + } else { + expr = parsePostfixExpression(); + } + + return expr; + } + + function binaryPrecedence(token, allowIn) { + var prec = 0; + + if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { + return 0; + } + + switch (token.value) { + case '||': + prec = 1; + break; + + case '&&': + prec = 2; + break; + + case '|': + prec = 3; + break; + + case '^': + prec = 4; + break; + + case '&': + prec = 5; + break; + + case '==': + case '!=': + case '===': + case '!==': + prec = 6; + break; + + case '<': + case '>': + case '<=': + case '>=': + case 'instanceof': + prec = 7; + break; + + case 'in': + prec = allowIn ? 7 : 0; + break; + + case '<<': + case '>>': + case '>>>': + prec = 8; + break; + + case '+': + case '-': + prec = 9; + break; + + case '*': + case '/': + case '%': + prec = 11; + break; + + default: + break; + } + + return prec; + } + + // 11.5 Multiplicative Operators + // 11.6 Additive Operators + // 11.7 Bitwise Shift Operators + // 11.8 Relational Operators + // 11.9 Equality Operators + // 11.10 Binary Bitwise Operators + // 11.11 Binary Logical Operators + + function parseBinaryExpression() { + var marker, markers, expr, token, prec, stack, right, operator, left, i; + + marker = lookahead; + left = parseUnaryExpression(); + + token = lookahead; + prec = binaryPrecedence(token, state.allowIn); + if (prec === 0) { + return left; + } + token.prec = prec; + lex(); + + markers = [marker, lookahead]; + right = parseUnaryExpression(); + + stack = [left, token, right]; + + while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) { + + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { + right = stack.pop(); + operator = stack.pop().value; + left = stack.pop(); + expr = delegate.createBinaryExpression(operator, left, right); + markers.pop(); + marker = markers[markers.length - 1]; + delegate.markEnd(expr, marker); + stack.push(expr); + } + + // Shift. + token = lex(); + token.prec = prec; + stack.push(token); + markers.push(lookahead); + expr = parseUnaryExpression(); + stack.push(expr); + } + + // Final reduce to clean-up the stack. + i = stack.length - 1; + expr = stack[i]; + markers.pop(); + while (i > 1) { + expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr); + i -= 2; + marker = markers.pop(); + delegate.markEnd(expr, marker); } return expr; } + // 11.12 Conditional Operator function parseConditionalExpression() { - var expr, previousAllowIn, consequent; + var expr, previousAllowIn, consequent, alternate, startToken; + + startToken = lookahead; - expr = parseLogicalORExpression(); + expr = parseBinaryExpression(); if (match('?')) { lex(); @@ -6438,13 +7063,10 @@ parseStatement: true, parseSourceElement: true */ consequent = parseAssignmentExpression(); state.allowIn = previousAllowIn; expect(':'); + alternate = parseAssignmentExpression(); - expr = { - type: Syntax.ConditionalExpression, - test: expr, - consequent: consequent, - alternate: parseAssignmentExpression() - }; + expr = delegate.createConditionalExpression(expr, consequent, alternate); + delegate.markEnd(expr, startToken); } return expr; @@ -6453,43 +7075,41 @@ parseStatement: true, parseSourceElement: true */ // 11.13 Assignment Operators function parseAssignmentExpression() { - var token, expr; + var token, left, right, node, startToken; + + token = lookahead; + startToken = lookahead; - token = lookahead(); - expr = parseConditionalExpression(); + node = left = parseConditionalExpression(); if (matchAssign()) { // LeftHandSideExpression - if (!isLeftHandSide(expr)) { + if (!isLeftHandSide(left)) { throwErrorTolerant({}, Messages.InvalidLHSInAssignment); } // 11.13.1 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { + if (strict && left.type === Syntax.Identifier && isRestrictedWord(left.name)) { throwErrorTolerant(token, Messages.StrictLHSAssignment); } - expr = { - type: Syntax.AssignmentExpression, - operator: lex().value, - left: expr, - right: parseAssignmentExpression() - }; + token = lex(); + right = parseAssignmentExpression(); + node = delegate.markEnd(delegate.createAssignmentExpression(token.value, left, right), startToken); } - return expr; + return node; } // 11.14 Comma Operator function parseExpression() { - var expr = parseAssignmentExpression(); + var expr, startToken = lookahead; + + expr = parseAssignmentExpression(); if (match(',')) { - expr = { - type: Syntax.SequenceExpression, - expressions: [ expr ] - }; + expr = delegate.createSequenceExpression([ expr ]); while (index < length) { if (!match(',')) { @@ -6499,7 +7119,9 @@ parseStatement: true, parseSourceElement: true */ expr.expressions.push(parseAssignmentExpression()); } + delegate.markEnd(expr, startToken); } + return expr; } @@ -6524,38 +7146,38 @@ parseStatement: true, parseSourceElement: true */ } function parseBlock() { - var block; + var block, startToken; + startToken = lookahead; expect('{'); block = parseStatementList(); expect('}'); - return { - type: Syntax.BlockStatement, - body: block - }; + return delegate.markEnd(delegate.createBlockStatement(block), startToken); } // 12.2 Variable Statement function parseVariableIdentifier() { - var token = lex(); + var token, startToken; + + startToken = lookahead; + token = lex(); if (token.type !== Token.Identifier) { throwUnexpected(token); } - return { - type: Syntax.Identifier, - name: token.value - }; + return delegate.markEnd(delegate.createIdentifier(token.value), startToken); } function parseVariableDeclaration(kind) { - var id = parseVariableIdentifier(), - init = null; + var init = null, id, startToken; + + startToken = lookahead; + id = parseVariableIdentifier(); // 12.2.1 if (strict && isRestrictedWord(id.name)) { @@ -6570,11 +7192,7 @@ parseStatement: true, parseSourceElement: true */ init = parseAssignmentExpression(); } - return { - type: Syntax.VariableDeclarator, - id: id, - init: init - }; + return delegate.markEnd(delegate.createVariableDeclarator(id, init), startToken); } function parseVariableDeclarationList(kind) { @@ -6600,11 +7218,7 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: 'var' - }; + return delegate.createVariableDeclaration(declarations, 'var'); } // kind may be `const` or `let` @@ -6612,7 +7226,9 @@ parseStatement: true, parseSourceElement: true */ // see http://wiki.ecmascript.org/doku.php?id=harmony:const // and http://wiki.ecmascript.org/doku.php?id=harmony:let function parseConstLetDeclaration(kind) { - var declarations; + var declarations, startToken; + + startToken = lookahead; expectKeyword(kind); @@ -6620,34 +7236,22 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: kind - }; + return delegate.markEnd(delegate.createVariableDeclaration(declarations, kind), startToken); } // 12.3 Empty Statement function parseEmptyStatement() { expect(';'); - - return { - type: Syntax.EmptyStatement - }; + return delegate.createEmptyStatement(); } // 12.4 Expression Statement function parseExpressionStatement() { var expr = parseExpression(); - consumeSemicolon(); - - return { - type: Syntax.ExpressionStatement, - expression: expr - }; + return delegate.createExpressionStatement(expr); } // 12.5 If statement @@ -6672,12 +7276,7 @@ parseStatement: true, parseSourceElement: true */ alternate = null; } - return { - type: Syntax.IfStatement, - test: test, - consequent: consequent, - alternate: alternate - }; + return delegate.createIfStatement(test, consequent, alternate); } // 12.6 Iteration Statements @@ -6706,11 +7305,7 @@ parseStatement: true, parseSourceElement: true */ lex(); } - return { - type: Syntax.DoWhileStatement, - body: body, - test: test - }; + return delegate.createDoWhileStatement(body, test); } function parseWhileStatement() { @@ -6731,21 +7326,17 @@ parseStatement: true, parseSourceElement: true */ state.inIteration = oldInIteration; - return { - type: Syntax.WhileStatement, - test: test, - body: body - }; + return delegate.createWhileStatement(test, body); } function parseForVariableDeclaration() { - var token = lex(); + var token, declarations, startToken; - return { - type: Syntax.VariableDeclaration, - declarations: parseVariableDeclarationList(), - kind: token.value - }; + startToken = lookahead; + token = lex(); + declarations = parseVariableDeclarationList(); + + return delegate.markEnd(delegate.createVariableDeclaration(declarations, token.value), startToken); } function parseForStatement() { @@ -6815,44 +7406,27 @@ parseStatement: true, parseSourceElement: true */ state.inIteration = oldInIteration; - if (typeof left === 'undefined') { - return { - type: Syntax.ForStatement, - init: init, - test: test, - update: update, - body: body - }; - } - - return { - type: Syntax.ForInStatement, - left: left, - right: right, - body: body, - each: false - }; + return (typeof left === 'undefined') ? + delegate.createForStatement(init, test, update, body) : + delegate.createForInStatement(left, right, body); } // 12.7 The continue statement function parseContinueStatement() { - var token, label = null; + var label = null, key; expectKeyword('continue'); // Optimize the most common form: 'continue;'. - if (source[index] === ';') { + if (source.charCodeAt(index) === 0x3B) { lex(); if (!state.inIteration) { throwError({}, Messages.IllegalContinue); } - return { - type: Syntax.ContinueStatement, - label: null - }; + return delegate.createContinueStatement(null); } if (peekLineTerminator()) { @@ -6860,17 +7434,14 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalContinue); } - return { - type: Syntax.ContinueStatement, - label: null - }; + return delegate.createContinueStatement(null); } - token = lookahead(); - if (token.type === Token.Identifier) { + if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); - if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) { + key = '$' + label.name; + if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError({}, Messages.UnknownLabel, label.name); } } @@ -6881,31 +7452,25 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalContinue); } - return { - type: Syntax.ContinueStatement, - label: label - }; + return delegate.createContinueStatement(label); } // 12.8 The break statement function parseBreakStatement() { - var token, label = null; + var label = null, key; expectKeyword('break'); - // Optimize the most common form: 'break;'. - if (source[index] === ';') { + // Catch the very common case first: immediately a semicolon (U+003B). + if (source.charCodeAt(index) === 0x3B) { lex(); if (!(state.inIteration || state.inSwitch)) { throwError({}, Messages.IllegalBreak); } - return { - type: Syntax.BreakStatement, - label: null - }; + return delegate.createBreakStatement(null); } if (peekLineTerminator()) { @@ -6913,17 +7478,14 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalBreak); } - return { - type: Syntax.BreakStatement, - label: null - }; + return delegate.createBreakStatement(null); } - token = lookahead(); - if (token.type === Token.Identifier) { + if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); - if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) { + key = '$' + label.name; + if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError({}, Messages.UnknownLabel, label.name); } } @@ -6934,16 +7496,13 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalBreak); } - return { - type: Syntax.BreakStatement, - label: label - }; + return delegate.createBreakStatement(label); } // 12.9 The return statement function parseReturnStatement() { - var token, argument = null; + var argument = null; expectKeyword('return'); @@ -6952,37 +7511,27 @@ parseStatement: true, parseSourceElement: true */ } // 'return' followed by a space and an identifier is very common. - if (source[index] === ' ') { - if (isIdentifierStart(source[index + 1])) { + if (source.charCodeAt(index) === 0x20) { + if (isIdentifierStart(source.charCodeAt(index + 1))) { argument = parseExpression(); consumeSemicolon(); - return { - type: Syntax.ReturnStatement, - argument: argument - }; + return delegate.createReturnStatement(argument); } } if (peekLineTerminator()) { - return { - type: Syntax.ReturnStatement, - argument: null - }; + return delegate.createReturnStatement(null); } if (!match(';')) { - token = lookahead(); - if (!match('}') && token.type !== Token.EOF) { + if (!match('}') && lookahead.type !== Token.EOF) { argument = parseExpression(); } } consumeSemicolon(); - return { - type: Syntax.ReturnStatement, - argument: argument - }; + return delegate.createReturnStatement(argument); } // 12.10 The with statement @@ -6991,6 +7540,8 @@ parseStatement: true, parseSourceElement: true */ var object, body; if (strict) { + // TODO(ikarienator): Should we update the test cases instead? + skipComment(); throwErrorTolerant({}, Messages.StrictModeWith); } @@ -7004,20 +7555,15 @@ parseStatement: true, parseSourceElement: true */ body = parseStatement(); - return { - type: Syntax.WithStatement, - object: object, - body: body - }; + return delegate.createWithStatement(object, body); } // 12.10 The swith statement function parseSwitchCase() { - var test, - consequent = [], - statement; + var test, consequent = [], statement, startToken; + startToken = lookahead; if (matchKeyword('default')) { lex(); test = null; @@ -7032,17 +7578,10 @@ parseStatement: true, parseSourceElement: true */ break; } statement = parseStatement(); - if (typeof statement === 'undefined') { - break; - } consequent.push(statement); } - return { - type: Syntax.SwitchCase, - test: test, - consequent: consequent - }; + return delegate.markEnd(delegate.createSwitchCase(test, consequent), startToken); } function parseSwitchStatement() { @@ -7062,11 +7601,7 @@ parseStatement: true, parseSourceElement: true */ if (match('}')) { lex(); - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; + return delegate.createSwitchStatement(discriminant, cases); } oldInSwitch = state.inSwitch; @@ -7091,11 +7626,7 @@ parseStatement: true, parseSourceElement: true */ expect('}'); - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; + return delegate.createSwitchStatement(discriminant, cases); } // 12.13 The throw statement @@ -7113,22 +7644,20 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.ThrowStatement, - argument: argument - }; + return delegate.createThrowStatement(argument); } // 12.14 The try statement function parseCatchClause() { - var param; + var param, body, startToken; + startToken = lookahead; expectKeyword('catch'); expect('('); if (match(')')) { - throwUnexpected(lookahead()); + throwUnexpected(lookahead); } param = parseVariableIdentifier(); @@ -7138,12 +7667,8 @@ parseStatement: true, parseSourceElement: true */ } expect(')'); - - return { - type: Syntax.CatchClause, - param: param, - body: parseBlock() - }; + body = parseBlock(); + return delegate.markEnd(delegate.createCatchClause(param, body), startToken); } function parseTryStatement() { @@ -7166,13 +7691,7 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.NoCatchOrFinally); } - return { - type: Syntax.TryStatement, - block: block, - guardedHandlers: [], - handlers: handlers, - finalizer: finalizer - }; + return delegate.createTryStatement(block, [], handlers, finalizer); } // 12.15 The debugger statement @@ -7182,65 +7701,69 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.DebuggerStatement - }; + return delegate.createDebuggerStatement(); } // 12 Statements function parseStatement() { - var token = lookahead(), + var type = lookahead.type, expr, - labeledBody; + labeledBody, + key, + startToken; - if (token.type === Token.EOF) { - throwUnexpected(token); + if (type === Token.EOF) { + throwUnexpected(lookahead); + } + + if (type === Token.Punctuator && lookahead.value === '{') { + return parseBlock(); } - if (token.type === Token.Punctuator) { - switch (token.value) { + startToken = lookahead; + + if (type === Token.Punctuator) { + switch (lookahead.value) { case ';': - return parseEmptyStatement(); - case '{': - return parseBlock(); + return delegate.markEnd(parseEmptyStatement(), startToken); case '(': - return parseExpressionStatement(); + return delegate.markEnd(parseExpressionStatement(), startToken); default: break; } } - if (token.type === Token.Keyword) { - switch (token.value) { + if (type === Token.Keyword) { + switch (lookahead.value) { case 'break': - return parseBreakStatement(); + return delegate.markEnd(parseBreakStatement(), startToken); case 'continue': - return parseContinueStatement(); + return delegate.markEnd(parseContinueStatement(), startToken); case 'debugger': - return parseDebuggerStatement(); + return delegate.markEnd(parseDebuggerStatement(), startToken); case 'do': - return parseDoWhileStatement(); + return delegate.markEnd(parseDoWhileStatement(), startToken); case 'for': - return parseForStatement(); + return delegate.markEnd(parseForStatement(), startToken); case 'function': - return parseFunctionDeclaration(); + return delegate.markEnd(parseFunctionDeclaration(), startToken); case 'if': - return parseIfStatement(); + return delegate.markEnd(parseIfStatement(), startToken); case 'return': - return parseReturnStatement(); + return delegate.markEnd(parseReturnStatement(), startToken); case 'switch': - return parseSwitchStatement(); + return delegate.markEnd(parseSwitchStatement(), startToken); case 'throw': - return parseThrowStatement(); + return delegate.markEnd(parseThrowStatement(), startToken); case 'try': - return parseTryStatement(); + return delegate.markEnd(parseTryStatement(), startToken); case 'var': - return parseVariableStatement(); + return delegate.markEnd(parseVariableStatement(), startToken); case 'while': - return parseWhileStatement(); + return delegate.markEnd(parseWhileStatement(), startToken); case 'with': - return parseWithStatement(); + return delegate.markEnd(parseWithStatement(), startToken); default: break; } @@ -7252,42 +7775,36 @@ parseStatement: true, parseSourceElement: true */ if ((expr.type === Syntax.Identifier) && match(':')) { lex(); - if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) { + key = '$' + expr.name; + if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError({}, Messages.Redeclaration, 'Label', expr.name); } - state.labelSet[expr.name] = true; + state.labelSet[key] = true; labeledBody = parseStatement(); - delete state.labelSet[expr.name]; - - return { - type: Syntax.LabeledStatement, - label: expr, - body: labeledBody - }; + delete state.labelSet[key]; + return delegate.markEnd(delegate.createLabeledStatement(expr, labeledBody), startToken); } consumeSemicolon(); - return { - type: Syntax.ExpressionStatement, - expression: expr - }; + return delegate.markEnd(delegate.createExpressionStatement(expr), startToken); } // 13 Function Definition function parseFunctionSourceElements() { var sourceElement, sourceElements = [], token, directive, firstRestricted, - oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody; + oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, startToken; + startToken = lookahead; expect('{'); while (index < length) { - token = lookahead(); - if (token.type !== Token.StringLiteral) { + if (lookahead.type !== Token.StringLiteral) { break; } + token = lookahead; sourceElement = parseSourceElement(); sourceElements.push(sourceElement); @@ -7295,7 +7812,7 @@ parseStatement: true, parseSourceElement: true */ // this is not directive break; } - directive = sliceSource(token.range[0] + 1, token.range[1] - 1); + directive = source.slice(token.start + 1, token.end - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { @@ -7336,45 +7853,25 @@ parseStatement: true, parseSourceElement: true */ state.inSwitch = oldInSwitch; state.inFunctionBody = oldInFunctionBody; - return { - type: Syntax.BlockStatement, - body: sourceElements - }; + return delegate.markEnd(delegate.createBlockStatement(sourceElements), startToken); } - function parseFunctionDeclaration() { - var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet; - - expectKeyword('function'); - token = lookahead(); - id = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictFunctionName); - } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } - } - - expect('('); + function parseParams(firstRestricted) { + var param, params = [], token, stricted, paramSet, key, message; + expect('('); if (!match(')')) { paramSet = {}; while (index < length) { - token = lookahead(); + token = lookahead; param = parseVariableIdentifier(); + key = '$' + token.value; if (strict) { if (isRestrictedWord(token.value)) { stricted = token; message = Messages.StrictParamName; } - if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { + if (Object.prototype.hasOwnProperty.call(paramSet, key)) { stricted = token; message = Messages.StrictParamDupe; } @@ -7385,13 +7882,13 @@ parseStatement: true, parseSourceElement: true */ } else if (isStrictModeReservedWord(token.value)) { firstRestricted = token; message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { + } else if (Object.prototype.hasOwnProperty.call(paramSet, key)) { firstRestricted = token; message = Messages.StrictParamDupe; } } params.push(param); - paramSet[param.name] = true; + paramSet[key] = true; if (match(')')) { break; } @@ -7401,6 +7898,44 @@ parseStatement: true, parseSourceElement: true */ expect(')'); + return { + params: params, + stricted: stricted, + firstRestricted: firstRestricted, + message: message + }; + } + + function parseFunctionDeclaration() { + var id, params = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, startToken; + + startToken = lookahead; + + expectKeyword('function'); + token = lookahead; + id = parseVariableIdentifier(); + if (strict) { + if (isRestrictedWord(token.value)) { + throwErrorTolerant(token, Messages.StrictFunctionName); + } + } else { + if (isRestrictedWord(token.value)) { + firstRestricted = token; + message = Messages.StrictFunctionName; + } else if (isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = Messages.StrictReservedWord; + } + } + + tmp = parseParams(firstRestricted); + params = tmp.params; + stricted = tmp.stricted; + firstRestricted = tmp.firstRestricted; + if (tmp.message) { + message = tmp.message; + } + previousStrict = strict; body = parseFunctionSourceElements(); if (strict && firstRestricted) { @@ -7411,25 +7946,17 @@ parseStatement: true, parseSourceElement: true */ } strict = previousStrict; - return { - type: Syntax.FunctionDeclaration, - id: id, - params: params, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; + return delegate.markEnd(delegate.createFunctionDeclaration(id, params, [], body), startToken); } function parseFunctionExpression() { - var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet; + var token, id = null, stricted, firstRestricted, message, tmp, params = [], body, previousStrict, startToken; + startToken = lookahead; expectKeyword('function'); if (!match('(')) { - token = lookahead(); + token = lookahead; id = parseVariableIdentifier(); if (strict) { if (isRestrictedWord(token.value)) { @@ -7446,45 +7973,14 @@ parseStatement: true, parseSourceElement: true */ } } - expect('('); - - if (!match(')')) { - paramSet = {}; - while (index < length) { - token = lookahead(); - param = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - stricted = token; - message = Messages.StrictParamName; - } - if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - stricted = token; - message = Messages.StrictParamDupe; - } - } else if (!firstRestricted) { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - firstRestricted = token; - message = Messages.StrictParamDupe; - } - } - params.push(param); - paramSet[param.name] = true; - if (match(')')) { - break; - } - expect(','); - } + tmp = parseParams(firstRestricted); + params = tmp.params; + stricted = tmp.stricted; + firstRestricted = tmp.firstRestricted; + if (tmp.message) { + message = tmp.message; } - expect(')'); - previousStrict = strict; body = parseFunctionSourceElements(); if (strict && firstRestricted) { @@ -7495,28 +7991,17 @@ parseStatement: true, parseSourceElement: true */ } strict = previousStrict; - return { - type: Syntax.FunctionExpression, - id: id, - params: params, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; + return delegate.markEnd(delegate.createFunctionExpression(id, params, [], body), startToken); } // 14 Program function parseSourceElement() { - var token = lookahead(); - - if (token.type === Token.Keyword) { - switch (token.value) { + if (lookahead.type === Token.Keyword) { + switch (lookahead.value) { case 'const': case 'let': - return parseConstLetDeclaration(token.value); + return parseConstLetDeclaration(lookahead.value); case 'function': return parseFunctionDeclaration(); default: @@ -7524,7 +8009,7 @@ parseStatement: true, parseSourceElement: true */ } } - if (token.type !== Token.EOF) { + if (lookahead.type !== Token.EOF) { return parseStatement(); } } @@ -7533,7 +8018,7 @@ parseStatement: true, parseSourceElement: true */ var sourceElement, sourceElements = [], token, directive, firstRestricted; while (index < length) { - token = lookahead(); + token = lookahead; if (token.type !== Token.StringLiteral) { break; } @@ -7544,7 +8029,7 @@ parseStatement: true, parseSourceElement: true */ // this is not directive break; } - directive = sliceSource(token.range[0] + 1, token.range[1] - 1); + directive = source.slice(token.start + 1, token.end - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { @@ -7559,6 +8044,7 @@ parseStatement: true, parseSourceElement: true */ while (index < length) { sourceElement = parseSourceElement(); + /* istanbul ignore if */ if (typeof sourceElement === 'undefined') { break; } @@ -7568,728 +8054,160 @@ parseStatement: true, parseSourceElement: true */ } function parseProgram() { - var program; - strict = false; - program = { - type: Syntax.Program, - body: parseSourceElements() - }; - return program; - } - - // The following functions are needed only when the option to preserve - // the comments is active. - - function addComment(type, value, start, end, loc) { - assert(typeof start === 'number', 'Comment must have valid position'); - - // Because the way the actual token is scanned, often the comments - // (if any) are skipped twice during the lexical analysis. - // Thus, we need to skip adding a comment if the comment array already - // handled it. - if (extra.comments.length > 0) { - if (extra.comments[extra.comments.length - 1].range[1] > start) { - return; - } - } - - extra.comments.push({ - type: type, - value: value, - range: [start, end], - loc: loc - }); - } - - function scanComment() { - var comment, ch, loc, start, blockComment, lineComment; - - comment = ''; - blockComment = false; - lineComment = false; + var body, startToken; - while (index < length) { - ch = source[index]; + skipComment(); + peek(); + startToken = lookahead; + strict = false; - if (lineComment) { - ch = source[index++]; - if (isLineTerminator(ch)) { - loc.end = { - line: lineNumber, - column: index - lineStart - 1 - }; - lineComment = false; - addComment('Line', comment, start, index - 1, loc); - if (ch === '\r' && source[index] === '\n') { - ++index; - } - ++lineNumber; - lineStart = index; - comment = ''; - } else if (index >= length) { - lineComment = false; - comment += ch; - loc.end = { - line: lineNumber, - column: length - lineStart - }; - addComment('Line', comment, start, length, loc); - } else { - comment += ch; - } - } else if (blockComment) { - if (isLineTerminator(ch)) { - if (ch === '\r' && source[index + 1] === '\n') { - ++index; - comment += '\r\n'; - } else { - comment += ch; - } - ++lineNumber; - ++index; - lineStart = index; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - ch = source[index++]; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - comment += ch; - if (ch === '*') { - ch = source[index]; - if (ch === '/') { - comment = comment.substr(0, comment.length - 1); - blockComment = false; - ++index; - loc.end = { - line: lineNumber, - column: index - lineStart - }; - addComment('Block', comment, start, index, loc); - comment = ''; - } - } - } - } else if (ch === '/') { - ch = source[index + 1]; - if (ch === '/') { - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; - start = index; - index += 2; - lineComment = true; - if (index >= length) { - loc.end = { - line: lineNumber, - column: index - lineStart - }; - lineComment = false; - addComment('Line', comment, start, index, loc); - } - } else if (ch === '*') { - start = index; - index += 2; - blockComment = true; - loc = { - start: { - line: lineNumber, - column: index - lineStart - 2 - } - }; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - break; - } - } else if (isWhiteSpace(ch)) { - ++index; - } else if (isLineTerminator(ch)) { - ++index; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - ++lineNumber; - lineStart = index; - } else { - break; - } - } + body = parseSourceElements(); + return delegate.markEnd(delegate.createProgram(body), startToken); } - function filterCommentLocation() { - var i, entry, comment, comments = []; + function filterTokenLocation() { + var i, entry, token, tokens = []; - for (i = 0; i < extra.comments.length; ++i) { - entry = extra.comments[i]; - comment = { + for (i = 0; i < extra.tokens.length; ++i) { + entry = extra.tokens[i]; + token = { type: entry.type, value: entry.value }; if (extra.range) { - comment.range = entry.range; + token.range = entry.range; } if (extra.loc) { - comment.loc = entry.loc; + token.loc = entry.loc; } - comments.push(comment); + tokens.push(token); } - extra.comments = comments; + extra.tokens = tokens; } - function collectToken() { - var start, loc, token, range, value; + function tokenize(code, options) { + var toString, + token, + tokens; - skipComment(); - start = index; - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; + toString = String; + if (typeof code !== 'string' && !(code instanceof String)) { + code = toString(code); + } - token = extra.advance(); - loc.end = { - line: lineNumber, - column: index - lineStart + delegate = SyntaxTreeDelegate; + source = code; + index = 0; + lineNumber = (source.length > 0) ? 1 : 0; + lineStart = 0; + length = source.length; + lookahead = null; + state = { + allowIn: true, + labelSet: {}, + inFunctionBody: false, + inIteration: false, + inSwitch: false, + lastCommentStart: -1 }; - if (token.type !== Token.EOF) { - range = [token.range[0], token.range[1]]; - value = sliceSource(token.range[0], token.range[1]); - extra.tokens.push({ - type: TokenName[token.type], - value: value, - range: range, - loc: loc - }); - } + extra = {}; - return token; - } + // Options matching. + options = options || {}; - function collectRegex() { - var pos, loc, regex, token; + // Of course we collect tokens here. + options.tokens = true; + extra.tokens = []; + extra.tokenize = true; + // The following two fields are necessary to compute the Regex tokens. + extra.openParenToken = -1; + extra.openCurlyToken = -1; - skipComment(); + extra.range = (typeof options.range === 'boolean') && options.range; + extra.loc = (typeof options.loc === 'boolean') && options.loc; - pos = index; - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; + if (typeof options.comment === 'boolean' && options.comment) { + extra.comments = []; + } + if (typeof options.tolerant === 'boolean' && options.tolerant) { + extra.errors = []; + } - regex = extra.scanRegExp(); - loc.end = { - line: lineNumber, - column: index - lineStart - }; + try { + peek(); + if (lookahead.type === Token.EOF) { + return extra.tokens; + } - // Pop the previous token, which is likely '/' or '/=' - if (extra.tokens.length > 0) { - token = extra.tokens[extra.tokens.length - 1]; - if (token.range[0] === pos && token.type === 'Punctuator') { - if (token.value === '/' || token.value === '/=') { - extra.tokens.pop(); + token = lex(); + while (lookahead.type !== Token.EOF) { + try { + token = lex(); + } catch (lexError) { + token = lookahead; + if (extra.errors) { + extra.errors.push(lexError); + // We have to break on the first error + // to avoid infinite loops. + break; + } else { + throw lexError; + } } } - } - extra.tokens.push({ - type: 'RegularExpression', - value: regex.literal, - range: [pos, index], - loc: loc - }); - - return regex; + filterTokenLocation(); + tokens = extra.tokens; + if (typeof extra.comments !== 'undefined') { + tokens.comments = extra.comments; + } + if (typeof extra.errors !== 'undefined') { + tokens.errors = extra.errors; + } + } catch (e) { + throw e; + } finally { + extra = {}; + } + return tokens; } - function filterTokenLocation() { - var i, entry, token, tokens = []; - - for (i = 0; i < extra.tokens.length; ++i) { - entry = extra.tokens[i]; - token = { - type: entry.type, - value: entry.value - }; - if (extra.range) { - token.range = entry.range; - } - if (extra.loc) { - token.loc = entry.loc; - } - tokens.push(token); - } - - extra.tokens = tokens; - } - - function createLiteral(token) { - return { - type: Syntax.Literal, - value: token.value - }; - } - - function createRawLiteral(token) { - return { - type: Syntax.Literal, - value: token.value, - raw: sliceSource(token.range[0], token.range[1]) - }; - } - - function createLocationMarker() { - var marker = {}; - - marker.range = [index, index]; - marker.loc = { - start: { - line: lineNumber, - column: index - lineStart - }, - end: { - line: lineNumber, - column: index - lineStart - } - }; - - marker.end = function () { - this.range[1] = index; - this.loc.end.line = lineNumber; - this.loc.end.column = index - lineStart; - }; - - marker.applyGroup = function (node) { - if (extra.range) { - node.groupRange = [this.range[0], this.range[1]]; - } - if (extra.loc) { - node.groupLoc = { - start: { - line: this.loc.start.line, - column: this.loc.start.column - }, - end: { - line: this.loc.end.line, - column: this.loc.end.column - } - }; - } - }; - - marker.apply = function (node) { - if (extra.range) { - node.range = [this.range[0], this.range[1]]; - } - if (extra.loc) { - node.loc = { - start: { - line: this.loc.start.line, - column: this.loc.start.column - }, - end: { - line: this.loc.end.line, - column: this.loc.end.column - } - }; - } - }; - - return marker; - } - - function trackGroupExpression() { - var marker, expr; - - skipComment(); - marker = createLocationMarker(); - expect('('); - - expr = parseExpression(); - - expect(')'); - - marker.end(); - marker.applyGroup(expr); - - return expr; - } - - function trackLeftHandSideExpression() { - var marker, expr; - - skipComment(); - marker = createLocationMarker(); - - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - - while (match('.') || match('[')) { - if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - marker.end(); - marker.apply(expr); - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; - marker.end(); - marker.apply(expr); - } - } - - return expr; - } - - function trackLeftHandSideExpressionAllowCall() { - var marker, expr; - - skipComment(); - marker = createLocationMarker(); - - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - - while (match('.') || match('[') || match('(')) { - if (match('(')) { - expr = { - type: Syntax.CallExpression, - callee: expr, - 'arguments': parseArguments() - }; - marker.end(); - marker.apply(expr); - } else if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - marker.end(); - marker.apply(expr); - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; - marker.end(); - marker.apply(expr); - } - } - - return expr; - } - - function filterGroup(node) { - var n, i, entry; - - n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {}; - for (i in node) { - if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') { - entry = node[i]; - if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) { - n[i] = entry; - } else { - n[i] = filterGroup(entry); - } - } - } - return n; - } - - function wrapTrackingFunction(range, loc) { - - return function (parseFunction) { - - function isBinary(node) { - return node.type === Syntax.LogicalExpression || - node.type === Syntax.BinaryExpression; - } - - function visit(node) { - var start, end; - - if (isBinary(node.left)) { - visit(node.left); - } - if (isBinary(node.right)) { - visit(node.right); - } - - if (range) { - if (node.left.groupRange || node.right.groupRange) { - start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0]; - end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1]; - node.range = [start, end]; - } else if (typeof node.range === 'undefined') { - start = node.left.range[0]; - end = node.right.range[1]; - node.range = [start, end]; - } - } - if (loc) { - if (node.left.groupLoc || node.right.groupLoc) { - start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start; - end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end; - node.loc = { - start: start, - end: end - }; - } else if (typeof node.loc === 'undefined') { - node.loc = { - start: node.left.loc.start, - end: node.right.loc.end - }; - } - } - } - - return function () { - var marker, node; - - skipComment(); - - marker = createLocationMarker(); - node = parseFunction.apply(null, arguments); - marker.end(); - - if (range && typeof node.range === 'undefined') { - marker.apply(node); - } - - if (loc && typeof node.loc === 'undefined') { - marker.apply(node); - } - - if (isBinary(node)) { - visit(node); - } - - return node; - }; - }; - } - - function patch() { - - var wrapTracking; - - if (extra.comments) { - extra.skipComment = skipComment; - skipComment = scanComment; - } - - if (extra.raw) { - extra.createLiteral = createLiteral; - createLiteral = createRawLiteral; - } - - if (extra.range || extra.loc) { - - extra.parseGroupExpression = parseGroupExpression; - extra.parseLeftHandSideExpression = parseLeftHandSideExpression; - extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall; - parseGroupExpression = trackGroupExpression; - parseLeftHandSideExpression = trackLeftHandSideExpression; - parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall; - - wrapTracking = wrapTrackingFunction(extra.range, extra.loc); - - extra.parseAdditiveExpression = parseAdditiveExpression; - extra.parseAssignmentExpression = parseAssignmentExpression; - extra.parseBitwiseANDExpression = parseBitwiseANDExpression; - extra.parseBitwiseORExpression = parseBitwiseORExpression; - extra.parseBitwiseXORExpression = parseBitwiseXORExpression; - extra.parseBlock = parseBlock; - extra.parseFunctionSourceElements = parseFunctionSourceElements; - extra.parseCatchClause = parseCatchClause; - extra.parseComputedMember = parseComputedMember; - extra.parseConditionalExpression = parseConditionalExpression; - extra.parseConstLetDeclaration = parseConstLetDeclaration; - extra.parseEqualityExpression = parseEqualityExpression; - extra.parseExpression = parseExpression; - extra.parseForVariableDeclaration = parseForVariableDeclaration; - extra.parseFunctionDeclaration = parseFunctionDeclaration; - extra.parseFunctionExpression = parseFunctionExpression; - extra.parseLogicalANDExpression = parseLogicalANDExpression; - extra.parseLogicalORExpression = parseLogicalORExpression; - extra.parseMultiplicativeExpression = parseMultiplicativeExpression; - extra.parseNewExpression = parseNewExpression; - extra.parseNonComputedProperty = parseNonComputedProperty; - extra.parseObjectProperty = parseObjectProperty; - extra.parseObjectPropertyKey = parseObjectPropertyKey; - extra.parsePostfixExpression = parsePostfixExpression; - extra.parsePrimaryExpression = parsePrimaryExpression; - extra.parseProgram = parseProgram; - extra.parsePropertyFunction = parsePropertyFunction; - extra.parseRelationalExpression = parseRelationalExpression; - extra.parseStatement = parseStatement; - extra.parseShiftExpression = parseShiftExpression; - extra.parseSwitchCase = parseSwitchCase; - extra.parseUnaryExpression = parseUnaryExpression; - extra.parseVariableDeclaration = parseVariableDeclaration; - extra.parseVariableIdentifier = parseVariableIdentifier; - - parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression); - parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression); - parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression); - parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression); - parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression); - parseBlock = wrapTracking(extra.parseBlock); - parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements); - parseCatchClause = wrapTracking(extra.parseCatchClause); - parseComputedMember = wrapTracking(extra.parseComputedMember); - parseConditionalExpression = wrapTracking(extra.parseConditionalExpression); - parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration); - parseEqualityExpression = wrapTracking(extra.parseEqualityExpression); - parseExpression = wrapTracking(extra.parseExpression); - parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration); - parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration); - parseFunctionExpression = wrapTracking(extra.parseFunctionExpression); - parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression); - parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression); - parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression); - parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression); - parseNewExpression = wrapTracking(extra.parseNewExpression); - parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty); - parseObjectProperty = wrapTracking(extra.parseObjectProperty); - parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey); - parsePostfixExpression = wrapTracking(extra.parsePostfixExpression); - parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression); - parseProgram = wrapTracking(extra.parseProgram); - parsePropertyFunction = wrapTracking(extra.parsePropertyFunction); - parseRelationalExpression = wrapTracking(extra.parseRelationalExpression); - parseStatement = wrapTracking(extra.parseStatement); - parseShiftExpression = wrapTracking(extra.parseShiftExpression); - parseSwitchCase = wrapTracking(extra.parseSwitchCase); - parseUnaryExpression = wrapTracking(extra.parseUnaryExpression); - parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration); - parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier); - } - - if (typeof extra.tokens !== 'undefined') { - extra.advance = advance; - extra.scanRegExp = scanRegExp; - - advance = collectToken; - scanRegExp = collectRegex; - } - } - - function unpatch() { - if (typeof extra.skipComment === 'function') { - skipComment = extra.skipComment; - } - - if (extra.raw) { - createLiteral = extra.createLiteral; - } - - if (extra.range || extra.loc) { - parseAdditiveExpression = extra.parseAdditiveExpression; - parseAssignmentExpression = extra.parseAssignmentExpression; - parseBitwiseANDExpression = extra.parseBitwiseANDExpression; - parseBitwiseORExpression = extra.parseBitwiseORExpression; - parseBitwiseXORExpression = extra.parseBitwiseXORExpression; - parseBlock = extra.parseBlock; - parseFunctionSourceElements = extra.parseFunctionSourceElements; - parseCatchClause = extra.parseCatchClause; - parseComputedMember = extra.parseComputedMember; - parseConditionalExpression = extra.parseConditionalExpression; - parseConstLetDeclaration = extra.parseConstLetDeclaration; - parseEqualityExpression = extra.parseEqualityExpression; - parseExpression = extra.parseExpression; - parseForVariableDeclaration = extra.parseForVariableDeclaration; - parseFunctionDeclaration = extra.parseFunctionDeclaration; - parseFunctionExpression = extra.parseFunctionExpression; - parseGroupExpression = extra.parseGroupExpression; - parseLeftHandSideExpression = extra.parseLeftHandSideExpression; - parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall; - parseLogicalANDExpression = extra.parseLogicalANDExpression; - parseLogicalORExpression = extra.parseLogicalORExpression; - parseMultiplicativeExpression = extra.parseMultiplicativeExpression; - parseNewExpression = extra.parseNewExpression; - parseNonComputedProperty = extra.parseNonComputedProperty; - parseObjectProperty = extra.parseObjectProperty; - parseObjectPropertyKey = extra.parseObjectPropertyKey; - parsePrimaryExpression = extra.parsePrimaryExpression; - parsePostfixExpression = extra.parsePostfixExpression; - parseProgram = extra.parseProgram; - parsePropertyFunction = extra.parsePropertyFunction; - parseRelationalExpression = extra.parseRelationalExpression; - parseStatement = extra.parseStatement; - parseShiftExpression = extra.parseShiftExpression; - parseSwitchCase = extra.parseSwitchCase; - parseUnaryExpression = extra.parseUnaryExpression; - parseVariableDeclaration = extra.parseVariableDeclaration; - parseVariableIdentifier = extra.parseVariableIdentifier; - } - - if (typeof extra.scanRegExp === 'function') { - advance = extra.advance; - scanRegExp = extra.scanRegExp; - } - } - - function stringToArray(str) { - var length = str.length, - result = [], - i; - for (i = 0; i < length; ++i) { - result[i] = str.charAt(i); - } - return result; - } - - function parse(code, options) { - var program, toString; + function parse(code, options) { + var program, toString; toString = String; if (typeof code !== 'string' && !(code instanceof String)) { code = toString(code); } + delegate = SyntaxTreeDelegate; source = code; index = 0; lineNumber = (source.length > 0) ? 1 : 0; lineStart = 0; length = source.length; - buffer = null; + lookahead = null; state = { allowIn: true, labelSet: {}, inFunctionBody: false, inIteration: false, - inSwitch: false + inSwitch: false, + lastCommentStart: -1 }; extra = {}; if (typeof options !== 'undefined') { extra.range = (typeof options.range === 'boolean') && options.range; extra.loc = (typeof options.loc === 'boolean') && options.loc; - extra.raw = (typeof options.raw === 'boolean') && options.raw; + extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment; + + if (extra.loc && options.source !== null && options.source !== undefined) { + extra.source = toString(options.source); + } + if (typeof options.tokens === 'boolean' && options.tokens) { extra.tokens = []; } @@ -8299,29 +8217,18 @@ parseStatement: true, parseSourceElement: true */ if (typeof options.tolerant === 'boolean' && options.tolerant) { extra.errors = []; } - } - - if (length > 0) { - if (typeof source[0] === 'undefined') { - // Try first to convert to a string. This is good as fast path - // for old IE which understands string indexing for string - // literals only and not for string object. - if (code instanceof String) { - source = code.valueOf(); - } - - // Force accessing the characters via an array. - if (typeof source[0] === 'undefined') { - source = stringToArray(code); - } + if (extra.attachComment) { + extra.range = true; + extra.comments = []; + extra.bottomRightStack = []; + extra.trailingComments = []; + extra.leadingComments = []; } } - patch(); try { program = parseProgram(); if (typeof extra.comments !== 'undefined') { - filterCommentLocation(); program.comments = extra.comments; } if (typeof extra.tokens !== 'undefined') { @@ -8331,25 +8238,24 @@ parseStatement: true, parseSourceElement: true */ if (typeof extra.errors !== 'undefined') { program.errors = extra.errors; } - if (extra.range || extra.loc) { - program.body = filterGroup(program.body); - } } catch (e) { throw e; } finally { - unpatch(); extra = {}; } return program; } - // Sync with package.json. - exports.version = '1.0.4'; + // Sync with *.json manifests. + exports.version = '1.2.2'; + + exports.tokenize = tokenize; exports.parse = parse; // Deep copy. + /* istanbul ignore next */ exports.Syntax = (function () { var name, types = {}; @@ -8373,7 +8279,7 @@ parseStatement: true, parseSourceElement: true */ })); /* vim: set sw=4 ts=4 et tw=80 : */ /** - * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -13587,7 +13493,7 @@ define('source-map/source-map-consumer', function (require, exports, module) { * - sourceRoot: Optional. The URL root from which all sources are relative. * - sourcesContent: Optional. An array of contents of the original source files. * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. + * - file: Optional. The generated file this source map is associated with. * * Here is an example source map, taken from the source map spec[0]: * @@ -13610,13 +13516,17 @@ define('source-map/source-map-consumer', function (require, exports, module) { var version = util.getArg(sourceMap, 'version'); var sources = util.getArg(sourceMap, 'sources'); - var names = util.getArg(sourceMap, 'names'); + // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which + // requires the array) to play nice here. + var names = util.getArg(sourceMap, 'names', []); var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); var mappings = util.getArg(sourceMap, 'mappings'); var file = util.getArg(sourceMap, 'file', null); - if (version !== this._version) { + // Once again, Sass deviates from the spec and supplies the version as a + // string rather than a number, so we use loose equality checking here. + if (version != this._version) { throw new Error('Unsupported version: ' + version); } @@ -13626,36 +13536,11 @@ define('source-map/source-map-consumer', function (require, exports, module) { // #72 and bugzil.la/889492. this._names = ArraySet.fromArray(names, true); this._sources = ArraySet.fromArray(sources, true); + this.sourceRoot = sourceRoot; this.sourcesContent = sourcesContent; + this._mappings = mappings; this.file = file; - - // `this._generatedMappings` and `this._originalMappings` hold the parsed - // mapping coordinates from the source map's "mappings" attribute. Each - // object in the array is of the form - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `this._generatedMappings` is ordered by the generated positions. - // - // `this._originalMappings` is ordered by the original positions. - this._generatedMappings = []; - this._originalMappings = []; - this._parseMappings(mappings, sourceRoot); } /** @@ -13676,9 +13561,9 @@ define('source-map/source-map-consumer', function (require, exports, module) { smc.sourceRoot); smc.file = aSourceMap._file; - smc._generatedMappings = aSourceMap._mappings.slice() + smc.__generatedMappings = aSourceMap._mappings.slice() .sort(util.compareByGeneratedPositions); - smc._originalMappings = aSourceMap._mappings.slice() + smc.__originalMappings = aSourceMap._mappings.slice() .sort(util.compareByOriginalPositions); return smc; @@ -13700,9 +13585,66 @@ define('source-map/source-map-consumer', function (require, exports, module) { } }); + // `__generatedMappings` and `__originalMappings` are arrays that hold the + // parsed mapping coordinates from the source map's "mappings" attribute. They + // are lazily instantiated, accessed via the `_generatedMappings` and + // `_originalMappings` getters respectively, and we only parse the mappings + // and create these arrays once queried for a source location. We jump through + // these hoops because there can be many thousands of mappings, and parsing + // them is expensive, so we only want to do it if we must. + // + // Each object in the arrays is of the form: + // + // { + // generatedLine: The line number in the generated code, + // generatedColumn: The column number in the generated code, + // source: The path to the original source file that generated this + // chunk of code, + // originalLine: The line number in the original source that + // corresponds to this chunk of generated code, + // originalColumn: The column number in the original source that + // corresponds to this chunk of generated code, + // name: The name of the original symbol which generated this chunk of + // code. + // } + // + // All properties except for `generatedLine` and `generatedColumn` can be + // `null`. + // + // `_generatedMappings` is ordered by the generated positions. + // + // `_originalMappings` is ordered by the original positions. + + SourceMapConsumer.prototype.__generatedMappings = null; + Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { + get: function () { + if (!this.__generatedMappings) { + this.__generatedMappings = []; + this.__originalMappings = []; + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__generatedMappings; + } + }); + + SourceMapConsumer.prototype.__originalMappings = null; + Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { + get: function () { + if (!this.__originalMappings) { + this.__generatedMappings = []; + this.__originalMappings = []; + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__originalMappings; + } + }); + /** * Parse the mappings in a string in to a data structure which we can easily - * query (an ordered list in this._generatedMappings). + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). */ SourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { @@ -13772,14 +13714,15 @@ define('source-map/source-map-consumer', function (require, exports, module) { } } - this._generatedMappings.push(mapping); + this.__generatedMappings.push(mapping); if (typeof mapping.originalLine === 'number') { - this._originalMappings.push(mapping); + this.__originalMappings.push(mapping); } } } - this._originalMappings.sort(util.compareByOriginalPositions); + this.__generatedMappings.sort(util.compareByGeneratedPositions); + this.__originalMappings.sort(util.compareByOriginalPositions); }; /** @@ -13834,7 +13777,7 @@ define('source-map/source-map-consumer', function (require, exports, module) { "generatedColumn", util.compareByGeneratedPositions); - if (mapping) { + if (mapping && mapping.generatedLine === needle.generatedLine) { var source = util.getArg(mapping, 'source', null); if (source && this.sourceRoot) { source = util.join(this.sourceRoot, source); @@ -14012,14 +13955,17 @@ define('source-map/source-map-generator', function (require, exports, module) { /** * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: + * being built incrementally. You may pass an object with the following + * properties: * * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. + * - sourceRoot: A root for all relative URLs in this source map. */ function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); + if (!aArgs) { + aArgs = {}; + } + this._file = util.getArg(aArgs, 'file', null); this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); this._sources = new ArraySet(); this._names = new ArraySet(); @@ -14149,11 +14095,23 @@ define('source-map/source-map-generator', function (require, exports, module) { * @param aSourceMapConsumer The source map to be applied. * @param aSourceFile Optional. The filename of the source file. * If omitted, SourceMapConsumer's file property will be used. + * @param aSourceMapPath Optional. The dirname of the path to the source map + * to be applied. If relative, it is relative to the SourceMapConsumer. + * This parameter is needed when the two source maps aren't in the same + * directory, and the source map to be applied contains relative source + * paths. If so, those relative source paths need to be rewritten + * relative to the SourceMapGenerator. */ SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { + function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) { // If aSourceFile is omitted, we will use the file property of the SourceMap if (!aSourceFile) { + if (!aSourceMapConsumer.file) { + throw new Error( + 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' + + 'or the source map\'s "file" property. Both were omitted.' + ); + } aSourceFile = aSourceMapConsumer.file; } var sourceRoot = this._sourceRoot; @@ -14176,10 +14134,12 @@ define('source-map/source-map-generator', function (require, exports, module) { }); if (original.source !== null) { // Copy mapping + mapping.source = original.source; + if (aSourceMapPath) { + mapping.source = util.join(aSourceMapPath, mapping.source) + } if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; + mapping.source = util.relative(sourceRoot, mapping.source); } mapping.originalLine = original.line; mapping.originalColumn = original.column; @@ -14249,7 +14209,7 @@ define('source-map/source-map-generator', function (require, exports, module) { throw new Error('Invalid mapping: ' + JSON.stringify({ generated: aGenerated, source: aSource, - orginal: aOriginal, + original: aOriginal, name: aName })); } @@ -14434,41 +14394,16 @@ define('source-map/source-node', function (require, exports, module) { var lastMapping = null; aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { + if (lastMapping !== null) { // We add the code from "lastMapping" to "mapping": // First check if there is a new line in between. if (lastGeneratedLine < mapping.generatedLine) { var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); + // Associate first line with "lastMapping" + addMappingWithCode(lastMapping, remainingLines.shift() + "\n"); + lastGeneratedLine++; + lastGeneratedColumn = 0; + // The remaining code is added without mapping } else { // There is no new line in between. // Associate the code between "lastGeneratedColumn" and @@ -14480,14 +14415,37 @@ define('source-map/source-node', function (require, exports, module) { lastGeneratedColumn); lastGeneratedColumn = mapping.generatedColumn; addMappingWithCode(lastMapping, code); + // No more remaining code, continue + lastMapping = mapping; + return; } } + // We add the generated code until the first mapping + // to the SourceNode without any mapping. + // Each line is added as separate string. + while (lastGeneratedLine < mapping.generatedLine) { + node.add(remainingLines.shift() + "\n"); + lastGeneratedLine++; + } + if (lastGeneratedColumn < mapping.generatedColumn) { + var nextLine = remainingLines[0]; + node.add(nextLine.substr(0, mapping.generatedColumn)); + remainingLines[0] = nextLine.substr(mapping.generatedColumn); + lastGeneratedColumn = mapping.generatedColumn; + } lastMapping = mapping; }, this); // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); + if (remainingLines.length > 0) { + if (lastMapping) { + // Associate the remaining code in the current line with "lastMapping" + var lastLine = remainingLines.shift(); + if (remainingLines.length > 0) lastLine += "\n"; + addMappingWithCode(lastMapping, lastLine); + } + // and add the remaining lines without any mapping + node.add(remainingLines.join("\n")); + } // Copy sourcesContent into SourceNode aSourceMapConsumer.sources.forEach(function (sourceFile) { @@ -14725,10 +14683,28 @@ define('source-map/source-node', function (require, exports, module) { lastOriginalSource = null; sourceMappingActive = false; } - chunk.split('').forEach(function (ch) { + chunk.split('').forEach(function (ch, idx, array) { if (ch === '\n') { generated.line++; generated.column = 0; + // Mappings end at eol + if (idx + 1 === array.length) { + lastOriginalSource = null; + sourceMappingActive = false; + } else if (sourceMappingActive) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } } else { generated.column++; } @@ -14774,8 +14750,8 @@ define('source-map/util', function (require, exports, module) { } exports.getArg = getArg; - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; + var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; + var dataUrlRegexp = /^data:.+\,.+$/; function urlParse(aUrl) { var match = aUrl.match(urlRegexp); @@ -14784,18 +14760,22 @@ define('source-map/util', function (require, exports, module) { } return { scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] + auth: match[2], + host: match[3], + port: match[4], + path: match[5] }; } exports.urlParse = urlParse; function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; + var url = ''; + if (aParsedUrl.scheme) { + url += aParsedUrl.scheme + ':'; + } + url += '//'; if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" + url += aParsedUrl.auth + '@'; } if (aParsedUrl.host) { url += aParsedUrl.host; @@ -14810,19 +14790,112 @@ define('source-map/util', function (require, exports, module) { } exports.urlGenerate = urlGenerate; + /** + * Normalizes a path, or the path portion of a URL: + * + * - Replaces consequtive slashes with one slash. + * - Removes unnecessary '.' parts. + * - Removes unnecessary '/..' parts. + * + * Based on code in the Node.js 'path' core module. + * + * @param aPath The path or url to normalize. + */ + function normalize(aPath) { + var path = aPath; + var url = urlParse(aPath); + if (url) { + if (!url.path) { + return aPath; + } + path = url.path; + } + var isAbsolute = (path.charAt(0) === '/'); + + var parts = path.split(/\/+/); + for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { + part = parts[i]; + if (part === '.') { + parts.splice(i, 1); + } else if (part === '..') { + up++; + } else if (up > 0) { + if (part === '') { + // The first part is blank if the path is absolute. Trying to go + // above the root is a no-op. Therefore we can remove all '..' parts + // directly after the root. + parts.splice(i + 1, up); + up = 0; + } else { + parts.splice(i, 2); + up--; + } + } + } + path = parts.join('/'); + + if (path === '') { + path = isAbsolute ? '/' : '.'; + } + + if (url) { + url.path = path; + return urlGenerate(url); + } + return path; + } + exports.normalize = normalize; + + /** + * Joins two paths/URLs. + * + * @param aRoot The root path or URL. + * @param aPath The path or URL to be joined with the root. + * + * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a + * scheme-relative URL: Then the scheme of aRoot, if any, is prepended + * first. + * - Otherwise aPath is a path. If aRoot is a URL, then its path portion + * is updated with the result and aRoot is returned. Otherwise the result + * is returned. + * - If aPath is absolute, the result is aPath. + * - Otherwise the two paths are joined with a slash. + * - Joining for example 'http://' and 'www.example.com' is also supported. + */ function join(aRoot, aPath) { - var url; + var aPathUrl = urlParse(aPath); + var aRootUrl = urlParse(aRoot); + if (aRootUrl) { + aRoot = aRootUrl.path || '/'; + } + + // `join(foo, '//www.example.org')` + if (aPathUrl && !aPathUrl.scheme) { + if (aRootUrl) { + aPathUrl.scheme = aRootUrl.scheme; + } + return urlGenerate(aPathUrl); + } - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { + if (aPathUrl || aPath.match(dataUrlRegexp)) { return aPath; } - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); + // `join('http://', 'www.example.com')` + if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { + aRootUrl.host = aPath; + return urlGenerate(aRootUrl); } - return aRoot.replace(/\/$/, '') + '/' + aPath; + var joined = aPath.charAt(0) === '/' + ? aPath + : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); + + if (aRootUrl) { + aRootUrl.path = joined; + return urlGenerate(aRootUrl); + } + return joined; } exports.join = join; @@ -14963,4713 +15036,5926 @@ exports.SourceNode = require('./source-map/source-node').SourceNode; //Distributed under the BSD license: //Copyright 2012 (c) Mihai Bazon define('uglifyjs2', ['exports', 'source-map', 'logger', 'env!env/file'], function (exports, MOZ_SourceMap, logger, rjsFile) { -(function(exports, global) { - global["UglifyJS"] = exports; - "use strict"; - function array_to_hash(a) { - var ret = Object.create(null); - for (var i = 0; i < a.length; ++i) ret[a[i]] = true; - return ret; - } - function slice(a, start) { - return Array.prototype.slice.call(a, start || 0); - } - function characters(str) { - return str.split(""); - } - function member(name, array) { - for (var i = array.length; --i >= 0; ) if (array[i] == name) return true; - return false; - } - function find_if(func, array) { - for (var i = 0, n = array.length; i < n; ++i) { - if (func(array[i])) return array[i]; - } - } - function repeat_string(str, i) { - if (i <= 0) return ""; - if (i == 1) return str; - var d = repeat_string(str, i >> 1); - d += d; - if (i & 1) d += str; - return d; - } - function DefaultsError(msg, defs) { - this.msg = msg; - this.defs = defs; - } - function defaults(args, defs, croak) { - if (args === true) args = {}; - var ret = args || {}; - if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i)) throw new DefaultsError("`" + i + "` is not a supported option", defs); - for (var i in defs) if (defs.hasOwnProperty(i)) { - ret[i] = args && args.hasOwnProperty(i) ? args[i] : defs[i]; - } - return ret; - } - function merge(obj, ext) { - for (var i in ext) if (ext.hasOwnProperty(i)) { - obj[i] = ext[i]; - } - return obj; - } - function noop() {} - var MAP = function() { - function MAP(a, f, backwards) { - var ret = [], top = [], i; - function doit() { - var val = f(a[i], i); - var is_last = val instanceof Last; - if (is_last) val = val.v; - if (val instanceof AtTop) { - val = val.v; - if (val instanceof Splice) { - top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); - } else { - top.push(val); - } - } else if (val !== skip) { - if (val instanceof Splice) { - ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); - } else { - ret.push(val); - } - } - return is_last; - } - if (a instanceof Array) { - if (backwards) { - for (i = a.length; --i >= 0; ) if (doit()) break; - ret.reverse(); - top.reverse(); - } else { - for (i = 0; i < a.length; ++i) if (doit()) break; - } - } else { - for (i in a) if (a.hasOwnProperty(i)) if (doit()) break; - } - return top.concat(ret); - } - MAP.at_top = function(val) { - return new AtTop(val); - }; - MAP.splice = function(val) { - return new Splice(val); - }; - MAP.last = function(val) { - return new Last(val); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. + + ***********************************************************************/ + +"use strict"; + +function array_to_hash(a) { + var ret = Object.create(null); + for (var i = 0; i < a.length; ++i) + ret[a[i]] = true; + return ret; +}; + +function slice(a, start) { + return Array.prototype.slice.call(a, start || 0); +}; + +function characters(str) { + return str.split(""); +}; + +function member(name, array) { + for (var i = array.length; --i >= 0;) + if (array[i] == name) + return true; + return false; +}; + +function find_if(func, array) { + for (var i = 0, n = array.length; i < n; ++i) { + if (func(array[i])) + return array[i]; + } +}; + +function repeat_string(str, i) { + if (i <= 0) return ""; + if (i == 1) return str; + var d = repeat_string(str, i >> 1); + d += d; + if (i & 1) d += str; + return d; +}; + +function DefaultsError(msg, defs) { + Error.call(this, msg); + this.msg = msg; + this.defs = defs; +}; +DefaultsError.prototype = Object.create(Error.prototype); +DefaultsError.prototype.constructor = DefaultsError; + +DefaultsError.croak = function(msg, defs) { + throw new DefaultsError(msg, defs); +}; + +function defaults(args, defs, croak) { + if (args === true) + args = {}; + var ret = args || {}; + if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i)) + DefaultsError.croak("`" + i + "` is not a supported option", defs); + for (var i in defs) if (defs.hasOwnProperty(i)) { + ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i]; + } + return ret; +}; + +function merge(obj, ext) { + for (var i in ext) if (ext.hasOwnProperty(i)) { + obj[i] = ext[i]; + } + return obj; +}; + +function noop() {}; + +var MAP = (function(){ + function MAP(a, f, backwards) { + var ret = [], top = [], i; + function doit() { + var val = f(a[i], i); + var is_last = val instanceof Last; + if (is_last) val = val.v; + if (val instanceof AtTop) { + val = val.v; + if (val instanceof Splice) { + top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); + } else { + top.push(val); + } + } + else if (val !== skip) { + if (val instanceof Splice) { + ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); + } else { + ret.push(val); + } + } + return is_last; }; - var skip = MAP.skip = {}; - function AtTop(val) { - this.v = val; - } - function Splice(val) { - this.v = val; + if (a instanceof Array) { + if (backwards) { + for (i = a.length; --i >= 0;) if (doit()) break; + ret.reverse(); + top.reverse(); + } else { + for (i = 0; i < a.length; ++i) if (doit()) break; + } } - function Last(val) { - this.v = val; + else { + for (i in a) if (a.hasOwnProperty(i)) if (doit()) break; } - return MAP; - }(); - function push_uniq(array, el) { - if (array.indexOf(el) < 0) array.push(el); + return top.concat(ret); + }; + MAP.at_top = function(val) { return new AtTop(val) }; + MAP.splice = function(val) { return new Splice(val) }; + MAP.last = function(val) { return new Last(val) }; + var skip = MAP.skip = {}; + function AtTop(val) { this.v = val }; + function Splice(val) { this.v = val }; + function Last(val) { this.v = val }; + return MAP; +})(); + +function push_uniq(array, el) { + if (array.indexOf(el) < 0) + array.push(el); +}; + +function string_template(text, props) { + return text.replace(/\{(.+?)\}/g, function(str, p){ + return props[p]; + }); +}; + +function remove(array, el) { + for (var i = array.length; --i >= 0;) { + if (array[i] === el) array.splice(i, 1); } - function string_template(text, props) { - return text.replace(/\{(.+?)\}/g, function(str, p) { - return props[p]; - }); +}; + +function mergeSort(array, cmp) { + if (array.length < 2) return array.slice(); + function merge(a, b) { + var r = [], ai = 0, bi = 0, i = 0; + while (ai < a.length && bi < b.length) { + cmp(a[ai], b[bi]) <= 0 + ? r[i++] = a[ai++] + : r[i++] = b[bi++]; + } + if (ai < a.length) r.push.apply(r, a.slice(ai)); + if (bi < b.length) r.push.apply(r, b.slice(bi)); + return r; + }; + function _ms(a) { + if (a.length <= 1) + return a; + var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m); + left = _ms(left); + right = _ms(right); + return merge(left, right); + }; + return _ms(array); +}; + +function set_difference(a, b) { + return a.filter(function(el){ + return b.indexOf(el) < 0; + }); +}; + +function set_intersection(a, b) { + return a.filter(function(el){ + return b.indexOf(el) >= 0; + }); +}; + +// this function is taken from Acorn [1], written by Marijn Haverbeke +// [1] https://github.com/marijnh/acorn +function makePredicate(words) { + if (!(words instanceof Array)) words = words.split(" "); + var f = "", cats = []; + out: for (var i = 0; i < words.length; ++i) { + for (var j = 0; j < cats.length; ++j) + if (cats[j][0].length == words[i].length) { + cats[j].push(words[i]); + continue out; + } + cats.push([words[i]]); + } + function compareTo(arr) { + if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; + f += "switch(str){"; + for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; + f += "return true}return false;"; + } + // When there are more than three length categories, an outer + // switch first dispatches on the lengths, to save on comparisons. + if (cats.length > 3) { + cats.sort(function(a, b) {return b.length - a.length;}); + f += "switch(str.length){"; + for (var i = 0; i < cats.length; ++i) { + var cat = cats[i]; + f += "case " + cat[0].length + ":"; + compareTo(cat); + } + f += "}"; + // Otherwise, simply generate a flat `switch` statement. + } else { + compareTo(words); } - function remove(array, el) { - for (var i = array.length; --i >= 0; ) { - if (array[i] === el) array.splice(i, 1); + return new Function("str", f); +}; + +function all(array, predicate) { + for (var i = array.length; --i >= 0;) + if (!predicate(array[i])) + return false; + return true; +}; + +function Dictionary() { + this._values = Object.create(null); + this._size = 0; +}; +Dictionary.prototype = { + set: function(key, val) { + if (!this.has(key)) ++this._size; + this._values["$" + key] = val; + return this; + }, + add: function(key, val) { + if (this.has(key)) { + this.get(key).push(val); + } else { + this.set(key, [ val ]); } - } - function mergeSort(array, cmp) { - if (array.length < 2) return array.slice(); - function merge(a, b) { - var r = [], ai = 0, bi = 0, i = 0; - while (ai < a.length && bi < b.length) { - cmp(a[ai], b[bi]) <= 0 ? r[i++] = a[ai++] : r[i++] = b[bi++]; - } - if (ai < a.length) r.push.apply(r, a.slice(ai)); - if (bi < b.length) r.push.apply(r, b.slice(bi)); - return r; + return this; + }, + get: function(key) { return this._values["$" + key] }, + del: function(key) { + if (this.has(key)) { + --this._size; + delete this._values["$" + key]; } - function _ms(a) { - if (a.length <= 1) return a; - var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m); - left = _ms(left); - right = _ms(right); - return merge(left, right); + return this; + }, + has: function(key) { return ("$" + key) in this._values }, + each: function(f) { + for (var i in this._values) + f(this._values[i], i.substr(1)); + }, + size: function() { + return this._size; + }, + map: function(f) { + var ret = []; + for (var i in this._values) + ret.push(f(this._values[i], i.substr(1))); + return ret; + } +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. + + ***********************************************************************/ + +"use strict"; + +function DEFNODE(type, props, methods, base) { + if (arguments.length < 4) base = AST_Node; + if (!props) props = []; + else props = props.split(/\s+/); + var self_props = props; + if (base && base.PROPS) + props = props.concat(base.PROPS); + var code = "return function AST_" + type + "(props){ if (props) { "; + for (var i = props.length; --i >= 0;) { + code += "this." + props[i] + " = props." + props[i] + ";"; + } + var proto = base && new base; + if (proto && proto.initialize || (methods && methods.initialize)) + code += "this.initialize();"; + code += "}}"; + var ctor = new Function(code)(); + if (proto) { + ctor.prototype = proto; + ctor.BASE = base; + } + if (base) base.SUBCLASSES.push(ctor); + ctor.prototype.CTOR = ctor; + ctor.PROPS = props || null; + ctor.SELF_PROPS = self_props; + ctor.SUBCLASSES = []; + if (type) { + ctor.prototype.TYPE = ctor.TYPE = type; + } + if (methods) for (i in methods) if (methods.hasOwnProperty(i)) { + if (/^\$/.test(i)) { + ctor[i.substr(1)] = methods[i]; + } else { + ctor.prototype[i] = methods[i]; } - return _ms(array); } - function set_difference(a, b) { - return a.filter(function(el) { - return b.indexOf(el) < 0; + ctor.DEFMETHOD = function(name, method) { + this.prototype[name] = method; + }; + return ctor; +}; + +var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", { +}, null); + +var AST_Node = DEFNODE("Node", "start end", { + clone: function() { + return new this.CTOR(this); + }, + $documentation: "Base class of all AST nodes", + $propdoc: { + start: "[AST_Token] The first token of this node", + end: "[AST_Token] The last token of this node" + }, + _walk: function(visitor) { + return visitor._visit(this); + }, + walk: function(visitor) { + return this._walk(visitor); // not sure the indirection will be any help + } +}, null); + +AST_Node.warn_function = null; +AST_Node.warn = function(txt, props) { + if (AST_Node.warn_function) + AST_Node.warn_function(string_template(txt, props)); +}; + +/* -----[ statements ]----- */ + +var AST_Statement = DEFNODE("Statement", null, { + $documentation: "Base class of all statements", +}); + +var AST_Debugger = DEFNODE("Debugger", null, { + $documentation: "Represents a debugger statement", +}, AST_Statement); + +var AST_Directive = DEFNODE("Directive", "value scope", { + $documentation: "Represents a directive, like \"use strict\";", + $propdoc: { + value: "[string] The value of this directive as a plain string (it's not an AST_String!)", + scope: "[AST_Scope/S] The scope that this directive affects" + }, +}, AST_Statement); + +var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { + $documentation: "A statement consisting of an expression, i.e. a = 1 + 2", + $propdoc: { + body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); + }); + } +}, AST_Statement); + +function walk_body(node, visitor) { + if (node.body instanceof AST_Statement) { + node.body._walk(visitor); + } + else node.body.forEach(function(stat){ + stat._walk(visitor); + }); +}; + +var AST_Block = DEFNODE("Block", "body", { + $documentation: "A body of statements (usually bracketed)", + $propdoc: { + body: "[AST_Statement*] an array of statements" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + walk_body(this, visitor); }); } - function set_intersection(a, b) { - return a.filter(function(el) { - return b.indexOf(el) >= 0; +}, AST_Statement); + +var AST_BlockStatement = DEFNODE("BlockStatement", null, { + $documentation: "A block statement", +}, AST_Block); + +var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { + $documentation: "The empty statement (empty block or simply a semicolon)", + _walk: function(visitor) { + return visitor._visit(this); + } +}, AST_Statement); + +var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { + $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", + $propdoc: { + body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); }); } - function makePredicate(words) { - if (!(words instanceof Array)) words = words.split(" "); - var f = "", cats = []; - out: for (var i = 0; i < words.length; ++i) { - for (var j = 0; j < cats.length; ++j) if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; +}, AST_Statement); + +var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { + $documentation: "Statement with a label", + $propdoc: { + label: "[AST_Label] a label definition" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.label._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_StatementWithBody); + +var AST_IterationStatement = DEFNODE("IterationStatement", null, { + $documentation: "Internal class. All loops inherit from it." +}, AST_StatementWithBody); + +var AST_DWLoop = DEFNODE("DWLoop", "condition", { + $documentation: "Base class for do/while statements", + $propdoc: { + condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.condition._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_IterationStatement); + +var AST_Do = DEFNODE("Do", null, { + $documentation: "A `do` statement", +}, AST_DWLoop); + +var AST_While = DEFNODE("While", null, { + $documentation: "A `while` statement", +}, AST_DWLoop); + +var AST_For = DEFNODE("For", "init condition step", { + $documentation: "A `for` statement", + $propdoc: { + init: "[AST_Node?] the `for` initialization code, or null if empty", + condition: "[AST_Node?] the `for` termination clause, or null if empty", + step: "[AST_Node?] the `for` update clause, or null if empty" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + if (this.init) this.init._walk(visitor); + if (this.condition) this.condition._walk(visitor); + if (this.step) this.step._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_IterationStatement); + +var AST_ForIn = DEFNODE("ForIn", "init name object", { + $documentation: "A `for ... in` statement", + $propdoc: { + init: "[AST_Node] the `for/in` initialization code", + name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var", + object: "[AST_Node] the object that we're looping through" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.init._walk(visitor); + this.object._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_IterationStatement); + +var AST_With = DEFNODE("With", "expression", { + $documentation: "A `with` statement", + $propdoc: { + expression: "[AST_Node] the `with` expression" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_StatementWithBody); + +/* -----[ scope and functions ]----- */ + +var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", { + $documentation: "Base class for all statements introducing a lexical scope", + $propdoc: { + directives: "[string*/S] an array of directives declared in this scope", + variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope", + functions: "[Object/S] like `variables`, but only lists function declarations", + uses_with: "[boolean/S] tells whether this scope uses the `with` statement", + uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", + parent_scope: "[AST_Scope?/S] link to the parent scope", + enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", + cname: "[integer/S] current index for mangling variables (used internally by the mangler)", + }, +}, AST_Block); + +var AST_Toplevel = DEFNODE("Toplevel", "globals", { + $documentation: "The toplevel scope", + $propdoc: { + globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", + }, + wrap_enclose: function(arg_parameter_pairs) { + var self = this; + var args = []; + var parameters = []; + + arg_parameter_pairs.forEach(function(pair) { + var split = pair.split(":"); + + args.push(split[0]); + parameters.push(split[1]); + }); + + var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")"; + wrapped_tl = parse(wrapped_tl); + wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ + if (node instanceof AST_Directive && node.value == "$ORIG") { + return MAP.splice(self.body); + } + })); + return wrapped_tl; + }, + wrap_commonjs: function(name, export_all) { + var self = this; + var to_export = []; + if (export_all) { + self.figure_out_scope(); + self.walk(new TreeWalker(function(node){ + if (node instanceof AST_SymbolDeclaration && node.definition().global) { + if (!find_if(function(n){ return n.name == node.name }, to_export)) + to_export.push(node); + } + })); + } + var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))"; + wrapped_tl = parse(wrapped_tl); + wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ + if (node instanceof AST_SimpleStatement) { + node = node.body; + if (node instanceof AST_String) switch (node.getValue()) { + case "$ORIG": + return MAP.splice(self.body); + case "$EXPORTS": + var body = []; + to_export.forEach(function(sym){ + body.push(new AST_SimpleStatement({ + body: new AST_Assign({ + left: new AST_Sub({ + expression: new AST_SymbolRef({ name: "exports" }), + property: new AST_String({ value: sym.name }), + }), + operator: "=", + right: new AST_SymbolRef(sym), + }), + })); + }); + return MAP.splice(body); + } + } + })); + return wrapped_tl; + } +}, AST_Scope); + +var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { + $documentation: "Base class for functions", + $propdoc: { + name: "[AST_SymbolDeclaration?] the name of this function", + argnames: "[AST_SymbolFunarg*] array of function arguments", + uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + if (this.name) this.name._walk(visitor); + this.argnames.forEach(function(arg){ + arg._walk(visitor); + }); + walk_body(this, visitor); + }); + } +}, AST_Scope); + +var AST_Accessor = DEFNODE("Accessor", null, { + $documentation: "A setter/getter function. The `name` property is always null." +}, AST_Lambda); + +var AST_Function = DEFNODE("Function", null, { + $documentation: "A function expression" +}, AST_Lambda); + +var AST_Defun = DEFNODE("Defun", null, { + $documentation: "A function definition" +}, AST_Lambda); + +/* -----[ JUMPS ]----- */ + +var AST_Jump = DEFNODE("Jump", null, { + $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" +}, AST_Statement); + +var AST_Exit = DEFNODE("Exit", "value", { + $documentation: "Base class for “exits” (`return` and `throw`)", + $propdoc: { + value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" + }, + _walk: function(visitor) { + return visitor._visit(this, this.value && function(){ + this.value._walk(visitor); + }); + } +}, AST_Jump); + +var AST_Return = DEFNODE("Return", null, { + $documentation: "A `return` statement" +}, AST_Exit); + +var AST_Throw = DEFNODE("Throw", null, { + $documentation: "A `throw` statement" +}, AST_Exit); + +var AST_LoopControl = DEFNODE("LoopControl", "label", { + $documentation: "Base class for loop control statements (`break` and `continue`)", + $propdoc: { + label: "[AST_LabelRef?] the label, or null if none", + }, + _walk: function(visitor) { + return visitor._visit(this, this.label && function(){ + this.label._walk(visitor); + }); + } +}, AST_Jump); + +var AST_Break = DEFNODE("Break", null, { + $documentation: "A `break` statement" +}, AST_LoopControl); + +var AST_Continue = DEFNODE("Continue", null, { + $documentation: "A `continue` statement" +}, AST_LoopControl); + +/* -----[ IF ]----- */ + +var AST_If = DEFNODE("If", "condition alternative", { + $documentation: "A `if` statement", + $propdoc: { + condition: "[AST_Node] the `if` condition", + alternative: "[AST_Statement?] the `else` part, or null if not present" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.condition._walk(visitor); + this.body._walk(visitor); + if (this.alternative) this.alternative._walk(visitor); + }); + } +}, AST_StatementWithBody); + +/* -----[ SWITCH ]----- */ + +var AST_Switch = DEFNODE("Switch", "expression", { + $documentation: "A `switch` statement", + $propdoc: { + expression: "[AST_Node] the `switch` “discriminant”" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + walk_body(this, visitor); + }); + } +}, AST_Block); + +var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { + $documentation: "Base class for `switch` branches", +}, AST_Block); + +var AST_Default = DEFNODE("Default", null, { + $documentation: "A `default` switch branch", +}, AST_SwitchBranch); + +var AST_Case = DEFNODE("Case", "expression", { + $documentation: "A `case` switch branch", + $propdoc: { + expression: "[AST_Node] the `case` expression" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + walk_body(this, visitor); + }); + } +}, AST_SwitchBranch); + +/* -----[ EXCEPTIONS ]----- */ + +var AST_Try = DEFNODE("Try", "bcatch bfinally", { + $documentation: "A `try` statement", + $propdoc: { + bcatch: "[AST_Catch?] the catch block, or null if not present", + bfinally: "[AST_Finally?] the finally block, or null if not present" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + walk_body(this, visitor); + if (this.bcatch) this.bcatch._walk(visitor); + if (this.bfinally) this.bfinally._walk(visitor); + }); + } +}, AST_Block); + +var AST_Catch = DEFNODE("Catch", "argname", { + $documentation: "A `catch` node; only makes sense as part of a `try` statement", + $propdoc: { + argname: "[AST_SymbolCatch] symbol for the exception" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.argname._walk(visitor); + walk_body(this, visitor); + }); + } +}, AST_Block); + +var AST_Finally = DEFNODE("Finally", null, { + $documentation: "A `finally` node; only makes sense as part of a `try` statement" +}, AST_Block); + +/* -----[ VAR/CONST ]----- */ + +var AST_Definitions = DEFNODE("Definitions", "definitions", { + $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", + $propdoc: { + definitions: "[AST_VarDef*] array of variable definitions" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.definitions.forEach(function(def){ + def._walk(visitor); + }); + }); + } +}, AST_Statement); + +var AST_Var = DEFNODE("Var", null, { + $documentation: "A `var` statement" +}, AST_Definitions); + +var AST_Const = DEFNODE("Const", null, { + $documentation: "A `const` statement" +}, AST_Definitions); + +var AST_VarDef = DEFNODE("VarDef", "name value", { + $documentation: "A variable declaration; only appears in a AST_Definitions node", + $propdoc: { + name: "[AST_SymbolVar|AST_SymbolConst] name of the variable", + value: "[AST_Node?] initializer, or null of there's no initializer" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.name._walk(visitor); + if (this.value) this.value._walk(visitor); + }); + } +}); + +/* -----[ OTHER ]----- */ + +var AST_Call = DEFNODE("Call", "expression args", { + $documentation: "A function call expression", + $propdoc: { + expression: "[AST_Node] expression to invoke as function", + args: "[AST_Node*] array of arguments" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + this.args.forEach(function(arg){ + arg._walk(visitor); + }); + }); + } +}); + +var AST_New = DEFNODE("New", null, { + $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" +}, AST_Call); + +var AST_Seq = DEFNODE("Seq", "car cdr", { + $documentation: "A sequence expression (two comma-separated expressions)", + $propdoc: { + car: "[AST_Node] first element in sequence", + cdr: "[AST_Node] second element in sequence" + }, + $cons: function(x, y) { + var seq = new AST_Seq(x); + seq.car = x; + seq.cdr = y; + return seq; + }, + $from_array: function(array) { + if (array.length == 0) return null; + if (array.length == 1) return array[0].clone(); + var list = null; + for (var i = array.length; --i >= 0;) { + list = AST_Seq.cons(array[i], list); + } + var p = list; + while (p) { + if (p.cdr && !p.cdr.cdr) { + p.cdr = p.cdr.car; + break; + } + p = p.cdr; + } + return list; + }, + to_array: function() { + var p = this, a = []; + while (p) { + a.push(p.car); + if (p.cdr && !(p.cdr instanceof AST_Seq)) { + a.push(p.cdr); + break; + } + p = p.cdr; + } + return a; + }, + add: function(node) { + var p = this; + while (p) { + if (!(p.cdr instanceof AST_Seq)) { + var cell = AST_Seq.cons(p.cdr, node); + return p.cdr = cell; + } + p = p.cdr; + } + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.car._walk(visitor); + if (this.cdr) this.cdr._walk(visitor); + }); + } +}); + +var AST_PropAccess = DEFNODE("PropAccess", "expression property", { + $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`", + $propdoc: { + expression: "[AST_Node] the “container” expression", + property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" + } +}); + +var AST_Dot = DEFNODE("Dot", null, { + $documentation: "A dotted property access expression", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + }); + } +}, AST_PropAccess); + +var AST_Sub = DEFNODE("Sub", null, { + $documentation: "Index-style property access, i.e. `a[\"foo\"]`", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + this.property._walk(visitor); + }); + } +}, AST_PropAccess); + +var AST_Unary = DEFNODE("Unary", "operator expression", { + $documentation: "Base class for unary expressions", + $propdoc: { + operator: "[string] the operator", + expression: "[AST_Node] expression that this unary operator applies to" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + }); + } +}); + +var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { + $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" +}, AST_Unary); + +var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { + $documentation: "Unary postfix expression, i.e. `i++`" +}, AST_Unary); + +var AST_Binary = DEFNODE("Binary", "left operator right", { + $documentation: "Binary expression, i.e. `a + b`", + $propdoc: { + left: "[AST_Node] left-hand side expression", + operator: "[string] the operator", + right: "[AST_Node] right-hand side expression" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.left._walk(visitor); + this.right._walk(visitor); + }); + } +}); + +var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { + $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", + $propdoc: { + condition: "[AST_Node]", + consequent: "[AST_Node]", + alternative: "[AST_Node]" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.condition._walk(visitor); + this.consequent._walk(visitor); + this.alternative._walk(visitor); + }); + } +}); + +var AST_Assign = DEFNODE("Assign", null, { + $documentation: "An assignment expression — `a = b + 5`", +}, AST_Binary); + +/* -----[ LITERALS ]----- */ + +var AST_Array = DEFNODE("Array", "elements", { + $documentation: "An array literal", + $propdoc: { + elements: "[AST_Node*] array of elements" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.elements.forEach(function(el){ + el._walk(visitor); + }); + }); + } +}); + +var AST_Object = DEFNODE("Object", "properties", { + $documentation: "An object literal", + $propdoc: { + properties: "[AST_ObjectProperty*] array of properties" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.properties.forEach(function(prop){ + prop._walk(visitor); + }); + }); + } +}); + +var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { + $documentation: "Base class for literal object properties", + $propdoc: { + key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.", + value: "[AST_Node] property value. For setters and getters this is an AST_Function." + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.value._walk(visitor); + }); + } +}); + +var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, { + $documentation: "A key: value object property", +}, AST_ObjectProperty); + +var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { + $documentation: "An object setter property", +}, AST_ObjectProperty); + +var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { + $documentation: "An object getter property", +}, AST_ObjectProperty); + +var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { + $propdoc: { + name: "[string] name of this symbol", + scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", + thedef: "[SymbolDef/S] the definition of this symbol" + }, + $documentation: "Base class for all symbols", +}); + +var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { + $documentation: "The name of a property accessor (setter/getter function)" +}, AST_Symbol); + +var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { + $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", + $propdoc: { + init: "[AST_Node*/S] array of initializers for this declaration." + } +}, AST_Symbol); + +var AST_SymbolVar = DEFNODE("SymbolVar", null, { + $documentation: "Symbol defining a variable", +}, AST_SymbolDeclaration); + +var AST_SymbolConst = DEFNODE("SymbolConst", null, { + $documentation: "A constant declaration" +}, AST_SymbolDeclaration); + +var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { + $documentation: "Symbol naming a function argument", +}, AST_SymbolVar); + +var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { + $documentation: "Symbol defining a function", +}, AST_SymbolDeclaration); + +var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { + $documentation: "Symbol naming a function expression", +}, AST_SymbolDeclaration); + +var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { + $documentation: "Symbol naming the exception in catch", +}, AST_SymbolDeclaration); + +var AST_Label = DEFNODE("Label", "references", { + $documentation: "Symbol naming a label (declaration)", + $propdoc: { + references: "[AST_LoopControl*] a list of nodes referring to this label" + }, + initialize: function() { + this.references = []; + this.thedef = this; + } +}, AST_Symbol); + +var AST_SymbolRef = DEFNODE("SymbolRef", null, { + $documentation: "Reference to some symbol (not definition/declaration)", +}, AST_Symbol); + +var AST_LabelRef = DEFNODE("LabelRef", null, { + $documentation: "Reference to a label symbol", +}, AST_Symbol); + +var AST_This = DEFNODE("This", null, { + $documentation: "The `this` symbol", +}, AST_Symbol); + +var AST_Constant = DEFNODE("Constant", null, { + $documentation: "Base class for all constants", + getValue: function() { + return this.value; + } +}); + +var AST_String = DEFNODE("String", "value", { + $documentation: "A string literal", + $propdoc: { + value: "[string] the contents of this string" + } +}, AST_Constant); + +var AST_Number = DEFNODE("Number", "value", { + $documentation: "A number literal", + $propdoc: { + value: "[number] the numeric value" + } +}, AST_Constant); + +var AST_RegExp = DEFNODE("RegExp", "value", { + $documentation: "A regexp literal", + $propdoc: { + value: "[RegExp] the actual regexp" + } +}, AST_Constant); + +var AST_Atom = DEFNODE("Atom", null, { + $documentation: "Base class for atoms", +}, AST_Constant); + +var AST_Null = DEFNODE("Null", null, { + $documentation: "The `null` atom", + value: null +}, AST_Atom); + +var AST_NaN = DEFNODE("NaN", null, { + $documentation: "The impossible value", + value: 0/0 +}, AST_Atom); + +var AST_Undefined = DEFNODE("Undefined", null, { + $documentation: "The `undefined` value", + value: (function(){}()) +}, AST_Atom); + +var AST_Hole = DEFNODE("Hole", null, { + $documentation: "A hole in an array", + value: (function(){}()) +}, AST_Atom); + +var AST_Infinity = DEFNODE("Infinity", null, { + $documentation: "The `Infinity` value", + value: 1/0 +}, AST_Atom); + +var AST_Boolean = DEFNODE("Boolean", null, { + $documentation: "Base class for booleans", +}, AST_Atom); + +var AST_False = DEFNODE("False", null, { + $documentation: "The `false` atom", + value: false +}, AST_Boolean); + +var AST_True = DEFNODE("True", null, { + $documentation: "The `true` atom", + value: true +}, AST_Boolean); + +/* -----[ TreeWalker ]----- */ + +function TreeWalker(callback) { + this.visit = callback; + this.stack = []; +}; +TreeWalker.prototype = { + _visit: function(node, descend) { + this.stack.push(node); + var ret = this.visit(node, descend ? function(){ + descend.call(node); + } : noop); + if (!ret && descend) { + descend.call(node); + } + this.stack.pop(); + return ret; + }, + parent: function(n) { + return this.stack[this.stack.length - 2 - (n || 0)]; + }, + push: function (node) { + this.stack.push(node); + }, + pop: function() { + return this.stack.pop(); + }, + self: function() { + return this.stack[this.stack.length - 1]; + }, + find_parent: function(type) { + var stack = this.stack; + for (var i = stack.length; --i >= 0;) { + var x = stack[i]; + if (x instanceof type) return x; + } + }, + has_directive: function(type) { + return this.find_parent(AST_Scope).has_directive(type); + }, + in_boolean_context: function() { + var stack = this.stack; + var i = stack.length, self = stack[--i]; + while (i > 0) { + var p = stack[--i]; + if ((p instanceof AST_If && p.condition === self) || + (p instanceof AST_Conditional && p.condition === self) || + (p instanceof AST_DWLoop && p.condition === self) || + (p instanceof AST_For && p.condition === self) || + (p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self)) + { + return true; + } + if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||"))) + return false; + self = p; + } + }, + loopcontrol_target: function(label) { + var stack = this.stack; + if (label) for (var i = stack.length; --i >= 0;) { + var x = stack[i]; + if (x instanceof AST_LabeledStatement && x.label.name == label.name) { + return x.body; + } + } else for (var i = stack.length; --i >= 0;) { + var x = stack[i]; + if (x instanceof AST_Switch || x instanceof AST_IterationStatement) + return x; + } + } +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/). + + 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. + + ***********************************************************************/ + +"use strict"; + +var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with'; +var KEYWORDS_ATOM = 'false null true'; +var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield' + + " " + KEYWORDS_ATOM + " " + KEYWORDS; +var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; + +KEYWORDS = makePredicate(KEYWORDS); +RESERVED_WORDS = makePredicate(RESERVED_WORDS); +KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION); +KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); + +var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); + +var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; +var RE_OCT_NUMBER = /^0[0-7]+$/; +var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; + +var OPERATORS = makePredicate([ + "in", + "instanceof", + "typeof", + "new", + "void", + "delete", + "++", + "--", + "+", + "-", + "!", + "~", + "&", + "|", + "^", + "*", + "/", + "%", + ">>", + "<<", + ">>>", + "<", + ">", + "<=", + ">=", + "==", + "===", + "!=", + "!==", + "?", + "=", + "+=", + "-=", + "/=", + "*=", + "%=", + ">>=", + "<<=", + ">>>=", + "|=", + "^=", + "&=", + "&&", + "||" +]); + +var WHITESPACE_CHARS = makePredicate(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 = makePredicate(characters("[{(,.;:")); + +var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); + +var REGEXP_MODIFIERS = makePredicate(characters("gmsiy")); + +/* -----[ Tokenizer ]----- */ + +// regexps adapted from http://xregexp.com/plugins/#unicode +var UNICODE = { + letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"), + non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"), + space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"), + connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]") +}; + +function is_letter(code) { + return (code >= 97 && code <= 122) + || (code >= 65 && code <= 90) + || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code))); +}; + +function is_digit(code) { + return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9 +}; + +function is_alphanumeric_char(code) { + return is_digit(code) || is_letter(code); +}; + +function is_unicode_combining_mark(ch) { + return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch); +}; + +function is_unicode_connector_punctuation(ch) { + return UNICODE.connector_punctuation.test(ch); +}; + +function is_identifier(name) { + return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name); +}; + +function is_identifier_start(code) { + return code == 36 || code == 95 || is_letter(code); +}; + +function is_identifier_char(ch) { + var code = ch.charCodeAt(0); + return is_identifier_start(code) + || is_digit(code) + || code == 8204 // \u200c: zero-width non-joiner + || code == 8205 // \u200d: zero-width joiner (in my ECMA-262 PDF, this is also 200c) + || is_unicode_combining_mark(ch) + || is_unicode_connector_punctuation(ch) + ; +}; + +function is_identifier_string(str){ + var i = str.length; + if (i == 0) return false; + if (!is_identifier_start(str.charCodeAt(0))) return false; + while (--i >= 0) { + if (!is_identifier_char(str.charAt(i))) + return false; + } + return true; +}; + +function parse_js_number(num) { + if (RE_HEX_NUMBER.test(num)) { + return parseInt(num.substr(2), 16); + } else if (RE_OCT_NUMBER.test(num)) { + return parseInt(num.substr(1), 8); + } else if (RE_DEC_NUMBER.test(num)) { + return parseFloat(num); + } +}; + +function JS_Parse_Error(message, line, col, pos) { + this.message = message; + this.line = line; + this.col = col; + this.pos = pos; + this.stack = new Error().stack; +}; + +JS_Parse_Error.prototype.toString = function() { + return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; +}; + +function js_error(message, filename, line, col, pos) { + throw new JS_Parse_Error(message, line, col, pos); +}; + +function is_token(token, type, val) { + return token.type == type && (val == null || token.value == val); +}; + +var EX_EOF = {}; + +function tokenizer($TEXT, filename, html5_comments) { + + var S = { + text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''), + filename : filename, + pos : 0, + tokpos : 0, + line : 1, + tokline : 0, + col : 0, + tokcol : 0, + newline_before : false, + regex_allowed : false, + comments_before : [] + }; + + function peek() { return S.text.charAt(S.pos); }; + + function next(signal_eof, in_string) { + var ch = S.text.charAt(S.pos++); + if (signal_eof && !ch) + throw EX_EOF; + if (ch == "\n") { + S.newline_before = S.newline_before || !in_string; + ++S.line; + S.col = 0; + } else { + ++S.col; + } + return ch; + }; + + function forward(i) { + while (i-- > 0) next(); + }; + + function looking_at(str) { + return S.text.substr(S.pos, str.length) == str; + }; + + function find(what, signal_eof) { + var pos = S.text.indexOf(what, S.pos); + if (signal_eof && pos == -1) throw EX_EOF; + return pos; + }; + + function start_token() { + S.tokline = S.line; + S.tokcol = S.col; + S.tokpos = S.pos; + }; + + var prev_was_dot = false; + function token(type, value, is_comment) { + S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || + (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || + (type == "punc" && PUNC_BEFORE_EXPRESSION(value))); + prev_was_dot = (type == "punc" && value == "."); + var ret = { + type : type, + value : value, + line : S.tokline, + col : S.tokcol, + pos : S.tokpos, + endpos : S.pos, + nlb : S.newline_before, + file : filename + }; + if (!is_comment) { + ret.comments_before = S.comments_before; + S.comments_before = []; + // make note of any newlines in the comments that came before + for (var i = 0, len = ret.comments_before.length; i < len; i++) { + ret.nlb = ret.nlb || ret.comments_before[i].nlb; + } + } + S.newline_before = false; + return new AST_Token(ret); + }; + + function skip_whitespace() { + while (WHITESPACE_CHARS(peek())) + next(); + }; + + function read_while(pred) { + var ret = "", ch, i = 0; + while ((ch = peek()) && pred(ch, i++)) + ret += next(); + return ret; + }; + + function parse_error(err) { + js_error(err, filename, S.tokline, S.tokcol, S.tokpos); + }; + + function read_num(prefix) { + var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; + var num = read_while(function(ch, i){ + var code = ch.charCodeAt(0); + switch (code) { + case 120: case 88: // xX + return has_x ? false : (has_x = true); + case 101: case 69: // eE + return has_x ? true : has_e ? false : (has_e = after_e = true); + case 45: // - + return after_e || (i == 0 && !prefix); + case 43: // + + return after_e; + case (after_e = false, 46): // . + return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; + } + return is_alphanumeric_char(code); + }); + if (prefix) num = prefix + num; + var valid = parse_js_number(num); + if (!isNaN(valid)) { + return token("num", valid); + } else { + parse_error("Invalid syntax: " + num); + } + }; + + function read_escaped_char(in_string) { + var ch = next(true, in_string); + switch (ch.charCodeAt(0)) { + case 110 : return "\n"; + case 114 : return "\r"; + case 116 : return "\t"; + case 98 : return "\b"; + case 118 : return "\u000b"; // \v + case 102 : return "\f"; + case 48 : return "\0"; + case 120 : return String.fromCharCode(hex_bytes(2)); // \x + case 117 : return String.fromCharCode(hex_bytes(4)); // \u + case 10 : return ""; // newline + default : return ch; + } + }; + + function hex_bytes(n) { + var num = 0; + for (; n > 0; --n) { + var digit = parseInt(next(true), 16); + if (isNaN(digit)) + parse_error("Invalid hex-character pattern in string"); + num = (num << 4) | digit; + } + return num; + }; + + var read_string = with_eof_error("Unterminated string constant", function(){ + var quote = next(), ret = ""; + for (;;) { + var ch = next(true); + if (ch == "\\") { + // read OctalEscapeSequence (XXX: deprecated if "strict mode") + // https://github.com/mishoo/UglifyJS/issues/178 + var octal_len = 0, first = null; + ch = read_while(function(ch){ + if (ch >= "0" && ch <= "7") { + if (!first) { + first = ch; + return ++octal_len; + } + else if (first <= "3" && octal_len <= 2) return ++octal_len; + else if (first >= "4" && octal_len <= 1) return ++octal_len; + } + return false; + }); + if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8)); + else ch = read_escaped_char(true); + } + else if (ch == quote) break; + ret += ch; + } + return token("string", ret); + }); + + function skip_line_comment(type) { + var regex_allowed = S.regex_allowed; + var i = find("\n"), ret; + if (i == -1) { + ret = S.text.substr(S.pos); + S.pos = S.text.length; + } else { + ret = S.text.substring(S.pos, i); + S.pos = i; + } + S.comments_before.push(token(type, ret, true)); + S.regex_allowed = regex_allowed; + return next_token(); + }; + + var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){ + var regex_allowed = S.regex_allowed; + var i = find("*/", true); + var text = S.text.substring(S.pos, i); + var a = text.split("\n"), n = a.length; + // update stream position + S.pos = i + 2; + S.line += n - 1; + if (n > 1) S.col = a[n - 1].length; + else S.col += a[n - 1].length; + S.col += 2; + var nlb = S.newline_before = S.newline_before || text.indexOf("\n") >= 0; + S.comments_before.push(token("comment2", text, true)); + S.regex_allowed = regex_allowed; + S.newline_before = nlb; + return next_token(); + }); + + function read_name() { + var backslash = false, name = "", ch, escaped = false, hex; + while ((ch = peek()) != null) { + if (!backslash) { + 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"); + ch = read_escaped_char(); + if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); + name += ch; + backslash = false; + } + } + if (KEYWORDS(name) && escaped) { + hex = name.charCodeAt(0).toString(16).toUpperCase(); + name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1); + } + return name; + }; + + var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){ + var prev_backslash = false, ch, in_class = false; + while ((ch = next(true))) if (prev_backslash) { + regexp += "\\" + ch; + prev_backslash = false; + } else if (ch == "[") { + in_class = true; + regexp += ch; + } else if (ch == "]" && in_class) { + in_class = false; + regexp += ch; + } else if (ch == "/" && !in_class) { + break; + } else if (ch == "\\") { + prev_backslash = true; + } else { + regexp += ch; + } + var mods = read_name(); + return token("regexp", new RegExp(regexp, mods)); + }); + + function read_operator(prefix) { + function grow(op) { + if (!peek()) return op; + var bigger = op + peek(); + if (OPERATORS(bigger)) { + next(); + return grow(bigger); + } else { + return op; + } + }; + return token("operator", grow(prefix || next())); + }; + + function handle_slash() { + next(); + switch (peek()) { + case "/": + next(); + return skip_line_comment("comment1"); + case "*": + next(); + return skip_multiline_comment(); + } + return S.regex_allowed ? read_regexp("") : read_operator("/"); + }; + + function handle_dot() { + next(); + return is_digit(peek().charCodeAt(0)) + ? read_num(".") + : token("punc", "."); + }; + + function read_word() { + var word = read_name(); + if (prev_was_dot) return token("name", word); + return KEYWORDS_ATOM(word) ? token("atom", word) + : !KEYWORDS(word) ? token("name", word) + : OPERATORS(word) ? token("operator", word) + : token("keyword", word); + }; + + function with_eof_error(eof_error, cont) { + return function(x) { + try { + return cont(x); + } catch(ex) { + if (ex === EX_EOF) parse_error(eof_error); + else throw ex; + } + }; + }; + + function next_token(force_regexp) { + if (force_regexp != null) + return read_regexp(force_regexp); + skip_whitespace(); + start_token(); + if (html5_comments) { + if (looking_at("") && S.newline_before) { + forward(3); + return skip_line_comment("comment4"); + } + } + var ch = peek(); + if (!ch) return token("eof"); + var code = ch.charCodeAt(0); + switch (code) { + case 34: case 39: return read_string(); + case 46: return handle_dot(); + case 47: return handle_slash(); + } + if (is_digit(code)) return read_num(); + if (PUNC_CHARS(ch)) return token("punc", next()); + if (OPERATOR_CHARS(ch)) return read_operator(); + if (code == 92 || is_identifier_start(code)) return read_word(); + parse_error("Unexpected character '" + ch + "'"); + }; + + next_token.context = function(nc) { + if (nc) S = nc; + return S; + }; + + return next_token; + +}; + +/* -----[ Parser (constants) ]----- */ + +var UNARY_PREFIX = makePredicate([ + "typeof", + "void", + "delete", + "--", + "++", + "!", + "~", + "-", + "+" +]); + +var UNARY_POSTFIX = makePredicate([ "--", "++" ]); + +var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); + +var PRECEDENCE = (function(a, ret){ + for (var i = 0; i < a.length; ++i) { + var b = a[i]; + for (var j = 0; j < b.length; ++j) { + ret[b[j]] = i + 1; + } + } + return ret; +})( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ], + {} +); + +var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); + +var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); + +/* -----[ Parser ]----- */ + +function parse($TEXT, options) { + + options = defaults(options, { + strict : false, + filename : null, + toplevel : null, + expression : false, + html5_comments : true, + }); + + var S = { + input : (typeof $TEXT == "string" + ? tokenizer($TEXT, options.filename, + options.html5_comments) + : $TEXT), + token : null, + prev : null, + peeked : null, + in_function : 0, + in_directives : true, + in_loop : 0, + labels : [] + }; + + S.token = next(); + + function is(type, value) { + return is_token(S.token, type, value); + }; + + function peek() { return S.peeked || (S.peeked = S.input()); }; + + function next() { + S.prev = S.token; + if (S.peeked) { + S.token = S.peeked; + S.peeked = null; + } else { + S.token = S.input(); + } + S.in_directives = S.in_directives && ( + S.token.type == "string" || is("punc", ";") + ); + return S.token; + }; + + function prev() { + return S.prev; + }; + + function croak(msg, line, col, pos) { + var ctx = S.input.context(); + js_error(msg, + ctx.filename, + line != null ? line : ctx.tokline, + col != null ? col : ctx.tokcol, + pos != null ? pos : ctx.tokpos); + }; + + function token_error(token, msg) { + croak(msg, token.line, token.col); + }; + + function unexpected(token) { + if (token == null) + token = S.token; + token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); + }; + + function expect_token(type, val) { + if (is(type, val)) { + return next(); + } + token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); + }; + + function expect(punc) { return expect_token("punc", punc); }; + + function can_insert_semicolon() { + return !options.strict && ( + S.token.nlb || is("eof") || is("punc", "}") + ); + }; + + function semicolon() { + if (is("punc", ";")) next(); + else if (!can_insert_semicolon()) unexpected(); + }; + + function parenthesised() { + expect("("); + var exp = expression(true); + expect(")"); + return exp; + }; + + function embed_tokens(parser) { + return function() { + var start = S.token; + var expr = parser(); + var end = prev(); + expr.start = start; + expr.end = end; + return expr; + }; + }; + + function handle_regexp() { + if (is("operator", "/") || is("operator", "/=")) { + S.peeked = null; + S.token = S.input(S.token.value.substr(1)); // force regexp + } + }; + + var statement = embed_tokens(function() { + var tmp; + handle_regexp(); + switch (S.token.type) { + case "string": + var dir = S.in_directives, stat = simple_statement(); + // XXXv2: decide how to fix directives + if (dir && stat.body instanceof AST_String && !is("punc", ",")) + return new AST_Directive({ value: stat.body.value }); + return stat; + case "num": + case "regexp": + case "operator": + case "atom": + return simple_statement(); + + case "name": + return is_token(peek(), "punc", ":") + ? labeled_statement() + : simple_statement(); + + case "punc": + switch (S.token.value) { + case "{": + return new AST_BlockStatement({ + start : S.token, + body : block_(), + end : prev() + }); + case "[": + case "(": + return simple_statement(); + case ";": + next(); + return new AST_EmptyStatement(); + default: + unexpected(); + } + + case "keyword": + switch (tmp = S.token.value, next(), tmp) { + case "break": + return break_cont(AST_Break); + + case "continue": + return break_cont(AST_Continue); + + case "debugger": + semicolon(); + return new AST_Debugger(); + + case "do": + return new AST_Do({ + body : in_loop(statement), + condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp) + }); + + case "while": + return new AST_While({ + condition : parenthesised(), + body : in_loop(statement) + }); + + case "for": + return for_(); + + case "function": + return function_(AST_Defun); + + case "if": + return if_(); + + case "return": + if (S.in_function == 0) + croak("'return' outside of function"); + return new AST_Return({ + value: ( is("punc", ";") + ? (next(), null) + : can_insert_semicolon() + ? null + : (tmp = expression(true), semicolon(), tmp) ) + }); + + case "switch": + return new AST_Switch({ + expression : parenthesised(), + body : in_loop(switch_body_) + }); + + case "throw": + if (S.token.nlb) + croak("Illegal newline after 'throw'"); + return new AST_Throw({ + value: (tmp = expression(true), semicolon(), tmp) + }); + + case "try": + return try_(); + + case "var": + return tmp = var_(), semicolon(), tmp; + + case "const": + return tmp = const_(), semicolon(), tmp; + + case "with": + return new AST_With({ + expression : parenthesised(), + body : statement() + }); + + default: + unexpected(); + } + } + }); + + function labeled_statement() { + var label = as_symbol(AST_Label); + if (find_if(function(l){ return l.name == label.name }, S.labels)) { + // ECMA-262, 12.12: An ECMAScript program is considered + // syntactically incorrect if it contains a + // LabelledStatement that is enclosed by a + // LabelledStatement with the same Identifier as label. + croak("Label " + label.name + " defined twice"); + } + expect(":"); + S.labels.push(label); + var stat = statement(); + S.labels.pop(); + if (!(stat instanceof AST_IterationStatement)) { + // check for `continue` that refers to this label. + // those should be reported as syntax errors. + // https://github.com/mishoo/UglifyJS2/issues/287 + label.references.forEach(function(ref){ + if (ref instanceof AST_Continue) { + ref = ref.label.start; + croak("Continue label `" + label.name + "` refers to non-IterationStatement.", + ref.line, ref.col, ref.pos); + } + }); + } + return new AST_LabeledStatement({ body: stat, label: label }); + }; + + function simple_statement(tmp) { + return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); + }; + + function break_cont(type) { + var label = null, ldef; + if (!can_insert_semicolon()) { + label = as_symbol(AST_LabelRef, true); + } + if (label != null) { + ldef = find_if(function(l){ return l.name == label.name }, S.labels); + if (!ldef) + croak("Undefined label " + label.name); + label.thedef = ldef; + } + else if (S.in_loop == 0) + croak(type.TYPE + " not inside a loop or switch"); + semicolon(); + var stat = new type({ label: label }); + if (ldef) ldef.references.push(stat); + return stat; + }; + + function for_() { + expect("("); + var init = null; + if (!is("punc", ";")) { + init = is("keyword", "var") + ? (next(), var_(true)) + : expression(true, true); + if (is("operator", "in")) { + if (init instanceof AST_Var && init.definitions.length > 1) + croak("Only one variable declaration allowed in for..in loop"); + next(); + return for_in(init); + } + } + return regular_for(init); + }; + + function regular_for(init) { + expect(";"); + var test = is("punc", ";") ? null : expression(true); + expect(";"); + var step = is("punc", ")") ? null : expression(true); + expect(")"); + return new AST_For({ + init : init, + condition : test, + step : step, + body : in_loop(statement) + }); + }; + + function for_in(init) { + var lhs = init instanceof AST_Var ? init.definitions[0].name : null; + var obj = expression(true); + expect(")"); + return new AST_ForIn({ + init : init, + name : lhs, + object : obj, + body : in_loop(statement) + }); + }; + + var function_ = function(ctor) { + var in_statement = ctor === AST_Defun; + var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; + if (in_statement && !name) + unexpected(); + expect("("); + return new ctor({ + name: name, + argnames: (function(first, a){ + while (!is("punc", ")")) { + if (first) first = false; else expect(","); + a.push(as_symbol(AST_SymbolFunarg)); + } + next(); + return a; + })(true, []), + body: (function(loop, labels){ + ++S.in_function; + S.in_directives = true; + S.in_loop = 0; + S.labels = []; + var a = block_(); + --S.in_function; + S.in_loop = loop; + S.labels = labels; + return a; + })(S.in_loop, S.labels) + }); + }; + + function if_() { + var cond = parenthesised(), body = statement(), belse = null; + if (is("keyword", "else")) { + next(); + belse = statement(); + } + return new AST_If({ + condition : cond, + body : body, + alternative : belse + }); + }; + + function block_() { + expect("{"); + var a = []; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + a.push(statement()); + } + next(); + return a; + }; + + function switch_body_() { + expect("{"); + var a = [], cur = null, branch = null, tmp; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + if (is("keyword", "case")) { + if (branch) branch.end = prev(); + cur = []; + branch = new AST_Case({ + start : (tmp = S.token, next(), tmp), + expression : expression(true), + body : cur + }); + a.push(branch); + expect(":"); + } + else if (is("keyword", "default")) { + if (branch) branch.end = prev(); + cur = []; + branch = new AST_Default({ + start : (tmp = S.token, next(), expect(":"), tmp), + body : cur + }); + a.push(branch); + } + else { + if (!cur) unexpected(); + cur.push(statement()); + } + } + if (branch) branch.end = prev(); + next(); + return a; + }; + + function try_() { + var body = block_(), bcatch = null, bfinally = null; + if (is("keyword", "catch")) { + var start = S.token; + next(); + expect("("); + var name = as_symbol(AST_SymbolCatch); + expect(")"); + bcatch = new AST_Catch({ + start : start, + argname : name, + body : block_(), + end : prev() + }); + } + if (is("keyword", "finally")) { + var start = S.token; + next(); + bfinally = new AST_Finally({ + start : start, + body : block_(), + end : prev() + }); + } + if (!bcatch && !bfinally) + croak("Missing catch/finally blocks"); + return new AST_Try({ + body : body, + bcatch : bcatch, + bfinally : bfinally + }); + }; + + function vardefs(no_in, in_const) { + var a = []; + for (;;) { + a.push(new AST_VarDef({ + start : S.token, + name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), + value : is("operator", "=") ? (next(), expression(false, no_in)) : null, + end : prev() + })); + if (!is("punc", ",")) + break; + next(); + } + return a; + }; + + var var_ = function(no_in) { + return new AST_Var({ + start : prev(), + definitions : vardefs(no_in, false), + end : prev() + }); + }; + + var const_ = function() { + return new AST_Const({ + start : prev(), + definitions : vardefs(false, true), + end : prev() + }); + }; + + var new_ = function() { + var start = S.token; + expect_token("operator", "new"); + var newexp = expr_atom(false), args; + if (is("punc", "(")) { + next(); + args = expr_list(")"); + } else { + args = []; + } + return subscripts(new AST_New({ + start : start, + expression : newexp, + args : args, + end : prev() + }), true); + }; + + function as_atom_node() { + var tok = S.token, ret; + switch (tok.type) { + case "name": + case "keyword": + ret = _make_symbol(AST_SymbolRef); + break; + case "num": + ret = new AST_Number({ start: tok, end: tok, value: tok.value }); + break; + case "string": + ret = new AST_String({ start: tok, end: tok, value: tok.value }); + break; + case "regexp": + ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); + break; + case "atom": + switch (tok.value) { + case "false": + ret = new AST_False({ start: tok, end: tok }); + break; + case "true": + ret = new AST_True({ start: tok, end: tok }); + break; + case "null": + ret = new AST_Null({ start: tok, end: tok }); + break; } - cats.push([ words[i] ]); + break; } - function compareTo(arr) { - if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; - f += "switch(str){"; - for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; - f += "return true}return false;"; + next(); + return ret; + }; + + var expr_atom = function(allow_calls) { + if (is("operator", "new")) { + return new_(); } - if (cats.length > 3) { - cats.sort(function(a, b) { - return b.length - a.length; - }); - f += "switch(str.length){"; - for (var i = 0; i < cats.length; ++i) { - var cat = cats[i]; - f += "case " + cat[0].length + ":"; - compareTo(cat); + var start = S.token; + if (is("punc")) { + switch (start.value) { + case "(": + next(); + var ex = expression(true); + ex.start = start; + ex.end = S.token; + expect(")"); + return subscripts(ex, allow_calls); + case "[": + return subscripts(array_(), allow_calls); + case "{": + return subscripts(object_(), allow_calls); } - f += "}"; - } else { - compareTo(words); + unexpected(); } - return new Function("str", f); - } - function all(array, predicate) { - for (var i = array.length; --i >= 0; ) if (!predicate(array[i])) return false; - return true; - } - function Dictionary() { - this._values = Object.create(null); - this._size = 0; - } - Dictionary.prototype = { - set: function(key, val) { - if (!this.has(key)) ++this._size; - this._values["$" + key] = val; - return this; - }, - add: function(key, val) { - if (this.has(key)) { - this.get(key).push(val); - } else { - this.set(key, [ val ]); - } - return this; - }, - get: function(key) { - return this._values["$" + key]; - }, - del: function(key) { - if (this.has(key)) { - --this._size; - delete this._values["$" + key]; - } - return this; - }, - has: function(key) { - return "$" + key in this._values; - }, - each: function(f) { - for (var i in this._values) f(this._values[i], i.substr(1)); - }, - size: function() { - return this._size; - }, - map: function(f) { - var ret = []; - for (var i in this._values) ret.push(f(this._values[i], i.substr(1))); - return ret; + if (is("keyword", "function")) { + next(); + var func = function_(AST_Function); + func.start = start; + func.end = prev(); + return subscripts(func, allow_calls); } + if (ATOMIC_START_TOKEN[S.token.type]) { + return subscripts(as_atom_node(), allow_calls); + } + unexpected(); }; - "use strict"; - function DEFNODE(type, props, methods, base) { - if (arguments.length < 4) base = AST_Node; - if (!props) props = []; else props = props.split(/\s+/); - var self_props = props; - if (base && base.PROPS) props = props.concat(base.PROPS); - var code = "return function AST_" + type + "(props){ if (props) { "; - for (var i = props.length; --i >= 0; ) { - code += "this." + props[i] + " = props." + props[i] + ";"; - } - var proto = base && new base(); - if (proto && proto.initialize || methods && methods.initialize) code += "this.initialize();"; - code += "}}"; - var ctor = new Function(code)(); - if (proto) { - ctor.prototype = proto; - ctor.BASE = base; - } - if (base) base.SUBCLASSES.push(ctor); - ctor.prototype.CTOR = ctor; - ctor.PROPS = props || null; - ctor.SELF_PROPS = self_props; - ctor.SUBCLASSES = []; - if (type) { - ctor.prototype.TYPE = ctor.TYPE = type; - } - if (methods) for (i in methods) if (methods.hasOwnProperty(i)) { - if (/^\$/.test(i)) { - ctor[i.substr(1)] = methods[i]; + + function expr_list(closing, allow_trailing_comma, allow_empty) { + var first = true, a = []; + while (!is("punc", closing)) { + if (first) first = false; else expect(","); + if (allow_trailing_comma && is("punc", closing)) break; + if (is("punc", ",") && allow_empty) { + a.push(new AST_Hole({ start: S.token, end: S.token })); } else { - ctor.prototype[i] = methods[i]; + a.push(expression(false)); } } - ctor.DEFMETHOD = function(name, method) { - this.prototype[name] = method; - }; - return ctor; - } - var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {}, null); - var AST_Node = DEFNODE("Node", "start end", { - clone: function() { - return new this.CTOR(this); - }, - $documentation: "Base class of all AST nodes", - $propdoc: { - start: "[AST_Token] The first token of this node", - end: "[AST_Token] The last token of this node" - }, - _walk: function(visitor) { - return visitor._visit(this); - }, - walk: function(visitor) { - return this._walk(visitor); - } - }, null); - AST_Node.warn_function = null; - AST_Node.warn = function(txt, props) { - if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props)); + next(); + return a; }; - var AST_Statement = DEFNODE("Statement", null, { - $documentation: "Base class of all statements" - }); - var AST_Debugger = DEFNODE("Debugger", null, { - $documentation: "Represents a debugger statement" - }, AST_Statement); - var AST_Directive = DEFNODE("Directive", "value scope", { - $documentation: 'Represents a directive, like "use strict";', - $propdoc: { - value: "[string] The value of this directive as a plain string (it's not an AST_String!)", - scope: "[AST_Scope/S] The scope that this directive affects" - } - }, AST_Statement); - var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { - $documentation: "A statement consisting of an expression, i.e. a = 1 + 2", - $propdoc: { - body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.body._walk(visitor); - }); - } - }, AST_Statement); - function walk_body(node, visitor) { - if (node.body instanceof AST_Statement) { - node.body._walk(visitor); - } else node.body.forEach(function(stat) { - stat._walk(visitor); + + var array_ = embed_tokens(function() { + expect("["); + return new AST_Array({ + elements: expr_list("]", !options.strict, true) }); - } - var AST_Block = DEFNODE("Block", "body", { - $documentation: "A body of statements (usually bracketed)", - $propdoc: { - body: "[AST_Statement*] an array of statements" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - walk_body(this, visitor); - }); - } - }, AST_Statement); - var AST_BlockStatement = DEFNODE("BlockStatement", null, { - $documentation: "A block statement" - }, AST_Block); - var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { - $documentation: "The empty statement (empty block or simply a semicolon)", - _walk: function(visitor) { - return visitor._visit(this); - } - }, AST_Statement); - var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { - $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", - $propdoc: { - body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.body._walk(visitor); - }); - } - }, AST_Statement); - var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { - $documentation: "Statement with a label", - $propdoc: { - label: "[AST_Label] a label definition" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.label._walk(visitor); - this.body._walk(visitor); - }); - } - }, AST_StatementWithBody); - var AST_DWLoop = DEFNODE("DWLoop", "condition", { - $documentation: "Base class for do/while statements", - $propdoc: { - condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.condition._walk(visitor); - this.body._walk(visitor); - }); - } - }, AST_StatementWithBody); - var AST_Do = DEFNODE("Do", null, { - $documentation: "A `do` statement" - }, AST_DWLoop); - var AST_While = DEFNODE("While", null, { - $documentation: "A `while` statement" - }, AST_DWLoop); - var AST_For = DEFNODE("For", "init condition step", { - $documentation: "A `for` statement", - $propdoc: { - init: "[AST_Node?] the `for` initialization code, or null if empty", - condition: "[AST_Node?] the `for` termination clause, or null if empty", - step: "[AST_Node?] the `for` update clause, or null if empty" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - if (this.init) this.init._walk(visitor); - if (this.condition) this.condition._walk(visitor); - if (this.step) this.step._walk(visitor); - this.body._walk(visitor); - }); - } - }, AST_StatementWithBody); - var AST_ForIn = DEFNODE("ForIn", "init name object", { - $documentation: "A `for ... in` statement", - $propdoc: { - init: "[AST_Node] the `for/in` initialization code", - name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var", - object: "[AST_Node] the object that we're looping through" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.init._walk(visitor); - this.object._walk(visitor); - this.body._walk(visitor); - }); - } - }, AST_StatementWithBody); - var AST_With = DEFNODE("With", "expression", { - $documentation: "A `with` statement", - $propdoc: { - expression: "[AST_Node] the `with` expression" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - this.body._walk(visitor); - }); - } - }, AST_StatementWithBody); - var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", { - $documentation: "Base class for all statements introducing a lexical scope", - $propdoc: { - directives: "[string*/S] an array of directives declared in this scope", - variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope", - functions: "[Object/S] like `variables`, but only lists function declarations", - uses_with: "[boolean/S] tells whether this scope uses the `with` statement", - uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", - parent_scope: "[AST_Scope?/S] link to the parent scope", - enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", - cname: "[integer/S] current index for mangling variables (used internally by the mangler)" - } - }, AST_Block); - var AST_Toplevel = DEFNODE("Toplevel", "globals", { - $documentation: "The toplevel scope", - $propdoc: { - globals: "[Object/S] a map of name -> SymbolDef for all undeclared names" - }, - wrap_enclose: function(arg_parameter_pairs) { - var self = this; - var args = []; - var parameters = []; - arg_parameter_pairs.forEach(function(pair) { - var split = pair.split(":"); - args.push(split[0]); - parameters.push(split[1]); - }); - var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")"; - wrapped_tl = parse(wrapped_tl); - wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node) { - if (node instanceof AST_Directive && node.value == "$ORIG") { - return MAP.splice(self.body); + }); + + var object_ = embed_tokens(function() { + expect("{"); + var first = true, a = []; + while (!is("punc", "}")) { + if (first) first = false; else expect(","); + if (!options.strict && is("punc", "}")) + // allow trailing comma + break; + var start = S.token; + var type = start.type; + var name = as_property_name(); + if (type == "name" && !is("punc", ":")) { + if (name == "get") { + a.push(new AST_ObjectGetter({ + start : start, + key : as_atom_node(), + value : function_(AST_Accessor), + end : prev() + })); + continue; + } + if (name == "set") { + a.push(new AST_ObjectSetter({ + start : start, + key : as_atom_node(), + value : function_(AST_Accessor), + end : prev() + })); + continue; } - })); - return wrapped_tl; - }, - wrap_commonjs: function(name, export_all) { - var self = this; - var to_export = []; - if (export_all) { - self.figure_out_scope(); - self.walk(new TreeWalker(function(node) { - if (node instanceof AST_SymbolDeclaration && node.definition().global) { - if (!find_if(function(n) { - return n.name == node.name; - }, to_export)) to_export.push(node); - } - })); } - var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))"; - wrapped_tl = parse(wrapped_tl); - wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node) { - if (node instanceof AST_SimpleStatement) { - node = node.body; - if (node instanceof AST_String) switch (node.getValue()) { - case "$ORIG": - return MAP.splice(self.body); - - case "$EXPORTS": - var body = []; - to_export.forEach(function(sym) { - body.push(new AST_SimpleStatement({ - body: new AST_Assign({ - left: new AST_Sub({ - expression: new AST_SymbolRef({ - name: "exports" - }), - property: new AST_String({ - value: sym.name - }) - }), - operator: "=", - right: new AST_SymbolRef(sym) - }) - })); - }); - return MAP.splice(body); - } - } + expect(":"); + a.push(new AST_ObjectKeyVal({ + start : start, + key : name, + value : expression(false), + end : prev() })); - return wrapped_tl; - } - }, AST_Scope); - var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { - $documentation: "Base class for functions", - $propdoc: { - name: "[AST_SymbolDeclaration?] the name of this function", - argnames: "[AST_SymbolFunarg*] array of function arguments", - uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - if (this.name) this.name._walk(visitor); - this.argnames.forEach(function(arg) { - arg._walk(visitor); - }); - walk_body(this, visitor); - }); } - }, AST_Scope); - var AST_Accessor = DEFNODE("Accessor", null, { - $documentation: "A setter/getter function" - }, AST_Lambda); - var AST_Function = DEFNODE("Function", null, { - $documentation: "A function expression" - }, AST_Lambda); - var AST_Defun = DEFNODE("Defun", null, { - $documentation: "A function definition" - }, AST_Lambda); - var AST_Jump = DEFNODE("Jump", null, { - $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" - }, AST_Statement); - var AST_Exit = DEFNODE("Exit", "value", { - $documentation: "Base class for “exits” (`return` and `throw`)", - $propdoc: { - value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" - }, - _walk: function(visitor) { - return visitor._visit(this, this.value && function() { - this.value._walk(visitor); - }); + next(); + return new AST_Object({ properties: a }); + }); + + function as_property_name() { + var tmp = S.token; + next(); + switch (tmp.type) { + case "num": + case "string": + case "name": + case "operator": + case "keyword": + case "atom": + return tmp.value; + default: + unexpected(); } - }, AST_Jump); - var AST_Return = DEFNODE("Return", null, { - $documentation: "A `return` statement" - }, AST_Exit); - var AST_Throw = DEFNODE("Throw", null, { - $documentation: "A `throw` statement" - }, AST_Exit); - var AST_LoopControl = DEFNODE("LoopControl", "label", { - $documentation: "Base class for loop control statements (`break` and `continue`)", - $propdoc: { - label: "[AST_LabelRef?] the label, or null if none" - }, - _walk: function(visitor) { - return visitor._visit(this, this.label && function() { - this.label._walk(visitor); - }); + }; + + function as_name() { + var tmp = S.token; + next(); + switch (tmp.type) { + case "name": + case "operator": + case "keyword": + case "atom": + return tmp.value; + default: + unexpected(); } - }, AST_Jump); - var AST_Break = DEFNODE("Break", null, { - $documentation: "A `break` statement" - }, AST_LoopControl); - var AST_Continue = DEFNODE("Continue", null, { - $documentation: "A `continue` statement" - }, AST_LoopControl); - var AST_If = DEFNODE("If", "condition alternative", { - $documentation: "A `if` statement", - $propdoc: { - condition: "[AST_Node] the `if` condition", - alternative: "[AST_Statement?] the `else` part, or null if not present" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.condition._walk(visitor); - this.body._walk(visitor); - if (this.alternative) this.alternative._walk(visitor); - }); + }; + + function _make_symbol(type) { + var name = S.token.value; + return new (name == "this" ? AST_This : type)({ + name : String(name), + start : S.token, + end : S.token + }); + }; + + function as_symbol(type, noerror) { + if (!is("name")) { + if (!noerror) croak("Name expected"); + return null; } - }, AST_StatementWithBody); - var AST_Switch = DEFNODE("Switch", "expression", { - $documentation: "A `switch` statement", - $propdoc: { - expression: "[AST_Node] the `switch` “discriminant”" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - walk_body(this, visitor); - }); + var sym = _make_symbol(type); + next(); + return sym; + }; + + var subscripts = function(expr, allow_calls) { + var start = expr.start; + if (is("punc", ".")) { + next(); + return subscripts(new AST_Dot({ + start : start, + expression : expr, + property : as_name(), + end : prev() + }), allow_calls); } - }, AST_Block); - var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { - $documentation: "Base class for `switch` branches" - }, AST_Block); - var AST_Default = DEFNODE("Default", null, { - $documentation: "A `default` switch branch" - }, AST_SwitchBranch); - var AST_Case = DEFNODE("Case", "expression", { - $documentation: "A `case` switch branch", - $propdoc: { - expression: "[AST_Node] the `case` expression" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - walk_body(this, visitor); - }); + if (is("punc", "[")) { + next(); + var prop = expression(true); + expect("]"); + return subscripts(new AST_Sub({ + start : start, + expression : expr, + property : prop, + end : prev() + }), allow_calls); } - }, AST_SwitchBranch); - var AST_Try = DEFNODE("Try", "bcatch bfinally", { - $documentation: "A `try` statement", - $propdoc: { - bcatch: "[AST_Catch?] the catch block, or null if not present", - bfinally: "[AST_Finally?] the finally block, or null if not present" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - walk_body(this, visitor); - if (this.bcatch) this.bcatch._walk(visitor); - if (this.bfinally) this.bfinally._walk(visitor); - }); + if (allow_calls && is("punc", "(")) { + next(); + return subscripts(new AST_Call({ + start : start, + expression : expr, + args : expr_list(")"), + end : prev() + }), true); } - }, AST_Block); - var AST_Catch = DEFNODE("Catch", "argname", { - $documentation: "A `catch` node; only makes sense as part of a `try` statement", - $propdoc: { - argname: "[AST_SymbolCatch] symbol for the exception" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.argname._walk(visitor); - walk_body(this, visitor); - }); + return expr; + }; + + var maybe_unary = function(allow_calls) { + var start = S.token; + if (is("operator") && UNARY_PREFIX(start.value)) { + next(); + handle_regexp(); + var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); + ex.start = start; + ex.end = prev(); + return ex; } - }, AST_Block); - var AST_Finally = DEFNODE("Finally", null, { - $documentation: "A `finally` node; only makes sense as part of a `try` statement" - }, AST_Block); - var AST_Definitions = DEFNODE("Definitions", "definitions", { - $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", - $propdoc: { - definitions: "[AST_VarDef*] array of variable definitions" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.definitions.forEach(function(def) { - def._walk(visitor); - }); - }); + var val = expr_atom(allow_calls); + while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { + val = make_unary(AST_UnaryPostfix, S.token.value, val); + val.start = start; + val.end = S.token; + next(); } - }, AST_Statement); - var AST_Var = DEFNODE("Var", null, { - $documentation: "A `var` statement" - }, AST_Definitions); - var AST_Const = DEFNODE("Const", null, { - $documentation: "A `const` statement" - }, AST_Definitions); - var AST_VarDef = DEFNODE("VarDef", "name value", { - $documentation: "A variable declaration; only appears in a AST_Definitions node", - $propdoc: { - name: "[AST_SymbolVar|AST_SymbolConst] name of the variable", - value: "[AST_Node?] initializer, or null of there's no initializer" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.name._walk(visitor); - if (this.value) this.value._walk(visitor); + return val; + }; + + function make_unary(ctor, op, expr) { + if ((op == "++" || op == "--") && !is_assignable(expr)) + croak("Invalid use of " + op + " operator"); + return new ctor({ operator: op, expression: expr }); + }; + + var expr_op = function(left, min_prec, no_in) { + var op = is("operator") ? S.token.value : null; + if (op == "in" && no_in) op = null; + var prec = op != null ? PRECEDENCE[op] : null; + if (prec != null && prec > min_prec) { + next(); + var right = expr_op(maybe_unary(true), prec, no_in); + return expr_op(new AST_Binary({ + start : left.start, + left : left, + operator : op, + right : right, + end : right.end + }), min_prec, no_in); + } + return left; + }; + + function expr_ops(no_in) { + return expr_op(maybe_unary(true), 0, no_in); + }; + + var maybe_conditional = function(no_in) { + var start = S.token; + var expr = expr_ops(no_in); + if (is("operator", "?")) { + next(); + var yes = expression(false); + expect(":"); + return new AST_Conditional({ + start : start, + condition : expr, + consequent : yes, + alternative : expression(false, no_in), + end : prev() }); } - }); - var AST_Call = DEFNODE("Call", "expression args", { - $documentation: "A function call expression", - $propdoc: { - expression: "[AST_Node] expression to invoke as function", - args: "[AST_Node*] array of arguments" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - this.args.forEach(function(arg) { - arg._walk(visitor); + return expr; + }; + + function is_assignable(expr) { + if (!options.strict) return true; + if (expr instanceof AST_This) return false; + return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol); + }; + + var maybe_assign = function(no_in) { + var start = S.token; + var left = maybe_conditional(no_in), val = S.token.value; + if (is("operator") && ASSIGNMENT(val)) { + if (is_assignable(left)) { + next(); + return new AST_Assign({ + start : start, + left : left, + operator : val, + right : maybe_assign(no_in), + end : prev() }); + } + croak("Invalid assignment"); + } + return left; + }; + + var expression = function(commas, no_in) { + var start = S.token; + var expr = maybe_assign(no_in); + if (commas && is("punc", ",")) { + next(); + return new AST_Seq({ + start : start, + car : expr, + cdr : expression(true, no_in), + end : peek() }); } - }); - var AST_New = DEFNODE("New", null, { - $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" - }, AST_Call); - var AST_Seq = DEFNODE("Seq", "car cdr", { - $documentation: "A sequence expression (two comma-separated expressions)", - $propdoc: { - car: "[AST_Node] first element in sequence", - cdr: "[AST_Node] second element in sequence" - }, - $cons: function(x, y) { - var seq = new AST_Seq(x); - seq.car = x; - seq.cdr = y; - return seq; - }, - $from_array: function(array) { - if (array.length == 0) return null; - if (array.length == 1) return array[0].clone(); - var list = null; - for (var i = array.length; --i >= 0; ) { - list = AST_Seq.cons(array[i], list); - } - var p = list; - while (p) { - if (p.cdr && !p.cdr.cdr) { - p.cdr = p.cdr.car; - break; - } - p = p.cdr; - } - return list; - }, - to_array: function() { - var p = this, a = []; - while (p) { - a.push(p.car); - if (p.cdr && !(p.cdr instanceof AST_Seq)) { - a.push(p.cdr); - break; - } - p = p.cdr; - } - return a; - }, - add: function(node) { - var p = this; - while (p) { - if (!(p.cdr instanceof AST_Seq)) { - var cell = AST_Seq.cons(p.cdr, node); - return p.cdr = cell; + return expr; + }; + + function in_loop(cont) { + ++S.in_loop; + var ret = cont(); + --S.in_loop; + return ret; + }; + + if (options.expression) { + return expression(true); + } + + return (function(){ + var start = S.token; + var body = []; + while (!is("eof")) + body.push(statement()); + var end = prev(); + var toplevel = options.toplevel; + if (toplevel) { + toplevel.body = toplevel.body.concat(body); + toplevel.end = end; + } else { + toplevel = new AST_Toplevel({ start: start, body: body, end: end }); + } + return toplevel; + })(); + +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. + + ***********************************************************************/ + +"use strict"; + +// Tree transformer helpers. + +function TreeTransformer(before, after) { + TreeWalker.call(this); + this.before = before; + this.after = after; +} +TreeTransformer.prototype = new TreeWalker; + +(function(undefined){ + + function _(node, descend) { + node.DEFMETHOD("transform", function(tw, in_list){ + var x, y; + tw.push(this); + if (tw.before) x = tw.before(this, descend, in_list); + if (x === undefined) { + if (!tw.after) { + x = this; + descend(x, tw); + } else { + tw.stack[tw.stack.length - 1] = x = this.clone(); + descend(x, tw); + y = tw.after(x, in_list); + if (y !== undefined) x = y; } - p = p.cdr; } - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.car._walk(visitor); - if (this.cdr) this.cdr._walk(visitor); - }); - } + tw.pop(); + return x; + }); + }; + + function do_list(list, tw) { + return MAP(list, function(node){ + return node.transform(tw, true); + }); + }; + + _(AST_Node, noop); + + _(AST_LabeledStatement, function(self, tw){ + self.label = self.label.transform(tw); + self.body = self.body.transform(tw); }); - var AST_PropAccess = DEFNODE("PropAccess", "expression property", { - $documentation: 'Base class for property access expressions, i.e. `a.foo` or `a["foo"]`', - $propdoc: { - expression: "[AST_Node] the “container” expression", - property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" - } + + _(AST_SimpleStatement, function(self, tw){ + self.body = self.body.transform(tw); }); - var AST_Dot = DEFNODE("Dot", null, { - $documentation: "A dotted property access expression", - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - }); - } - }, AST_PropAccess); - var AST_Sub = DEFNODE("Sub", null, { - $documentation: 'Index-style property access, i.e. `a["foo"]`', - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - this.property._walk(visitor); - }); - } - }, AST_PropAccess); - var AST_Unary = DEFNODE("Unary", "operator expression", { - $documentation: "Base class for unary expressions", - $propdoc: { - operator: "[string] the operator", - expression: "[AST_Node] expression that this unary operator applies to" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.expression._walk(visitor); - }); - } + + _(AST_Block, function(self, tw){ + self.body = do_list(self.body, tw); }); - var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { - $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" - }, AST_Unary); - var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { - $documentation: "Unary postfix expression, i.e. `i++`" - }, AST_Unary); - var AST_Binary = DEFNODE("Binary", "left operator right", { - $documentation: "Binary expression, i.e. `a + b`", - $propdoc: { - left: "[AST_Node] left-hand side expression", - operator: "[string] the operator", - right: "[AST_Node] right-hand side expression" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.left._walk(visitor); - this.right._walk(visitor); - }); - } + + _(AST_DWLoop, function(self, tw){ + self.condition = self.condition.transform(tw); + self.body = self.body.transform(tw); }); - var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { - $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", - $propdoc: { - condition: "[AST_Node]", - consequent: "[AST_Node]", - alternative: "[AST_Node]" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.condition._walk(visitor); - this.consequent._walk(visitor); - this.alternative._walk(visitor); - }); - } + + _(AST_For, function(self, tw){ + if (self.init) self.init = self.init.transform(tw); + if (self.condition) self.condition = self.condition.transform(tw); + if (self.step) self.step = self.step.transform(tw); + self.body = self.body.transform(tw); }); - var AST_Assign = DEFNODE("Assign", null, { - $documentation: "An assignment expression — `a = b + 5`" - }, AST_Binary); - var AST_Array = DEFNODE("Array", "elements", { - $documentation: "An array literal", - $propdoc: { - elements: "[AST_Node*] array of elements" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.elements.forEach(function(el) { - el._walk(visitor); - }); - }); - } + + _(AST_ForIn, function(self, tw){ + self.init = self.init.transform(tw); + self.object = self.object.transform(tw); + self.body = self.body.transform(tw); }); - var AST_Object = DEFNODE("Object", "properties", { - $documentation: "An object literal", - $propdoc: { - properties: "[AST_ObjectProperty*] array of properties" - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.properties.forEach(function(prop) { - prop._walk(visitor); - }); - }); - } + + _(AST_With, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = self.body.transform(tw); }); - var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { - $documentation: "Base class for literal object properties", - $propdoc: { - key: "[string] the property name; it's always a plain string in our AST, no matter if it was a string, number or identifier in original code", - value: "[AST_Node] property value. For setters and getters this is an AST_Function." - }, - _walk: function(visitor) { - return visitor._visit(this, function() { - this.value._walk(visitor); - }); - } + + _(AST_Exit, function(self, tw){ + if (self.value) self.value = self.value.transform(tw); }); - var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, { - $documentation: "A key: value object property" - }, AST_ObjectProperty); - var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { - $documentation: "An object setter property" - }, AST_ObjectProperty); - var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { - $documentation: "An object getter property" - }, AST_ObjectProperty); - var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { - $propdoc: { - name: "[string] name of this symbol", - scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", - thedef: "[SymbolDef/S] the definition of this symbol" - }, - $documentation: "Base class for all symbols" + + _(AST_LoopControl, function(self, tw){ + if (self.label) self.label = self.label.transform(tw); }); - var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { - $documentation: "The name of a property accessor (setter/getter function)" - }, AST_Symbol); - var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { - $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", - $propdoc: { - init: "[AST_Node*/S] array of initializers for this declaration." - } - }, AST_Symbol); - var AST_SymbolVar = DEFNODE("SymbolVar", null, { - $documentation: "Symbol defining a variable" - }, AST_SymbolDeclaration); - var AST_SymbolConst = DEFNODE("SymbolConst", null, { - $documentation: "A constant declaration" - }, AST_SymbolDeclaration); - var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { - $documentation: "Symbol naming a function argument" - }, AST_SymbolVar); - var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { - $documentation: "Symbol defining a function" - }, AST_SymbolDeclaration); - var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { - $documentation: "Symbol naming a function expression" - }, AST_SymbolDeclaration); - var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { - $documentation: "Symbol naming the exception in catch" - }, AST_SymbolDeclaration); - var AST_Label = DEFNODE("Label", "references", { - $documentation: "Symbol naming a label (declaration)", - $propdoc: { - references: "[AST_LabelRef*] a list of nodes referring to this label" - } - }, AST_Symbol); - var AST_SymbolRef = DEFNODE("SymbolRef", null, { - $documentation: "Reference to some symbol (not definition/declaration)" - }, AST_Symbol); - var AST_LabelRef = DEFNODE("LabelRef", null, { - $documentation: "Reference to a label symbol" - }, AST_Symbol); - var AST_This = DEFNODE("This", null, { - $documentation: "The `this` symbol" - }, AST_Symbol); - var AST_Constant = DEFNODE("Constant", null, { - $documentation: "Base class for all constants", - getValue: function() { - return this.value; - } + + _(AST_If, function(self, tw){ + self.condition = self.condition.transform(tw); + self.body = self.body.transform(tw); + if (self.alternative) self.alternative = self.alternative.transform(tw); }); - var AST_String = DEFNODE("String", "value", { - $documentation: "A string literal", - $propdoc: { - value: "[string] the contents of this string" - } - }, AST_Constant); - var AST_Number = DEFNODE("Number", "value", { - $documentation: "A number literal", - $propdoc: { - value: "[number] the numeric value" - } - }, AST_Constant); - var AST_RegExp = DEFNODE("RegExp", "value", { - $documentation: "A regexp literal", - $propdoc: { - value: "[RegExp] the actual regexp" - } - }, AST_Constant); - var AST_Atom = DEFNODE("Atom", null, { - $documentation: "Base class for atoms" - }, AST_Constant); - var AST_Null = DEFNODE("Null", null, { - $documentation: "The `null` atom", - value: null - }, AST_Atom); - var AST_NaN = DEFNODE("NaN", null, { - $documentation: "The impossible value", - value: 0 / 0 - }, AST_Atom); - var AST_Undefined = DEFNODE("Undefined", null, { - $documentation: "The `undefined` value", - value: function() {}() - }, AST_Atom); - var AST_Hole = DEFNODE("Hole", null, { - $documentation: "A hole in an array", - value: function() {}() - }, AST_Atom); - var AST_Infinity = DEFNODE("Infinity", null, { - $documentation: "The `Infinity` value", - value: 1 / 0 - }, AST_Atom); - var AST_Boolean = DEFNODE("Boolean", null, { - $documentation: "Base class for booleans" - }, AST_Atom); - var AST_False = DEFNODE("False", null, { - $documentation: "The `false` atom", - value: false - }, AST_Boolean); - var AST_True = DEFNODE("True", null, { - $documentation: "The `true` atom", - value: true - }, AST_Boolean); - function TreeWalker(callback) { - this.visit = callback; - this.stack = []; - } - TreeWalker.prototype = { - _visit: function(node, descend) { - this.stack.push(node); - var ret = this.visit(node, descend ? function() { - descend.call(node); - } : noop); - if (!ret && descend) { - descend.call(node); - } - this.stack.pop(); - return ret; - }, - parent: function(n) { - return this.stack[this.stack.length - 2 - (n || 0)]; - }, - push: function(node) { - this.stack.push(node); - }, - pop: function() { - return this.stack.pop(); - }, - self: function() { - return this.stack[this.stack.length - 1]; - }, - find_parent: function(type) { - var stack = this.stack; - for (var i = stack.length; --i >= 0; ) { - var x = stack[i]; - if (x instanceof type) return x; - } - }, - has_directive: function(type) { - return this.find_parent(AST_Scope).has_directive(type); - }, - in_boolean_context: function() { - var stack = this.stack; - var i = stack.length, self = stack[--i]; - while (i > 0) { - var p = stack[--i]; - if (p instanceof AST_If && p.condition === self || p instanceof AST_Conditional && p.condition === self || p instanceof AST_DWLoop && p.condition === self || p instanceof AST_For && p.condition === self || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) { - return true; - } - if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||"))) return false; - self = p; - } - }, - loopcontrol_target: function(label) { - var stack = this.stack; - if (label) { - for (var i = stack.length; --i >= 0; ) { - var x = stack[i]; - if (x instanceof AST_LabeledStatement && x.label.name == label.name) { - return x.body; - } - } - } else { - for (var i = stack.length; --i >= 0; ) { - var x = stack[i]; - if (x instanceof AST_Switch || x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) return x; - } - } - } - }; - "use strict"; - var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with"; - var KEYWORDS_ATOM = "false null true"; - var RESERVED_WORDS = "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile" + " " + KEYWORDS_ATOM + " " + KEYWORDS; - var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case"; - KEYWORDS = makePredicate(KEYWORDS); - RESERVED_WORDS = makePredicate(RESERVED_WORDS); - KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION); - KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); - var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); - var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; - var RE_OCT_NUMBER = /^0[0-7]+$/; - var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; - var OPERATORS = makePredicate([ "in", "instanceof", "typeof", "new", "void", "delete", "++", "--", "+", "-", "!", "~", "&", "|", "^", "*", "/", "%", ">>", "<<", ">>>", "<", ">", "<=", ">=", "==", "===", "!=", "!==", "?", "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=", "&&", "||" ]); - var WHITESPACE_CHARS = makePredicate(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 = makePredicate(characters("[{(,.;:")); - var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); - var REGEXP_MODIFIERS = makePredicate(characters("gmsiy")); - var UNICODE = { - letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"), - non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"), - space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"), - connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]") - }; - function is_letter(code) { - return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 170 && UNICODE.letter.test(String.fromCharCode(code)); - } - function is_digit(code) { - return code >= 48 && code <= 57; - } - function is_alphanumeric_char(code) { - return is_digit(code) || is_letter(code); - } - function is_unicode_combining_mark(ch) { - return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch); - } - function is_unicode_connector_punctuation(ch) { - return UNICODE.connector_punctuation.test(ch); - } - function is_identifier(name) { - return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name); - } - function is_identifier_start(code) { - return code == 36 || code == 95 || is_letter(code); - } - function is_identifier_char(ch) { - var code = ch.charCodeAt(0); - return is_identifier_start(code) || is_digit(code) || code == 8204 || code == 8205 || is_unicode_combining_mark(ch) || is_unicode_connector_punctuation(ch); - } - function is_identifier_string(str) { - var i = str.length; - if (i == 0) return false; - if (is_digit(str.charCodeAt(0))) return false; - while (--i >= 0) { - if (!is_identifier_char(str.charAt(i))) return false; - } - return true; - } - function parse_js_number(num) { - if (RE_HEX_NUMBER.test(num)) { - return parseInt(num.substr(2), 16); - } else if (RE_OCT_NUMBER.test(num)) { - return parseInt(num.substr(1), 8); - } else if (RE_DEC_NUMBER.test(num)) { - return parseFloat(num); - } - } - function JS_Parse_Error(message, line, col, pos) { - this.message = message; - this.line = line; - this.col = col; - this.pos = pos; - this.stack = new Error().stack; - } - JS_Parse_Error.prototype.toString = function() { - return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; - }; - function js_error(message, filename, line, col, pos) { - throw new JS_Parse_Error(message, line, col, pos); - } - function is_token(token, type, val) { - return token.type == type && (val == null || token.value == val); - } - var EX_EOF = {}; - function tokenizer($TEXT, filename) { - var S = { - text: $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ""), - filename: filename, - pos: 0, - tokpos: 0, - line: 1, - tokline: 0, - col: 0, - tokcol: 0, - newline_before: false, - regex_allowed: false, - comments_before: [] - }; - function peek() { - return S.text.charAt(S.pos); - } - function next(signal_eof, in_string) { - var ch = S.text.charAt(S.pos++); - if (signal_eof && !ch) throw EX_EOF; - if (ch == "\n") { - S.newline_before = S.newline_before || !in_string; - ++S.line; - S.col = 0; - } else { - ++S.col; - } - return ch; - } - function find(what, signal_eof) { - var pos = S.text.indexOf(what, S.pos); - if (signal_eof && pos == -1) throw EX_EOF; - return pos; - } - function start_token() { - S.tokline = S.line; - S.tokcol = S.col; - S.tokpos = S.pos; - } - function token(type, value, is_comment) { - S.regex_allowed = type == "operator" && !UNARY_POSTFIX(value) || type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value) || type == "punc" && PUNC_BEFORE_EXPRESSION(value); - var ret = { - type: type, - value: value, - line: S.tokline, - col: S.tokcol, - pos: S.tokpos, - endpos: S.pos, - nlb: S.newline_before, - file: filename - }; - if (!is_comment) { - ret.comments_before = S.comments_before; - S.comments_before = []; - for (var i = 0, len = ret.comments_before.length; i < len; i++) { - ret.nlb = ret.nlb || ret.comments_before[i].nlb; - } - } - S.newline_before = false; - return new AST_Token(ret); - } - function skip_whitespace() { - while (WHITESPACE_CHARS(peek())) next(); - } - function read_while(pred) { - var ret = "", ch, i = 0; - while ((ch = peek()) && pred(ch, i++)) ret += next(); - return ret; - } - function parse_error(err) { - js_error(err, filename, S.tokline, S.tokcol, S.tokpos); - } - function read_num(prefix) { - var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; - var num = read_while(function(ch, i) { - var code = ch.charCodeAt(0); - switch (code) { - case 120: - case 88: - return has_x ? false : has_x = true; - case 101: - case 69: - return has_x ? true : has_e ? false : has_e = after_e = true; + _(AST_Switch, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = do_list(self.body, tw); + }); - case 45: - return after_e || i == 0 && !prefix; + _(AST_Case, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = do_list(self.body, tw); + }); - case 43: - return after_e; + _(AST_Try, function(self, tw){ + self.body = do_list(self.body, tw); + if (self.bcatch) self.bcatch = self.bcatch.transform(tw); + if (self.bfinally) self.bfinally = self.bfinally.transform(tw); + }); - case after_e = false, 46: - return !has_dot && !has_x && !has_e ? has_dot = true : false; - } - return is_alphanumeric_char(code); - }); - if (prefix) num = prefix + num; - var valid = parse_js_number(num); - if (!isNaN(valid)) { - return token("num", valid); - } else { - parse_error("Invalid syntax: " + num); - } - } - function read_escaped_char(in_string) { - var ch = next(true, in_string); - switch (ch.charCodeAt(0)) { - case 110: - return "\n"; + _(AST_Catch, function(self, tw){ + self.argname = self.argname.transform(tw); + self.body = do_list(self.body, tw); + }); - case 114: - return "\r"; + _(AST_Definitions, function(self, tw){ + self.definitions = do_list(self.definitions, tw); + }); - case 116: - return " "; + _(AST_VarDef, function(self, tw){ + self.name = self.name.transform(tw); + if (self.value) self.value = self.value.transform(tw); + }); - case 98: - return "\b"; + _(AST_Lambda, function(self, tw){ + if (self.name) self.name = self.name.transform(tw); + self.argnames = do_list(self.argnames, tw); + self.body = do_list(self.body, tw); + }); - case 118: - return " "; + _(AST_Call, function(self, tw){ + self.expression = self.expression.transform(tw); + self.args = do_list(self.args, tw); + }); - case 102: - return "\f"; + _(AST_Seq, function(self, tw){ + self.car = self.car.transform(tw); + self.cdr = self.cdr.transform(tw); + }); - case 48: - return "\x00"; + _(AST_Dot, function(self, tw){ + self.expression = self.expression.transform(tw); + }); - case 120: - return String.fromCharCode(hex_bytes(2)); + _(AST_Sub, function(self, tw){ + self.expression = self.expression.transform(tw); + self.property = self.property.transform(tw); + }); - case 117: - return String.fromCharCode(hex_bytes(4)); + _(AST_Unary, function(self, tw){ + self.expression = self.expression.transform(tw); + }); - case 10: - return ""; + _(AST_Binary, function(self, tw){ + self.left = self.left.transform(tw); + self.right = self.right.transform(tw); + }); - default: - return ch; - } - } - function hex_bytes(n) { - var num = 0; - for (;n > 0; --n) { - var digit = parseInt(next(true), 16); - if (isNaN(digit)) parse_error("Invalid hex-character pattern in string"); - num = num << 4 | digit; - } - return num; - } - var read_string = with_eof_error("Unterminated string constant", function() { - var quote = next(), ret = ""; - for (;;) { - var ch = next(true); - if (ch == "\\") { - var octal_len = 0, first = null; - ch = read_while(function(ch) { - if (ch >= "0" && ch <= "7") { - if (!first) { - first = ch; - return ++octal_len; - } else if (first <= "3" && octal_len <= 2) return ++octal_len; else if (first >= "4" && octal_len <= 1) return ++octal_len; - } - return false; - }); - if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8)); else ch = read_escaped_char(true); - } else if (ch == quote) break; - ret += ch; - } - return token("string", ret); - }); - function read_line_comment() { - next(); - var i = find("\n"), ret; - if (i == -1) { - ret = S.text.substr(S.pos); - S.pos = S.text.length; - } else { - ret = S.text.substring(S.pos, i); - S.pos = i; - } - return token("comment1", ret, true); - } - var read_multiline_comment = with_eof_error("Unterminated multiline comment", function() { - next(); - var i = find("*/", true); - var text = S.text.substring(S.pos, i); - var a = text.split("\n"), n = a.length; - S.pos = i + 2; - S.line += n - 1; - if (n > 1) S.col = a[n - 1].length; else S.col += a[n - 1].length; - S.col += 2; - S.newline_before = S.newline_before || text.indexOf("\n") >= 0; - return token("comment2", text, true); - }); - function read_name() { - var backslash = false, name = "", ch, escaped = false, hex; - while ((ch = peek()) != null) { - if (!backslash) { - 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"); - ch = read_escaped_char(); - if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); - name += ch; - backslash = false; - } - } - if (KEYWORDS(name) && escaped) { - hex = name.charCodeAt(0).toString(16).toUpperCase(); - name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1); - } - return name; - } - var read_regexp = with_eof_error("Unterminated regular expression", function(regexp) { - var prev_backslash = false, ch, in_class = false; - while (ch = next(true)) if (prev_backslash) { - regexp += "\\" + ch; - prev_backslash = false; - } else if (ch == "[") { - in_class = true; - regexp += ch; - } else if (ch == "]" && in_class) { - in_class = false; - regexp += ch; - } else if (ch == "/" && !in_class) { - break; - } else if (ch == "\\") { - prev_backslash = true; - } else { - regexp += ch; - } - var mods = read_name(); - return token("regexp", new RegExp(regexp, mods)); - }); - function read_operator(prefix) { - function grow(op) { - if (!peek()) return op; - var bigger = op + peek(); - if (OPERATORS(bigger)) { - next(); - return grow(bigger); - } else { - return op; - } - } - return token("operator", grow(prefix || next())); - } - function handle_slash() { - next(); - var regex_allowed = S.regex_allowed; - switch (peek()) { - case "/": - S.comments_before.push(read_line_comment()); - S.regex_allowed = regex_allowed; - return next_token(); + _(AST_Conditional, function(self, tw){ + self.condition = self.condition.transform(tw); + self.consequent = self.consequent.transform(tw); + self.alternative = self.alternative.transform(tw); + }); + + _(AST_Array, function(self, tw){ + self.elements = do_list(self.elements, tw); + }); + + _(AST_Object, function(self, tw){ + self.properties = do_list(self.properties, tw); + }); + + _(AST_ObjectProperty, function(self, tw){ + self.value = self.value.transform(tw); + }); + +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. - case "*": - S.comments_before.push(read_multiline_comment()); - S.regex_allowed = regex_allowed; - return next_token(); - } - return S.regex_allowed ? read_regexp("") : read_operator("/"); - } - function handle_dot() { - next(); - return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", "."); - } - function read_word() { - var word = read_name(); - return KEYWORDS_ATOM(word) ? token("atom", word) : !KEYWORDS(word) ? token("name", word) : OPERATORS(word) ? token("operator", word) : token("keyword", word); - } - function with_eof_error(eof_error, cont) { - return function(x) { - try { - return cont(x); - } catch (ex) { - if (ex === EX_EOF) parse_error(eof_error); else throw ex; - } - }; - } - function next_token(force_regexp) { - if (force_regexp != null) return read_regexp(force_regexp); - skip_whitespace(); - start_token(); - var ch = peek(); - if (!ch) return token("eof"); - var code = ch.charCodeAt(0); - switch (code) { - case 34: - case 39: - return read_string(); + ***********************************************************************/ - case 46: - return handle_dot(); +"use strict"; + +function SymbolDef(scope, index, orig) { + this.name = orig.name; + this.orig = [ orig ]; + this.scope = scope; + this.references = []; + this.global = false; + this.mangled_name = null; + this.undeclared = false; + this.constant = false; + this.index = index; +}; - case 47: - return handle_slash(); - } - if (is_digit(code)) return read_num(); - if (PUNC_CHARS(ch)) return token("punc", next()); - if (OPERATOR_CHARS(ch)) return read_operator(); - if (code == 92 || is_identifier_start(code)) return read_word(); - parse_error("Unexpected character '" + ch + "'"); +SymbolDef.prototype = { + unmangleable: function(options) { + return (this.global && !(options && options.toplevel)) + || this.undeclared + || (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with)); + }, + mangle: function(options) { + if (!this.mangled_name && !this.unmangleable(options)) { + var s = this.scope; + if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda) + s = s.parent_scope; + this.mangled_name = s.next_mangled(options, this); } - next_token.context = function(nc) { - if (nc) S = nc; - return S; - }; - return next_token; } - var UNARY_PREFIX = makePredicate([ "typeof", "void", "delete", "--", "++", "!", "~", "-", "+" ]); - var UNARY_POSTFIX = makePredicate([ "--", "++" ]); - var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); - var PRECEDENCE = function(a, ret) { - for (var i = 0, n = 1; i < a.length; ++i, ++n) { - var b = a[i]; - for (var j = 0; j < b.length; ++j) { - ret[b[j]] = n; - } - } - return ret; - }([ [ "||" ], [ "&&" ], [ "|" ], [ "^" ], [ "&" ], [ "==", "===", "!=", "!==" ], [ "<", ">", "<=", ">=", "in", "instanceof" ], [ ">>", "<<", ">>>" ], [ "+", "-" ], [ "*", "/", "%" ] ], {}); - var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); - var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); - function parse($TEXT, options) { - options = defaults(options, { - strict: false, - filename: null, - toplevel: null, - expression: false - }); - var S = { - input: typeof $TEXT == "string" ? tokenizer($TEXT, options.filename) : $TEXT, - token: null, - prev: null, - peeked: null, - in_function: 0, - in_directives: true, - in_loop: 0, - labels: [] - }; - S.token = next(); - function is(type, value) { - return is_token(S.token, type, value); - } - function peek() { - return S.peeked || (S.peeked = S.input()); - } - function next() { - S.prev = S.token; - if (S.peeked) { - S.token = S.peeked; - S.peeked = null; - } else { - S.token = S.input(); - } - S.in_directives = S.in_directives && (S.token.type == "string" || is("punc", ";")); - return S.token; - } - function prev() { - return S.prev; +}; + +AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ + options = defaults(options, { + screw_ie8: false + }); + + // pass 1: setup scope chaining and handle definitions + var self = this; + var scope = self.parent_scope = null; + var defun = null; + var nesting = 0; + var tw = new TreeWalker(function(node, descend){ + if (options.screw_ie8 && node instanceof AST_Catch) { + var save_scope = scope; + scope = new AST_Scope(node); + scope.init_scope_vars(nesting); + scope.parent_scope = save_scope; + descend(); + scope = save_scope; + return true; } - function croak(msg, line, col, pos) { - var ctx = S.input.context(); - js_error(msg, ctx.filename, line != null ? line : ctx.tokline, col != null ? col : ctx.tokcol, pos != null ? pos : ctx.tokpos); + if (node instanceof AST_Scope) { + node.init_scope_vars(nesting); + var save_scope = node.parent_scope = scope; + var save_defun = defun; + defun = scope = node; + ++nesting; descend(); --nesting; + scope = save_scope; + defun = save_defun; + return true; // don't descend again in TreeWalker + } + if (node instanceof AST_Directive) { + node.scope = scope; + push_uniq(scope.directives, node.value); + return true; } - function token_error(token, msg) { - croak(msg, token.line, token.col); + if (node instanceof AST_With) { + for (var s = scope; s; s = s.parent_scope) + s.uses_with = true; + return; } - function unexpected(token) { - if (token == null) token = S.token; - token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); + if (node instanceof AST_Symbol) { + node.scope = scope; } - function expect_token(type, val) { - if (is(type, val)) { - return next(); - } - token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); + if (node instanceof AST_SymbolLambda) { + defun.def_function(node); } - function expect(punc) { - return expect_token("punc", punc); + else if (node instanceof AST_SymbolDefun) { + // Careful here, the scope where this should be defined is + // the parent scope. The reason is that we enter a new + // scope when we encounter the AST_Defun node (which is + // instanceof AST_Scope) but we get to the symbol a bit + // later. + (node.scope = defun.parent_scope).def_function(node); } - function can_insert_semicolon() { - return !options.strict && (S.token.nlb || is("eof") || is("punc", "}")); + else if (node instanceof AST_SymbolVar + || node instanceof AST_SymbolConst) { + var def = defun.def_variable(node); + def.constant = node instanceof AST_SymbolConst; + def.init = tw.parent().value; } - function semicolon() { - if (is("punc", ";")) next(); else if (!can_insert_semicolon()) unexpected(); + else if (node instanceof AST_SymbolCatch) { + (options.screw_ie8 ? scope : defun) + .def_variable(node); } - function parenthesised() { - expect("("); - var exp = expression(true); - expect(")"); - return exp; - } - function embed_tokens(parser) { - return function() { - var start = S.token; - var expr = parser(); - var end = prev(); - expr.start = start; - expr.end = end; - return expr; - }; + }); + self.walk(tw); + + // pass 2: find back references and eval + var func = null; + var globals = self.globals = new Dictionary(); + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_Lambda) { + var prev_func = func; + func = node; + descend(); + func = prev_func; + return true; } - var statement = embed_tokens(function() { - var tmp; - if (is("operator", "/") || is("operator", "/=")) { - S.peeked = null; - S.token = S.input(S.token.value.substr(1)); + if (node instanceof AST_SymbolRef) { + var name = node.name; + var sym = node.scope.find_variable(name); + if (!sym) { + var g; + if (globals.has(name)) { + g = globals.get(name); + } else { + g = new SymbolDef(self, globals.size(), node); + g.undeclared = true; + g.global = true; + globals.set(name, g); + } + node.thedef = g; + if (name == "eval" && tw.parent() instanceof AST_Call) { + for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) + s.uses_eval = true; + } + if (func && name == "arguments") { + func.uses_arguments = true; + } + } else { + node.thedef = sym; } - switch (S.token.type) { - case "string": - var dir = S.in_directives, stat = simple_statement(); - if (dir && stat.body instanceof AST_String && !is("punc", ",")) return new AST_Directive({ - value: stat.body.value - }); - return stat; + node.reference(); + return true; + } + }); + self.walk(tw); +}); - case "num": - case "regexp": - case "operator": - case "atom": - return simple_statement(); +AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ + this.directives = []; // contains the directives defined in this scope, i.e. "use strict" + this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) + this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) + this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement + this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` + this.parent_scope = null; // the parent scope + this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes + this.cname = -1; // the current index for mangling functions/variables + this.nesting = nesting; // the nesting level of this scope (0 means toplevel) +}); - case "name": - return is_token(peek(), "punc", ":") ? labeled_statement() : simple_statement(); +AST_Scope.DEFMETHOD("strict", function(){ + return this.has_directive("use strict"); +}); - case "punc": - switch (S.token.value) { - case "{": - return new AST_BlockStatement({ - start: S.token, - body: block_(), - end: prev() - }); +AST_Lambda.DEFMETHOD("init_scope_vars", function(){ + AST_Scope.prototype.init_scope_vars.apply(this, arguments); + this.uses_arguments = false; +}); - case "[": - case "(": - return simple_statement(); +AST_SymbolRef.DEFMETHOD("reference", function() { + var def = this.definition(); + def.references.push(this); + var s = this.scope; + while (s) { + push_uniq(s.enclosed, def); + if (s === def.scope) break; + s = s.parent_scope; + } + this.frame = this.scope.nesting - def.scope.nesting; +}); - case ";": - next(); - return new AST_EmptyStatement(); +AST_Scope.DEFMETHOD("find_variable", function(name){ + if (name instanceof AST_Symbol) name = name.name; + return this.variables.get(name) + || (this.parent_scope && this.parent_scope.find_variable(name)); +}); - default: - unexpected(); - } +AST_Scope.DEFMETHOD("has_directive", function(value){ + return this.parent_scope && this.parent_scope.has_directive(value) + || (this.directives.indexOf(value) >= 0 ? this : null); +}); + +AST_Scope.DEFMETHOD("def_function", function(symbol){ + this.functions.set(symbol.name, this.def_variable(symbol)); +}); - case "keyword": - switch (tmp = S.token.value, next(), tmp) { - case "break": - return break_cont(AST_Break); +AST_Scope.DEFMETHOD("def_variable", function(symbol){ + var def; + if (!this.variables.has(symbol.name)) { + def = new SymbolDef(this, this.variables.size(), symbol); + this.variables.set(symbol.name, def); + def.global = !this.parent_scope; + } else { + def = this.variables.get(symbol.name); + def.orig.push(symbol); + } + return symbol.thedef = def; +}); - case "continue": - return break_cont(AST_Continue); +AST_Scope.DEFMETHOD("next_mangled", function(options){ + var ext = this.enclosed; + out: while (true) { + var m = base54(++this.cname); + if (!is_identifier(m)) continue; // skip over "do" - case "debugger": - semicolon(); - return new AST_Debugger(); + // https://github.com/mishoo/UglifyJS2/issues/242 -- do not + // shadow a name excepted from mangling. + if (options.except.indexOf(m) >= 0) continue; - case "do": - return new AST_Do({ - body: in_loop(statement), - condition: (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), - tmp) - }); + // we must ensure that the mangled name does not shadow a name + // from some parent scope that is referenced in this or in + // inner scopes. + for (var i = ext.length; --i >= 0;) { + var sym = ext[i]; + var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); + if (m == name) continue out; + } + return m; + } +}); - case "while": - return new AST_While({ - condition: parenthesised(), - body: in_loop(statement) - }); +AST_Function.DEFMETHOD("next_mangled", function(options, def){ + // #179, #326 + // in Safari strict mode, something like (function x(x){...}) is a syntax error; + // a function expression's argument cannot shadow the function expression's name - case "for": - return for_(); + var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); + while (true) { + var name = AST_Lambda.prototype.next_mangled.call(this, options, def); + if (!(tricky_def && tricky_def.mangled_name == name)) + return name; + } +}); - case "function": - return function_(true); +AST_Scope.DEFMETHOD("references", function(sym){ + if (sym instanceof AST_Symbol) sym = sym.definition(); + return this.enclosed.indexOf(sym) < 0 ? null : sym; +}); - case "if": - return if_(); +AST_Symbol.DEFMETHOD("unmangleable", function(options){ + return this.definition().unmangleable(options); +}); - case "return": - if (S.in_function == 0) croak("'return' outside of function"); - return new AST_Return({ - value: is("punc", ";") ? (next(), null) : can_insert_semicolon() ? null : (tmp = expression(true), - semicolon(), tmp) - }); +// property accessors are not mangleable +AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){ + return true; +}); - case "switch": - return new AST_Switch({ - expression: parenthesised(), - body: in_loop(switch_body_) - }); +// labels are always mangleable +AST_Label.DEFMETHOD("unmangleable", function(){ + return false; +}); - case "throw": - if (S.token.nlb) croak("Illegal newline after 'throw'"); - return new AST_Throw({ - value: (tmp = expression(true), semicolon(), tmp) - }); +AST_Symbol.DEFMETHOD("unreferenced", function(){ + return this.definition().references.length == 0 + && !(this.scope.uses_eval || this.scope.uses_with); +}); - case "try": - return try_(); +AST_Symbol.DEFMETHOD("undeclared", function(){ + return this.definition().undeclared; +}); + +AST_LabelRef.DEFMETHOD("undeclared", function(){ + return false; +}); - case "var": - return tmp = var_(), semicolon(), tmp; +AST_Label.DEFMETHOD("undeclared", function(){ + return false; +}); - case "const": - return tmp = const_(), semicolon(), tmp; +AST_Symbol.DEFMETHOD("definition", function(){ + return this.thedef; +}); - case "with": - return new AST_With({ - expression: parenthesised(), - body: statement() - }); +AST_Symbol.DEFMETHOD("global", function(){ + return this.definition().global; +}); - default: - unexpected(); +AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ + return defaults(options, { + except : [], + eval : false, + sort : false, + toplevel : false, + screw_ie8 : false + }); +}); + +AST_Toplevel.DEFMETHOD("mangle_names", function(options){ + options = this._default_mangler_options(options); + // We only need to mangle declaration nodes. Special logic wired + // into the code generator will display the mangled name if it's + // present (and for AST_SymbolRef-s it'll use the mangled name of + // the AST_SymbolDeclaration that it points to). + var lname = -1; + var to_mangle = []; + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_LabeledStatement) { + // lname is incremented when we get to the AST_Label + var save_nesting = lname; + descend(); + lname = save_nesting; + return true; // don't descend again in TreeWalker + } + if (node instanceof AST_Scope) { + var p = tw.parent(), a = []; + node.variables.each(function(symbol){ + if (options.except.indexOf(symbol.name) < 0) { + a.push(symbol); } - } - }); - function labeled_statement() { - var label = as_symbol(AST_Label); - if (find_if(function(l) { - return l.name == label.name; - }, S.labels)) { - croak("Label " + label.name + " defined twice"); - } - expect(":"); - S.labels.push(label); - var stat = statement(); - S.labels.pop(); - return new AST_LabeledStatement({ - body: stat, - label: label }); - } - function simple_statement(tmp) { - return new AST_SimpleStatement({ - body: (tmp = expression(true), semicolon(), tmp) + if (options.sort) a.sort(function(a, b){ + return b.references.length - a.references.length; }); + to_mangle.push.apply(to_mangle, a); + return; } - function break_cont(type) { - var label = null; - if (!can_insert_semicolon()) { - label = as_symbol(AST_LabelRef, true); - } - if (label != null) { - if (!find_if(function(l) { - return l.name == label.name; - }, S.labels)) croak("Undefined label " + label.name); - } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch"); - semicolon(); - return new type({ - label: label - }); + if (node instanceof AST_Label) { + var name; + do name = base54(++lname); while (!is_identifier(name)); + node.mangled_name = name; + return true; } - function for_() { - expect("("); - var init = null; - if (!is("punc", ";")) { - init = is("keyword", "var") ? (next(), var_(true)) : expression(true, true); - if (is("operator", "in")) { - if (init instanceof AST_Var && init.definitions.length > 1) croak("Only one variable declaration allowed in for..in loop"); - next(); - return for_in(init); - } - } - return regular_for(init); - } - function regular_for(init) { - expect(";"); - var test = is("punc", ";") ? null : expression(true); - expect(";"); - var step = is("punc", ")") ? null : expression(true); - expect(")"); - return new AST_For({ - init: init, - condition: test, - step: step, - body: in_loop(statement) - }); + if (options.screw_ie8 && node instanceof AST_SymbolCatch) { + to_mangle.push(node.definition()); + return; } - function for_in(init) { - var lhs = init instanceof AST_Var ? init.definitions[0].name : null; - var obj = expression(true); - expect(")"); - return new AST_ForIn({ - init: init, - name: lhs, - object: obj, - body: in_loop(statement) - }); + }); + this.walk(tw); + to_mangle.forEach(function(def){ def.mangle(options) }); +}); + +AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ + options = this._default_mangler_options(options); + var tw = new TreeWalker(function(node){ + if (node instanceof AST_Constant) + base54.consider(node.print_to_string()); + else if (node instanceof AST_Return) + base54.consider("return"); + else if (node instanceof AST_Throw) + base54.consider("throw"); + else if (node instanceof AST_Continue) + base54.consider("continue"); + else if (node instanceof AST_Break) + base54.consider("break"); + else if (node instanceof AST_Debugger) + base54.consider("debugger"); + else if (node instanceof AST_Directive) + base54.consider(node.value); + else if (node instanceof AST_While) + base54.consider("while"); + else if (node instanceof AST_Do) + base54.consider("do while"); + else if (node instanceof AST_If) { + base54.consider("if"); + if (node.alternative) base54.consider("else"); + } + else if (node instanceof AST_Var) + base54.consider("var"); + else if (node instanceof AST_Const) + base54.consider("const"); + else if (node instanceof AST_Lambda) + base54.consider("function"); + else if (node instanceof AST_For) + base54.consider("for"); + else if (node instanceof AST_ForIn) + base54.consider("for in"); + else if (node instanceof AST_Switch) + base54.consider("switch"); + else if (node instanceof AST_Case) + base54.consider("case"); + else if (node instanceof AST_Default) + base54.consider("default"); + else if (node instanceof AST_With) + base54.consider("with"); + else if (node instanceof AST_ObjectSetter) + base54.consider("set" + node.key); + else if (node instanceof AST_ObjectGetter) + base54.consider("get" + node.key); + else if (node instanceof AST_ObjectKeyVal) + base54.consider(node.key); + else if (node instanceof AST_New) + base54.consider("new"); + else if (node instanceof AST_This) + base54.consider("this"); + else if (node instanceof AST_Try) + base54.consider("try"); + else if (node instanceof AST_Catch) + base54.consider("catch"); + else if (node instanceof AST_Finally) + base54.consider("finally"); + else if (node instanceof AST_Symbol && node.unmangleable(options)) + base54.consider(node.name); + else if (node instanceof AST_Unary || node instanceof AST_Binary) + base54.consider(node.operator); + else if (node instanceof AST_Dot) + base54.consider(node.property); + }); + this.walk(tw); + base54.sort(); +}); + +var base54 = (function() { + var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; + var chars, frequency; + function reset() { + frequency = Object.create(null); + chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); + chars.forEach(function(ch){ frequency[ch] = 0 }); + } + base54.consider = function(str){ + for (var i = str.length; --i >= 0;) { + var code = str.charCodeAt(i); + if (code in frequency) ++frequency[code]; } - var function_ = function(in_statement, ctor) { - var is_accessor = ctor === AST_Accessor; - var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : is_accessor ? AST_SymbolAccessor : AST_SymbolLambda) : is_accessor && (is("string") || is("num")) ? as_atom_node() : null; - if (in_statement && !name) unexpected(); - expect("("); - if (!ctor) ctor = in_statement ? AST_Defun : AST_Function; - return new ctor({ - name: name, - argnames: function(first, a) { - while (!is("punc", ")")) { - if (first) first = false; else expect(","); - a.push(as_symbol(AST_SymbolFunarg)); - } - next(); - return a; - }(true, []), - body: function(loop, labels) { - ++S.in_function; - S.in_directives = true; - S.in_loop = 0; - S.labels = []; - var a = block_(); - --S.in_function; - S.in_loop = loop; - S.labels = labels; - return a; - }(S.in_loop, S.labels) - }); - }; - function if_() { - var cond = parenthesised(), body = statement(), belse = null; - if (is("keyword", "else")) { - next(); - belse = statement(); - } - return new AST_If({ - condition: cond, - body: body, - alternative: belse + }; + base54.sort = function() { + chars = mergeSort(chars, function(a, b){ + if (is_digit(a) && !is_digit(b)) return 1; + if (is_digit(b) && !is_digit(a)) return -1; + return frequency[b] - frequency[a]; + }); + }; + base54.reset = reset; + reset(); + base54.get = function(){ return chars }; + base54.freq = function(){ return frequency }; + function base54(num) { + var ret = "", base = 54; + do { + ret += String.fromCharCode(chars[num % base]); + num = Math.floor(num / base); + base = 64; + } while (num > 0); + return ret; + }; + return base54; +})(); + +AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ + options = defaults(options, { + undeclared : false, // this makes a lot of noise + unreferenced : true, + assign_to_global : true, + func_arguments : true, + nested_defuns : true, + eval : true + }); + var tw = new TreeWalker(function(node){ + if (options.undeclared + && node instanceof AST_SymbolRef + && node.undeclared()) + { + // XXX: this also warns about JS standard names, + // i.e. Object, Array, parseInt etc. Should add a list of + // exceptions. + AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { + name: node.name, + file: node.start.file, + line: node.start.line, + col: node.start.col }); } - function block_() { - expect("{"); - var a = []; - while (!is("punc", "}")) { - if (is("eof")) unexpected(); - a.push(statement()); - } - next(); - return a; - } - function switch_body_() { - expect("{"); - var a = [], cur = null, branch = null, tmp; - while (!is("punc", "}")) { - if (is("eof")) unexpected(); - if (is("keyword", "case")) { - if (branch) branch.end = prev(); - cur = []; - branch = new AST_Case({ - start: (tmp = S.token, next(), tmp), - expression: expression(true), - body: cur - }); - a.push(branch); - expect(":"); - } else if (is("keyword", "default")) { - if (branch) branch.end = prev(); - cur = []; - branch = new AST_Default({ - start: (tmp = S.token, next(), expect(":"), tmp), - body: cur - }); - a.push(branch); - } else { - if (!cur) unexpected(); - cur.push(statement()); - } - } - if (branch) branch.end = prev(); - next(); - return a; - } - function try_() { - var body = block_(), bcatch = null, bfinally = null; - if (is("keyword", "catch")) { - var start = S.token; - next(); - expect("("); - var name = as_symbol(AST_SymbolCatch); - expect(")"); - bcatch = new AST_Catch({ - start: start, - argname: name, - body: block_(), - end: prev() + if (options.assign_to_global) + { + var sym = null; + if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) + sym = node.left; + else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) + sym = node.init; + if (sym + && (sym.undeclared() + || (sym.global() && sym.scope !== sym.definition().scope))) { + AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { + msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", + name: sym.name, + file: sym.start.file, + line: sym.start.line, + col: sym.start.col }); } - if (is("keyword", "finally")) { - var start = S.token; - next(); - bfinally = new AST_Finally({ - start: start, - body: block_(), - end: prev() - }); - } - if (!bcatch && !bfinally) croak("Missing catch/finally blocks"); - return new AST_Try({ - body: body, - bcatch: bcatch, - bfinally: bfinally - }); } - function vardefs(no_in, in_const) { - var a = []; - for (;;) { - a.push(new AST_VarDef({ - start: S.token, - name: as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), - value: is("operator", "=") ? (next(), expression(false, no_in)) : null, - end: prev() - })); - if (!is("punc", ",")) break; - next(); - } - return a; + if (options.eval + && node instanceof AST_SymbolRef + && node.undeclared() + && node.name == "eval") { + AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); + } + if (options.unreferenced + && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) + && node.unreferenced()) { + AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { + type: node instanceof AST_Label ? "Label" : "Symbol", + name: node.name, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); } - var var_ = function(no_in) { - return new AST_Var({ - start: prev(), - definitions: vardefs(no_in, false), - end: prev() + if (options.func_arguments + && node instanceof AST_Lambda + && node.uses_arguments) { + AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { + name: node.name ? node.name.name : "anonymous", + file: node.start.file, + line: node.start.line, + col: node.start.col }); - }; - var const_ = function() { - return new AST_Const({ - start: prev(), - definitions: vardefs(false, true), - end: prev() + } + if (options.nested_defuns + && node instanceof AST_Defun + && !(tw.parent() instanceof AST_Scope)) { + AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", { + name: node.name.name, + type: tw.parent().TYPE, + file: node.start.file, + line: node.start.line, + col: node.start.col }); - }; - var new_ = function() { - var start = S.token; - expect_token("operator", "new"); - var newexp = expr_atom(false), args; - if (is("punc", "(")) { - next(); - args = expr_list(")"); + } + }); + this.walk(tw); +}); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. + + ***********************************************************************/ + +"use strict"; + +function OutputStream(options) { + + options = defaults(options, { + indent_start : 0, + indent_level : 4, + quote_keys : false, + space_colon : true, + ascii_only : false, + unescape_regexps : false, + inline_script : false, + width : 80, + max_line_len : 32000, + beautify : false, + source_map : null, + bracketize : false, + semicolons : true, + comments : false, + preserve_line : false, + screw_ie8 : false, + preamble : null, + }, true); + + var indentation = 0; + var current_col = 0; + var current_line = 1; + var current_pos = 0; + var OUTPUT = ""; + + function to_ascii(str, identifier) { + return str.replace(/[\u0080-\uffff]/g, function(ch) { + var code = ch.charCodeAt(0).toString(16); + if (code.length <= 2 && !identifier) { + while (code.length < 2) code = "0" + code; + return "\\x" + code; } else { - args = []; + while (code.length < 4) code = "0" + code; + return "\\u" + code; } - return subscripts(new AST_New({ - start: start, - expression: newexp, - args: args, - end: prev() - }), true); - }; - function as_atom_node() { - var tok = S.token, ret; - switch (tok.type) { - case "name": - return as_symbol(AST_SymbolRef); - - case "num": - ret = new AST_Number({ - start: tok, - end: tok, - value: tok.value - }); - break; + }); + }; - case "string": - ret = new AST_String({ - start: tok, - end: tok, - value: tok.value - }); - break; + function make_string(str) { + var dq = 0, sq = 0; + str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){ + switch (s) { + case "\\": return "\\\\"; + case "\b": return "\\b"; + case "\f": return "\\f"; + case "\n": return "\\n"; + case "\r": return "\\r"; + case "\u2028": return "\\u2028"; + case "\u2029": return "\\u2029"; + case '"': ++dq; return '"'; + case "'": ++sq; return "'"; + case "\0": return "\\x00"; + } + return s; + }); + if (options.ascii_only) str = to_ascii(str); + if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; + else return '"' + str.replace(/\x22/g, '\\"') + '"'; + }; - case "regexp": - ret = new AST_RegExp({ - start: tok, - end: tok, - value: tok.value - }); - break; + function encode_string(str) { + var ret = make_string(str); + if (options.inline_script) + ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); + return ret; + }; + + function make_name(name) { + name = name.toString(); + if (options.ascii_only) + name = to_ascii(name, true); + return name; + }; + + function make_indent(back) { + return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); + }; + + /* -----[ beautification/minification ]----- */ + + var might_need_space = false; + var might_need_semicolon = false; + var last = null; + + function last_char() { + return last.charAt(last.length - 1); + }; - case "atom": - switch (tok.value) { - case "false": - ret = new AST_False({ - start: tok, - end: tok - }); - break; + function maybe_newline() { + if (options.max_line_len && current_col > options.max_line_len) + print("\n"); + }; - case "true": - ret = new AST_True({ - start: tok, - end: tok - }); - break; + var requireSemicolonChars = makePredicate("( [ + * / - , ."); - case "null": - ret = new AST_Null({ - start: tok, - end: tok - }); - break; - } - break; - } - next(); - return ret; - } - var expr_atom = function(allow_calls) { - if (is("operator", "new")) { - return new_(); - } - var start = S.token; - if (is("punc")) { - switch (start.value) { - case "(": - next(); - var ex = expression(true); - ex.start = start; - ex.end = S.token; - expect(")"); - return subscripts(ex, allow_calls); - - case "[": - return subscripts(array_(), allow_calls); - - case "{": - return subscripts(object_(), allow_calls); - } - unexpected(); - } - if (is("keyword", "function")) { - next(); - var func = function_(false); - func.start = start; - func.end = prev(); - return subscripts(func, allow_calls); - } - if (ATOMIC_START_TOKEN[S.token.type]) { - return subscripts(as_atom_node(), allow_calls); - } - unexpected(); - }; - function expr_list(closing, allow_trailing_comma, allow_empty) { - var first = true, a = []; - while (!is("punc", closing)) { - if (first) first = false; else expect(","); - if (allow_trailing_comma && is("punc", closing)) break; - if (is("punc", ",") && allow_empty) { - a.push(new AST_Hole({ - start: S.token, - end: S.token - })); + function print(str) { + str = String(str); + var ch = str.charAt(0); + if (might_need_semicolon) { + if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) { + if (options.semicolons || requireSemicolonChars(ch)) { + OUTPUT += ";"; + current_col++; + current_pos++; } else { - a.push(expression(false)); + OUTPUT += "\n"; + current_pos++; + current_line++; + current_col = 0; } + if (!options.beautify) + might_need_space = false; } - next(); - return a; + might_need_semicolon = false; + maybe_newline(); } - var array_ = embed_tokens(function() { - expect("["); - return new AST_Array({ - elements: expr_list("]", !options.strict, true) - }); - }); - var object_ = embed_tokens(function() { - expect("{"); - var first = true, a = []; - while (!is("punc", "}")) { - if (first) first = false; else expect(","); - if (!options.strict && is("punc", "}")) break; - var start = S.token; - var type = start.type; - var name = as_property_name(); - if (type == "name" && !is("punc", ":")) { - if (name == "get") { - a.push(new AST_ObjectGetter({ - start: start, - key: name, - value: function_(false, AST_Accessor), - end: prev() - })); - continue; - } - if (name == "set") { - a.push(new AST_ObjectSetter({ - start: start, - key: name, - value: function_(false, AST_Accessor), - end: prev() - })); - continue; - } - } - expect(":"); - a.push(new AST_ObjectKeyVal({ - start: start, - key: name, - value: expression(false), - end: prev() - })); - } - next(); - return new AST_Object({ - properties: a - }); - }); - function as_property_name() { - var tmp = S.token; - next(); - switch (tmp.type) { - case "num": - case "string": - case "name": - case "operator": - case "keyword": - case "atom": - return tmp.value; - default: - unexpected(); + if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { + var target_line = stack[stack.length - 1].start.line; + while (current_line < target_line) { + OUTPUT += "\n"; + current_pos++; + current_line++; + current_col = 0; + might_need_space = false; } } - function as_name() { - var tmp = S.token; - next(); - switch (tmp.type) { - case "name": - case "operator": - case "keyword": - case "atom": - return tmp.value; - default: - unexpected(); - } - } - function as_symbol(type, noerror) { - if (!is("name")) { - if (!noerror) croak("Name expected"); - return null; - } - var name = S.token.value; - var sym = new (name == "this" ? AST_This : type)({ - name: String(S.token.value), - start: S.token, - end: S.token - }); - next(); - return sym; - } - var subscripts = function(expr, allow_calls) { - var start = expr.start; - if (is("punc", ".")) { - next(); - return subscripts(new AST_Dot({ - start: start, - expression: expr, - property: as_name(), - end: prev() - }), allow_calls); - } - if (is("punc", "[")) { - next(); - var prop = expression(true); - expect("]"); - return subscripts(new AST_Sub({ - start: start, - expression: expr, - property: prop, - end: prev() - }), allow_calls); - } - if (allow_calls && is("punc", "(")) { - next(); - return subscripts(new AST_Call({ - start: start, - expression: expr, - args: expr_list(")"), - end: prev() - }), true); - } - return expr; - }; - var maybe_unary = function(allow_calls) { - var start = S.token; - if (is("operator") && UNARY_PREFIX(start.value)) { - next(); - var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); - ex.start = start; - ex.end = prev(); - return ex; - } - var val = expr_atom(allow_calls); - while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { - val = make_unary(AST_UnaryPostfix, S.token.value, val); - val.start = start; - val.end = S.token; - next(); - } - return val; - }; - function make_unary(ctor, op, expr) { - if ((op == "++" || op == "--") && !is_assignable(expr)) croak("Invalid use of " + op + " operator"); - return new ctor({ - operator: op, - expression: expr - }); - } - var expr_op = function(left, min_prec, no_in) { - var op = is("operator") ? S.token.value : null; - if (op == "in" && no_in) op = null; - var prec = op != null ? PRECEDENCE[op] : null; - if (prec != null && prec > min_prec) { - next(); - var right = expr_op(maybe_unary(true), prec, no_in); - return expr_op(new AST_Binary({ - start: left.start, - left: left, - operator: op, - right: right, - end: right.end - }), min_prec, no_in); - } - return left; - }; - function expr_ops(no_in) { - return expr_op(maybe_unary(true), 0, no_in); - } - var maybe_conditional = function(no_in) { - var start = S.token; - var expr = expr_ops(no_in); - if (is("operator", "?")) { - next(); - var yes = expression(false); - expect(":"); - return new AST_Conditional({ - start: start, - condition: expr, - consequent: yes, - alternative: expression(false, no_in), - end: peek() - }); - } - return expr; - }; - function is_assignable(expr) { - if (!options.strict) return true; - if (expr instanceof AST_This) return false; - return expr instanceof AST_PropAccess || expr instanceof AST_Symbol; - } - var maybe_assign = function(no_in) { - var start = S.token; - var left = maybe_conditional(no_in), val = S.token.value; - if (is("operator") && ASSIGNMENT(val)) { - if (is_assignable(left)) { - next(); - return new AST_Assign({ - start: start, - left: left, - operator: val, - right: maybe_assign(no_in), - end: prev() - }); - } - croak("Invalid assignment"); - } - return left; - }; - var expression = function(commas, no_in) { - var start = S.token; - var expr = maybe_assign(no_in); - if (commas && is("punc", ",")) { - next(); - return new AST_Seq({ - start: start, - car: expr, - cdr: expression(true, no_in), - end: peek() - }); + if (might_need_space) { + var prev = last_char(); + if ((is_identifier_char(prev) + && (is_identifier_char(ch) || ch == "\\")) + || (/^[\+\-\/]$/.test(ch) && ch == prev)) + { + OUTPUT += " "; + current_col++; + current_pos++; } - return expr; - }; - function in_loop(cont) { - ++S.in_loop; - var ret = cont(); - --S.in_loop; - return ret; - } - if (options.expression) { - return expression(true); + might_need_space = false; } - return function() { - var start = S.token; - var body = []; - while (!is("eof")) body.push(statement()); - var end = prev(); - var toplevel = options.toplevel; - if (toplevel) { - toplevel.body = toplevel.body.concat(body); - toplevel.end = end; - } else { - toplevel = new AST_Toplevel({ - start: start, - body: body, - end: end - }); - } - return toplevel; - }(); - } - "use strict"; - function TreeTransformer(before, after) { - TreeWalker.call(this); - this.before = before; - this.after = after; - } - TreeTransformer.prototype = new TreeWalker(); - (function(undefined) { - function _(node, descend) { - node.DEFMETHOD("transform", function(tw, in_list) { - var x, y; - tw.push(this); - if (tw.before) x = tw.before(this, descend, in_list); - if (x === undefined) { - if (!tw.after) { - x = this; - descend(x, tw); - } else { - tw.stack[tw.stack.length - 1] = x = this.clone(); - descend(x, tw); - y = tw.after(x, in_list); - if (y !== undefined) x = y; - } - } - tw.pop(); - return x; - }); + var a = str.split(/\r?\n/), n = a.length - 1; + current_line += n; + if (n == 0) { + current_col += a[n].length; + } else { + current_col = a[n].length; } - function do_list(list, tw) { - return MAP(list, function(node) { - return node.transform(tw, true); - }); + current_pos += str.length; + last = str; + OUTPUT += str; + }; + + var space = options.beautify ? function() { + print(" "); + } : function() { + might_need_space = true; + }; + + var indent = options.beautify ? function(half) { + if (options.beautify) { + print(make_indent(half ? 0.5 : 0)); } - _(AST_Node, noop); - _(AST_LabeledStatement, function(self, tw) { - self.label = self.label.transform(tw); - self.body = self.body.transform(tw); - }); - _(AST_SimpleStatement, function(self, tw) { - self.body = self.body.transform(tw); - }); - _(AST_Block, function(self, tw) { - self.body = do_list(self.body, tw); - }); - _(AST_DWLoop, function(self, tw) { - self.condition = self.condition.transform(tw); - self.body = self.body.transform(tw); - }); - _(AST_For, function(self, tw) { - if (self.init) self.init = self.init.transform(tw); - if (self.condition) self.condition = self.condition.transform(tw); - if (self.step) self.step = self.step.transform(tw); - self.body = self.body.transform(tw); - }); - _(AST_ForIn, function(self, tw) { - self.init = self.init.transform(tw); - self.object = self.object.transform(tw); - self.body = self.body.transform(tw); - }); - _(AST_With, function(self, tw) { - self.expression = self.expression.transform(tw); - self.body = self.body.transform(tw); - }); - _(AST_Exit, function(self, tw) { - if (self.value) self.value = self.value.transform(tw); - }); - _(AST_LoopControl, function(self, tw) { - if (self.label) self.label = self.label.transform(tw); - }); - _(AST_If, function(self, tw) { - self.condition = self.condition.transform(tw); - self.body = self.body.transform(tw); - if (self.alternative) self.alternative = self.alternative.transform(tw); - }); - _(AST_Switch, function(self, tw) { - self.expression = self.expression.transform(tw); - self.body = do_list(self.body, tw); - }); - _(AST_Case, function(self, tw) { - self.expression = self.expression.transform(tw); - self.body = do_list(self.body, tw); - }); - _(AST_Try, function(self, tw) { - self.body = do_list(self.body, tw); - if (self.bcatch) self.bcatch = self.bcatch.transform(tw); - if (self.bfinally) self.bfinally = self.bfinally.transform(tw); - }); - _(AST_Catch, function(self, tw) { - self.argname = self.argname.transform(tw); - self.body = do_list(self.body, tw); - }); - _(AST_Definitions, function(self, tw) { - self.definitions = do_list(self.definitions, tw); - }); - _(AST_VarDef, function(self, tw) { - self.name = self.name.transform(tw); - if (self.value) self.value = self.value.transform(tw); - }); - _(AST_Lambda, function(self, tw) { - if (self.name) self.name = self.name.transform(tw); - self.argnames = do_list(self.argnames, tw); - self.body = do_list(self.body, tw); - }); - _(AST_Call, function(self, tw) { - self.expression = self.expression.transform(tw); - self.args = do_list(self.args, tw); - }); - _(AST_Seq, function(self, tw) { - self.car = self.car.transform(tw); - self.cdr = self.cdr.transform(tw); - }); - _(AST_Dot, function(self, tw) { - self.expression = self.expression.transform(tw); - }); - _(AST_Sub, function(self, tw) { - self.expression = self.expression.transform(tw); - self.property = self.property.transform(tw); - }); - _(AST_Unary, function(self, tw) { - self.expression = self.expression.transform(tw); - }); - _(AST_Binary, function(self, tw) { - self.left = self.left.transform(tw); - self.right = self.right.transform(tw); - }); - _(AST_Conditional, function(self, tw) { - self.condition = self.condition.transform(tw); - self.consequent = self.consequent.transform(tw); - self.alternative = self.alternative.transform(tw); - }); - _(AST_Array, function(self, tw) { - self.elements = do_list(self.elements, tw); - }); - _(AST_Object, function(self, tw) { - self.properties = do_list(self.properties, tw); - }); - _(AST_ObjectProperty, function(self, tw) { - self.value = self.value.transform(tw); + } : noop; + + var with_indent = options.beautify ? function(col, cont) { + if (col === true) col = next_indent(); + var save_indentation = indentation; + indentation = col; + var ret = cont(); + indentation = save_indentation; + return ret; + } : function(col, cont) { return cont() }; + + var newline = options.beautify ? function() { + print("\n"); + } : noop; + + var semicolon = options.beautify ? function() { + print(";"); + } : function() { + might_need_semicolon = true; + }; + + function force_semicolon() { + might_need_semicolon = false; + print(";"); + }; + + function next_indent() { + return indentation + options.indent_level; + }; + + function with_block(cont) { + var ret; + print("{"); + newline(); + with_indent(next_indent(), function(){ + ret = cont(); }); - })(); - "use strict"; - function SymbolDef(scope, index, orig) { - this.name = orig.name; - this.orig = [ orig ]; - this.scope = scope; - this.references = []; - this.global = false; - this.mangled_name = null; - this.undeclared = false; - this.constant = false; - this.index = index; - } - SymbolDef.prototype = { - unmangleable: function(options) { - return this.global && !(options && options.toplevel) || this.undeclared || !(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with); - }, - mangle: function(options) { - if (!this.mangled_name && !this.unmangleable(options)) { - var s = this.scope; - if (this.orig[0] instanceof AST_SymbolLambda && !options.screw_ie8) s = s.parent_scope; - this.mangled_name = s.next_mangled(options); - } + indent(); + print("}"); + return ret; + }; + + function with_parens(cont) { + print("("); + //XXX: still nice to have that for argument lists + //var ret = with_indent(current_col, cont); + var ret = cont(); + print(")"); + return ret; + }; + + function with_square(cont) { + print("["); + //var ret = with_indent(current_col, cont); + var ret = cont(); + print("]"); + return ret; + }; + + function comma() { + print(","); + space(); + }; + + function colon() { + print(":"); + if (options.space_colon) space(); + }; + + var add_mapping = options.source_map ? function(token, name) { + try { + if (token) options.source_map.add( + token.file || "?", + current_line, current_col, + token.line, token.col, + (!name && token.type == "name") ? token.value : name + ); + } catch(ex) { + AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", { + file: token.file, + line: token.line, + col: token.col, + cline: current_line, + ccol: current_col, + name: name || "" + }) + } + } : noop; + + function get() { + return OUTPUT; + }; + + if (options.preamble) { + print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); + } + + var stack = []; + return { + get : get, + toString : get, + indent : indent, + indentation : function() { return indentation }, + current_width : function() { return current_col - indentation }, + should_break : function() { return options.width && this.current_width() >= options.width }, + newline : newline, + print : print, + space : space, + comma : comma, + colon : colon, + last : function() { return last }, + semicolon : semicolon, + force_semicolon : force_semicolon, + to_ascii : to_ascii, + print_name : function(name) { print(make_name(name)) }, + print_string : function(str) { print(encode_string(str)) }, + next_indent : next_indent, + with_indent : with_indent, + with_block : with_block, + with_parens : with_parens, + with_square : with_square, + add_mapping : add_mapping, + option : function(opt) { return options[opt] }, + line : function() { return current_line }, + col : function() { return current_col }, + pos : function() { return current_pos }, + push_node : function(node) { stack.push(node) }, + pop_node : function() { return stack.pop() }, + stack : function() { return stack }, + parent : function(n) { + return stack[stack.length - 2 - (n || 0)]; } }; - AST_Toplevel.DEFMETHOD("figure_out_scope", function() { - var self = this; - var scope = self.parent_scope = null; - var labels = new Dictionary(); - var nesting = 0; - var tw = new TreeWalker(function(node, descend) { - if (node instanceof AST_Scope) { - node.init_scope_vars(nesting); - var save_scope = node.parent_scope = scope; - var save_labels = labels; - ++nesting; - scope = node; - labels = new Dictionary(); - descend(); - labels = save_labels; - scope = save_scope; - --nesting; - return true; - } - if (node instanceof AST_Directive) { - node.scope = scope; - push_uniq(scope.directives, node.value); - return true; - } - if (node instanceof AST_With) { - for (var s = scope; s; s = s.parent_scope) s.uses_with = true; - return; - } - if (node instanceof AST_LabeledStatement) { - var l = node.label; - if (labels.has(l.name)) throw new Error(string_template("Label {name} defined twice", l)); - labels.set(l.name, l); - descend(); - labels.del(l.name); - return true; - } - if (node instanceof AST_Symbol) { - node.scope = scope; - } - if (node instanceof AST_Label) { - node.thedef = node; - node.init_scope_vars(); - } - if (node instanceof AST_SymbolLambda) { - scope.def_function(node); - } else if (node instanceof AST_SymbolDefun) { - (node.scope = scope.parent_scope).def_function(node); - } else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { - var def = scope.def_variable(node); - def.constant = node instanceof AST_SymbolConst; - def.init = tw.parent().value; - } else if (node instanceof AST_SymbolCatch) { - scope.def_variable(node); - } - if (node instanceof AST_LabelRef) { - var sym = labels.get(node.name); - if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", { - name: node.name, - line: node.start.line, - col: node.start.col - })); - node.thedef = sym; - } - }); - self.walk(tw); - var func = null; - var globals = self.globals = new Dictionary(); - var tw = new TreeWalker(function(node, descend) { - if (node instanceof AST_Lambda) { - var prev_func = func; - func = node; - descend(); - func = prev_func; - return true; - } - if (node instanceof AST_LabelRef) { - node.reference(); - return true; - } - if (node instanceof AST_SymbolRef) { - var name = node.name; - var sym = node.scope.find_variable(name); - if (!sym) { - var g; - if (globals.has(name)) { - g = globals.get(name); - } else { - g = new SymbolDef(self, globals.size(), node); - g.undeclared = true; - g.global = true; - globals.set(name, g); - } - node.thedef = g; - if (name == "eval" && tw.parent() instanceof AST_Call) { - for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) s.uses_eval = true; - } - if (name == "arguments") { - func.uses_arguments = true; - } - } else { - node.thedef = sym; - } - node.reference(); - return true; - } - }); - self.walk(tw); - }); - AST_Scope.DEFMETHOD("init_scope_vars", function(nesting) { - this.directives = []; - this.variables = new Dictionary(); - this.functions = new Dictionary(); - this.uses_with = false; - this.uses_eval = false; - this.parent_scope = null; - this.enclosed = []; - this.cname = -1; - this.nesting = nesting; - }); - AST_Scope.DEFMETHOD("strict", function() { - return this.has_directive("use strict"); - }); - AST_Lambda.DEFMETHOD("init_scope_vars", function() { - AST_Scope.prototype.init_scope_vars.apply(this, arguments); - this.uses_arguments = false; - }); - AST_SymbolRef.DEFMETHOD("reference", function() { - var def = this.definition(); - def.references.push(this); - var s = this.scope; - while (s) { - push_uniq(s.enclosed, def); - if (s === def.scope) break; - s = s.parent_scope; - } - this.frame = this.scope.nesting - def.scope.nesting; - }); - AST_Label.DEFMETHOD("init_scope_vars", function() { - this.references = []; - }); - AST_LabelRef.DEFMETHOD("reference", function() { - this.thedef.references.push(this); - }); - AST_Scope.DEFMETHOD("find_variable", function(name) { - if (name instanceof AST_Symbol) name = name.name; - return this.variables.get(name) || this.parent_scope && this.parent_scope.find_variable(name); - }); - AST_Scope.DEFMETHOD("has_directive", function(value) { - return this.parent_scope && this.parent_scope.has_directive(value) || (this.directives.indexOf(value) >= 0 ? this : null); - }); - AST_Scope.DEFMETHOD("def_function", function(symbol) { - this.functions.set(symbol.name, this.def_variable(symbol)); - }); - AST_Scope.DEFMETHOD("def_variable", function(symbol) { - var def; - if (!this.variables.has(symbol.name)) { - def = new SymbolDef(this, this.variables.size(), symbol); - this.variables.set(symbol.name, def); - def.global = !this.parent_scope; + +}; + +/* -----[ code generators ]----- */ + +(function(){ + + /* -----[ utils ]----- */ + + function DEFPRINT(nodetype, generator) { + nodetype.DEFMETHOD("_codegen", generator); + }; + + AST_Node.DEFMETHOD("print", function(stream, force_parens){ + var self = this, generator = self._codegen; + function doit() { + self.add_comments(stream); + self.add_source_map(stream); + generator(self, stream); + } + stream.push_node(self); + if (force_parens || self.needs_parens(stream)) { + stream.with_parens(doit); } else { - def = this.variables.get(symbol.name); - def.orig.push(symbol); + doit(); } - return symbol.thedef = def; + stream.pop_node(); + }); + + AST_Node.DEFMETHOD("print_to_string", function(options){ + var s = OutputStream(options); + this.print(s); + return s.get(); }); - AST_Scope.DEFMETHOD("next_mangled", function(options) { - var ext = this.enclosed; - out: while (true) { - var m = base54(++this.cname); - if (!is_identifier(m)) continue; - for (var i = ext.length; --i >= 0; ) { - var sym = ext[i]; - var name = sym.mangled_name || sym.unmangleable(options) && sym.name; - if (m == name) continue out; + + /* -----[ comments ]----- */ + + AST_Node.DEFMETHOD("add_comments", function(output){ + var c = output.option("comments"), self = this; + if (c) { + var start = self.start; + if (start && !start._comments_dumped) { + start._comments_dumped = true; + var comments = start.comments_before || []; + + // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 + // and https://github.com/mishoo/UglifyJS2/issues/372 + if (self instanceof AST_Exit && self.value) { + self.value.walk(new TreeWalker(function(node){ + if (node.start && node.start.comments_before) { + comments = comments.concat(node.start.comments_before); + node.start.comments_before = []; + } + if (node instanceof AST_Function || + node instanceof AST_Array || + node instanceof AST_Object) + { + return true; // don't go inside. + } + })); + } + + if (c.test) { + comments = comments.filter(function(comment){ + return c.test(comment.value); + }); + } else if (typeof c == "function") { + comments = comments.filter(function(comment){ + return c(self, comment); + }); + } + comments.forEach(function(c){ + if (/comment[134]/.test(c.type)) { + output.print("//" + c.value + "\n"); + output.indent(); + } + else if (c.type == "comment2") { + output.print("/*" + c.value + "*/"); + if (start.nlb) { + output.print("\n"); + output.indent(); + } else { + output.space(); + } + } + }); } - return m; } }); - AST_Scope.DEFMETHOD("references", function(sym) { - if (sym instanceof AST_Symbol) sym = sym.definition(); - return this.enclosed.indexOf(sym) < 0 ? null : sym; - }); - AST_Symbol.DEFMETHOD("unmangleable", function(options) { - return this.definition().unmangleable(options); - }); - AST_SymbolAccessor.DEFMETHOD("unmangleable", function() { - return true; - }); - AST_Label.DEFMETHOD("unmangleable", function() { - return false; - }); - AST_Symbol.DEFMETHOD("unreferenced", function() { - return this.definition().references.length == 0 && !(this.scope.uses_eval || this.scope.uses_with); - }); - AST_Symbol.DEFMETHOD("undeclared", function() { - return this.definition().undeclared; - }); - AST_LabelRef.DEFMETHOD("undeclared", function() { + + /* -----[ PARENTHESES ]----- */ + + function PARENS(nodetype, func) { + nodetype.DEFMETHOD("needs_parens", func); + }; + + PARENS(AST_Node, function(){ return false; }); - AST_Label.DEFMETHOD("undeclared", function() { - return false; + + // a function expression needs parens around it when it's provably + // the first token to appear in a statement. + PARENS(AST_Function, function(output){ + return first_in_statement(output); }); - AST_Symbol.DEFMETHOD("definition", function() { - return this.thedef; + + // same goes for an object literal, because otherwise it would be + // interpreted as a block of code. + PARENS(AST_Object, function(output){ + return first_in_statement(output); }); - AST_Symbol.DEFMETHOD("global", function() { - return this.definition().global; + + PARENS(AST_Unary, function(output){ + var p = output.parent(); + return p instanceof AST_PropAccess && p.expression === this; }); - AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) { - return defaults(options, { - except: [], - eval: false, - sort: false, - toplevel: false, - screw_ie8: false - }); + + PARENS(AST_Seq, function(output){ + var p = output.parent(); + return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) + || p instanceof AST_Unary // !(foo, bar, baz) + || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8 + || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4 + || p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2 + || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] + || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 + || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) + * ==> 20 (side effect, set a := 10 and b := 20) */ + ; }); - AST_Toplevel.DEFMETHOD("mangle_names", function(options) { - options = this._default_mangler_options(options); - var lname = -1; - var to_mangle = []; - var tw = new TreeWalker(function(node, descend) { - if (node instanceof AST_LabeledStatement) { - var save_nesting = lname; - descend(); - lname = save_nesting; - return true; - } - if (node instanceof AST_Scope) { - var p = tw.parent(), a = []; - node.variables.each(function(symbol) { - if (options.except.indexOf(symbol.name) < 0) { - a.push(symbol); - } - }); - if (options.sort) a.sort(function(a, b) { - return b.references.length - a.references.length; - }); - to_mangle.push.apply(to_mangle, a); - return; - } - if (node instanceof AST_Label) { - var name; - do name = base54(++lname); while (!is_identifier(name)); - node.mangled_name = name; + + PARENS(AST_Binary, function(output){ + var p = output.parent(); + // (foo && bar)() + if (p instanceof AST_Call && p.expression === this) + return true; + // typeof (foo && bar) + if (p instanceof AST_Unary) + return true; + // (foo && bar)["prop"], (foo && bar).prop + if (p instanceof AST_PropAccess && p.expression === this) + return true; + // this deals with precedence: 3 * (2 + 1) + if (p instanceof AST_Binary) { + var po = p.operator, pp = PRECEDENCE[po]; + var so = this.operator, sp = PRECEDENCE[so]; + if (pp > sp + || (pp == sp + && this === p.right)) { return true; } - }); - this.walk(tw); - to_mangle.forEach(function(def) { - def.mangle(options); - }); - }); - AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { - options = this._default_mangler_options(options); - var tw = new TreeWalker(function(node) { - if (node instanceof AST_Constant) base54.consider(node.print_to_string()); else if (node instanceof AST_Return) base54.consider("return"); else if (node instanceof AST_Throw) base54.consider("throw"); else if (node instanceof AST_Continue) base54.consider("continue"); else if (node instanceof AST_Break) base54.consider("break"); else if (node instanceof AST_Debugger) base54.consider("debugger"); else if (node instanceof AST_Directive) base54.consider(node.value); else if (node instanceof AST_While) base54.consider("while"); else if (node instanceof AST_Do) base54.consider("do while"); else if (node instanceof AST_If) { - base54.consider("if"); - if (node.alternative) base54.consider("else"); - } else if (node instanceof AST_Var) base54.consider("var"); else if (node instanceof AST_Const) base54.consider("const"); else if (node instanceof AST_Lambda) base54.consider("function"); else if (node instanceof AST_For) base54.consider("for"); else if (node instanceof AST_ForIn) base54.consider("for in"); else if (node instanceof AST_Switch) base54.consider("switch"); else if (node instanceof AST_Case) base54.consider("case"); else if (node instanceof AST_Default) base54.consider("default"); else if (node instanceof AST_With) base54.consider("with"); else if (node instanceof AST_ObjectSetter) base54.consider("set" + node.key); else if (node instanceof AST_ObjectGetter) base54.consider("get" + node.key); else if (node instanceof AST_ObjectKeyVal) base54.consider(node.key); else if (node instanceof AST_New) base54.consider("new"); else if (node instanceof AST_This) base54.consider("this"); else if (node instanceof AST_Try) base54.consider("try"); else if (node instanceof AST_Catch) base54.consider("catch"); else if (node instanceof AST_Finally) base54.consider("finally"); else if (node instanceof AST_Symbol && node.unmangleable(options)) base54.consider(node.name); else if (node instanceof AST_Unary || node instanceof AST_Binary) base54.consider(node.operator); else if (node instanceof AST_Dot) base54.consider(node.property); - }); - this.walk(tw); - base54.sort(); - }); - var base54 = function() { - var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; - var chars, frequency; - function reset() { - frequency = Object.create(null); - chars = string.split("").map(function(ch) { - return ch.charCodeAt(0); - }); - chars.forEach(function(ch) { - frequency[ch] = 0; - }); } - base54.consider = function(str) { - for (var i = str.length; --i >= 0; ) { - var code = str.charCodeAt(i); - if (code in frequency) ++frequency[code]; + }); + + PARENS(AST_PropAccess, function(output){ + var p = output.parent(); + if (p instanceof AST_New && p.expression === this) { + // i.e. new (foo.bar().baz) + // + // if there's one call into this subtree, then we need + // parens around it too, otherwise the call will be + // interpreted as passing the arguments to the upper New + // expression. + try { + this.walk(new TreeWalker(function(node){ + if (node instanceof AST_Call) throw p; + })); + } catch(ex) { + if (ex !== p) throw ex; + return true; } - }; - base54.sort = function() { - chars = mergeSort(chars, function(a, b) { - if (is_digit(a) && !is_digit(b)) return 1; - if (is_digit(b) && !is_digit(a)) return -1; - return frequency[b] - frequency[a]; - }); - }; - base54.reset = reset; - reset(); - base54.get = function() { - return chars; - }; - base54.freq = function() { - return frequency; - }; - function base54(num) { - var ret = "", base = 54; - do { - ret += String.fromCharCode(chars[num % base]); - num = Math.floor(num / base); - base = 64; - } while (num > 0); - return ret; } - return base54; - }(); - AST_Toplevel.DEFMETHOD("scope_warnings", function(options) { - options = defaults(options, { - undeclared: false, - unreferenced: true, - assign_to_global: true, - func_arguments: true, - nested_defuns: true, - eval: true - }); - var tw = new TreeWalker(function(node) { - if (options.undeclared && node instanceof AST_SymbolRef && node.undeclared()) { - AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { - name: node.name, - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - if (options.assign_to_global) { - var sym = null; - if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) sym = node.left; else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) sym = node.init; - if (sym && (sym.undeclared() || sym.global() && sym.scope !== sym.definition().scope)) { - AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { - msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", - name: sym.name, - file: sym.start.file, - line: sym.start.line, - col: sym.start.col - }); - } - } - if (options.eval && node instanceof AST_SymbolRef && node.undeclared() && node.name == "eval") { - AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); - } - if (options.unreferenced && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) && node.unreferenced()) { - AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { - type: node instanceof AST_Label ? "Label" : "Symbol", - name: node.name, - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - if (options.func_arguments && node instanceof AST_Lambda && node.uses_arguments) { - AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { - name: node.name ? node.name.name : "anonymous", - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - if (options.nested_defuns && node instanceof AST_Defun && !(tw.parent() instanceof AST_Scope)) { - AST_Node.warn('Function {name} declared in nested statement "{type}" [{file}:{line},{col}]', { - name: node.name.name, - type: tw.parent().TYPE, - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - }); - this.walk(tw); }); - "use strict"; - function OutputStream(options) { - options = defaults(options, { - indent_start: 0, - indent_level: 4, - quote_keys: false, - space_colon: true, - ascii_only: false, - inline_script: false, - width: 80, - max_line_len: 32e3, - beautify: false, - source_map: null, - bracketize: false, - semicolons: true, - comments: false, - preserve_line: false, - screw_ie8: false - }, true); - var indentation = 0; - var current_col = 0; - var current_line = 1; - var current_pos = 0; - var OUTPUT = ""; - function to_ascii(str, identifier) { - return str.replace(/[\u0080-\uffff]/g, function(ch) { - var code = ch.charCodeAt(0).toString(16); - if (code.length <= 2 && !identifier) { - while (code.length < 2) code = "0" + code; - return "\\x" + code; - } else { - while (code.length < 4) code = "0" + code; - return "\\u" + code; - } - }); - } - function make_string(str) { - var dq = 0, sq = 0; - str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s) { - switch (s) { - case "\\": - return "\\\\"; - case "\b": - return "\\b"; + PARENS(AST_Call, function(output){ + var p = output.parent(), p1; + if (p instanceof AST_New && p.expression === this) + return true; - case "\f": - return "\\f"; + // workaround for Safari bug. + // https://bugs.webkit.org/show_bug.cgi?id=123506 + return this.expression instanceof AST_Function + && p instanceof AST_PropAccess + && p.expression === this + && (p1 = output.parent(1)) instanceof AST_Assign + && p1.left === p; + }); - case "\n": - return "\\n"; + PARENS(AST_New, function(output){ + var p = output.parent(); + if (no_constructor_parens(this, output) + && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() + || p instanceof AST_Call && p.expression === this)) // (new foo)(bar) + return true; + }); - case "\r": - return "\\r"; + PARENS(AST_Number, function(output){ + var p = output.parent(); + if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this) + return true; + }); - case "\u2028": - return "\\u2028"; + PARENS(AST_NaN, function(output){ + var p = output.parent(); + if (p instanceof AST_PropAccess && p.expression === this) + return true; + }); - case "\u2029": - return "\\u2029"; + function assign_and_conditional_paren_rules(output) { + var p = output.parent(); + // !(a = false) → true + if (p instanceof AST_Unary) + return true; + // 1 + (a = 2) + 3 → 6, side effect setting a = 2 + if (p instanceof AST_Binary && !(p instanceof AST_Assign)) + return true; + // (a = func)() —or— new (a = Object)() + if (p instanceof AST_Call && p.expression === this) + return true; + // (a = foo) ? bar : baz + if (p instanceof AST_Conditional && p.condition === this) + return true; + // (a = foo)["prop"] —or— (a = foo).prop + if (p instanceof AST_PropAccess && p.expression === this) + return true; + }; - case '"': - ++dq; - return '"'; + PARENS(AST_Assign, assign_and_conditional_paren_rules); + PARENS(AST_Conditional, assign_and_conditional_paren_rules); - case "'": - ++sq; - return "'"; + /* -----[ PRINTERS ]----- */ - case "\x00": - return "\\x00"; - } - return s; - }); - if (options.ascii_only) str = to_ascii(str); - if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; else return '"' + str.replace(/\x22/g, '\\"') + '"'; - } - function encode_string(str) { - var ret = make_string(str); - if (options.inline_script) ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); - return ret; - } - function make_name(name) { - name = name.toString(); - if (options.ascii_only) name = to_ascii(name, true); - return name; - } - function make_indent(back) { - return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); - } - var might_need_space = false; - var might_need_semicolon = false; - var last = null; - function last_char() { - return last.charAt(last.length - 1); - } - function maybe_newline() { - if (options.max_line_len && current_col > options.max_line_len) print("\n"); - } - var requireSemicolonChars = makePredicate("( [ + * / - , ."); - function print(str) { - str = String(str); - var ch = str.charAt(0); - if (might_need_semicolon) { - if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) { - if (options.semicolons || requireSemicolonChars(ch)) { - OUTPUT += ";"; - current_col++; - current_pos++; - } else { - OUTPUT += "\n"; - current_pos++; - current_line++; - current_col = 0; - } - if (!options.beautify) might_need_space = false; - } - might_need_semicolon = false; - maybe_newline(); - } - if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { - var target_line = stack[stack.length - 1].start.line; - while (current_line < target_line) { - OUTPUT += "\n"; - current_pos++; - current_line++; - current_col = 0; - might_need_space = false; - } - } - if (might_need_space) { - var prev = last_char(); - if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") || /^[\+\-\/]$/.test(ch) && ch == prev) { - OUTPUT += " "; - current_col++; - current_pos++; - } - might_need_space = false; - } - var a = str.split(/\r?\n/), n = a.length - 1; - current_line += n; - if (n == 0) { - current_col += a[n].length; - } else { - current_col = a[n].length; - } - current_pos += str.length; - last = str; - OUTPUT += str; - } - var space = options.beautify ? function() { - print(" "); - } : function() { - might_need_space = true; - }; - var indent = options.beautify ? function(half) { - if (options.beautify) { - print(make_indent(half ? .5 : 0)); - } - } : noop; - var with_indent = options.beautify ? function(col, cont) { - if (col === true) col = next_indent(); - var save_indentation = indentation; - indentation = col; - var ret = cont(); - indentation = save_indentation; - return ret; - } : function(col, cont) { - return cont(); - }; - var newline = options.beautify ? function() { - print("\n"); - } : noop; - var semicolon = options.beautify ? function() { - print(";"); - } : function() { - might_need_semicolon = true; - }; - function force_semicolon() { - might_need_semicolon = false; - print(";"); - } - function next_indent() { - return indentation + options.indent_level; - } - function with_block(cont) { - var ret; - print("{"); - newline(); - with_indent(next_indent(), function() { - ret = cont(); - }); - indent(); - print("}"); - return ret; - } - function with_parens(cont) { - print("("); - var ret = cont(); - print(")"); - return ret; - } - function with_square(cont) { - print("["); - var ret = cont(); - print("]"); - return ret; - } - function comma() { - print(","); - space(); - } - function colon() { - print(":"); - if (options.space_colon) space(); - } - var add_mapping = options.source_map ? function(token, name) { - try { - if (token) options.source_map.add(token.file || "?", current_line, current_col, token.line, token.col, !name && token.type == "name" ? token.value : name); - } catch (ex) { - AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", { - file: token.file, - line: token.line, - col: token.col, - cline: current_line, - ccol: current_col, - name: name || "" - }); - } - } : noop; - function get() { - return OUTPUT; - } - var stack = []; - return { - get: get, - toString: get, - indent: indent, - indentation: function() { - return indentation; - }, - current_width: function() { - return current_col - indentation; - }, - should_break: function() { - return options.width && this.current_width() >= options.width; - }, - newline: newline, - print: print, - space: space, - comma: comma, - colon: colon, - last: function() { - return last; - }, - semicolon: semicolon, - force_semicolon: force_semicolon, - to_ascii: to_ascii, - print_name: function(name) { - print(make_name(name)); - }, - print_string: function(str) { - print(encode_string(str)); - }, - next_indent: next_indent, - with_indent: with_indent, - with_block: with_block, - with_parens: with_parens, - with_square: with_square, - add_mapping: add_mapping, - option: function(opt) { - return options[opt]; - }, - line: function() { - return current_line; - }, - col: function() { - return current_col; - }, - pos: function() { - return current_pos; - }, - push_node: function(node) { - stack.push(node); - }, - pop_node: function() { - return stack.pop(); - }, - stack: function() { - return stack; - }, - parent: function(n) { - return stack[stack.length - 2 - (n || 0)]; - } - }; - } - (function() { - function DEFPRINT(nodetype, generator) { - nodetype.DEFMETHOD("_codegen", generator); - } - AST_Node.DEFMETHOD("print", function(stream, force_parens) { - var self = this, generator = self._codegen; - function doit() { - self.add_comments(stream); - self.add_source_map(stream); - generator(self, stream); - } - stream.push_node(self); - if (force_parens || self.needs_parens(stream)) { - stream.with_parens(doit); - } else { - doit(); - } - stream.pop_node(); - }); - AST_Node.DEFMETHOD("print_to_string", function(options) { - var s = OutputStream(options); - this.print(s); - return s.get(); - }); - AST_Node.DEFMETHOD("add_comments", function(output) { - var c = output.option("comments"), self = this; - if (c) { - var start = self.start; - if (start && !start._comments_dumped) { - start._comments_dumped = true; - var comments = start.comments_before; - if (self instanceof AST_Exit && self.value && self.value.start.comments_before.length > 0) { - comments = (comments || []).concat(self.value.start.comments_before); - self.value.start.comments_before = []; - } - if (c.test) { - comments = comments.filter(function(comment) { - return c.test(comment.value); - }); - } else if (typeof c == "function") { - comments = comments.filter(function(comment) { - return c(self, comment); - }); - } - comments.forEach(function(c) { - if (c.type == "comment1") { - output.print("//" + c.value + "\n"); - output.indent(); - } else if (c.type == "comment2") { - output.print("/*" + c.value + "*/"); - if (start.nlb) { - output.print("\n"); - output.indent(); - } else { - output.space(); - } - } - }); - } - } - }); - function PARENS(nodetype, func) { - nodetype.DEFMETHOD("needs_parens", func); - } - PARENS(AST_Node, function() { - return false; - }); - PARENS(AST_Function, function(output) { - return first_in_statement(output); - }); - PARENS(AST_Object, function(output) { - return first_in_statement(output); - }); - PARENS(AST_Unary, function(output) { - var p = output.parent(); - return p instanceof AST_PropAccess && p.expression === this; - }); - PARENS(AST_Seq, function(output) { - var p = output.parent(); - return p instanceof AST_Call || p instanceof AST_Unary || p instanceof AST_Binary || p instanceof AST_VarDef || p instanceof AST_Dot || p instanceof AST_Array || p instanceof AST_ObjectProperty || p instanceof AST_Conditional; - }); - PARENS(AST_Binary, function(output) { - var p = output.parent(); - if (p instanceof AST_Call && p.expression === this) return true; - if (p instanceof AST_Unary) return true; - if (p instanceof AST_PropAccess && p.expression === this) return true; - if (p instanceof AST_Binary) { - var po = p.operator, pp = PRECEDENCE[po]; - var so = this.operator, sp = PRECEDENCE[so]; - if (pp > sp || pp == sp && this === p.right && !(so == po && (so == "*" || so == "&&" || so == "||"))) { - return true; - } - } - }); - PARENS(AST_PropAccess, function(output) { - var p = output.parent(); - if (p instanceof AST_New && p.expression === this) { - try { - this.walk(new TreeWalker(function(node) { - if (node instanceof AST_Call) throw p; - })); - } catch (ex) { - if (ex !== p) throw ex; - return true; - } - } - }); - PARENS(AST_Call, function(output) { - var p = output.parent(); - return p instanceof AST_New && p.expression === this; - }); - PARENS(AST_New, function(output) { - var p = output.parent(); - if (no_constructor_parens(this, output) && (p instanceof AST_PropAccess || p instanceof AST_Call && p.expression === this)) return true; - }); - PARENS(AST_Number, function(output) { - var p = output.parent(); - if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this) return true; - }); - PARENS(AST_NaN, function(output) { - var p = output.parent(); - if (p instanceof AST_PropAccess && p.expression === this) return true; - }); - function assign_and_conditional_paren_rules(output) { - var p = output.parent(); - if (p instanceof AST_Unary) return true; - if (p instanceof AST_Binary && !(p instanceof AST_Assign)) return true; - if (p instanceof AST_Call && p.expression === this) return true; - if (p instanceof AST_Conditional && p.condition === this) return true; - if (p instanceof AST_PropAccess && p.expression === this) return true; - } - PARENS(AST_Assign, assign_and_conditional_paren_rules); - PARENS(AST_Conditional, assign_and_conditional_paren_rules); - DEFPRINT(AST_Directive, function(self, output) { - output.print_string(self.value); - output.semicolon(); - }); - DEFPRINT(AST_Debugger, function(self, output) { - output.print("debugger"); - output.semicolon(); - }); - function display_body(body, is_toplevel, output) { - var last = body.length - 1; - body.forEach(function(stmt, i) { - if (!(stmt instanceof AST_EmptyStatement)) { - output.indent(); - stmt.print(output); - if (!(i == last && is_toplevel)) { - output.newline(); - if (is_toplevel) output.newline(); - } - } - }); - } - AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) { - force_statement(this.body, output); - }); - DEFPRINT(AST_Statement, function(self, output) { - self.body.print(output); - output.semicolon(); - }); - DEFPRINT(AST_Toplevel, function(self, output) { - display_body(self.body, true, output); - output.print(""); - }); - DEFPRINT(AST_LabeledStatement, function(self, output) { - self.label.print(output); - output.colon(); - self.body.print(output); - }); - DEFPRINT(AST_SimpleStatement, function(self, output) { - self.body.print(output); - output.semicolon(); - }); - function print_bracketed(body, output) { - if (body.length > 0) output.with_block(function() { - display_body(body, false, output); - }); else output.print("{}"); - } - DEFPRINT(AST_BlockStatement, function(self, output) { - print_bracketed(self.body, output); - }); - DEFPRINT(AST_EmptyStatement, function(self, output) { - output.semicolon(); - }); - DEFPRINT(AST_Do, function(self, output) { - output.print("do"); - output.space(); - self._do_print_body(output); - output.space(); - output.print("while"); - output.space(); - output.with_parens(function() { - self.condition.print(output); - }); - output.semicolon(); - }); - DEFPRINT(AST_While, function(self, output) { - output.print("while"); - output.space(); - output.with_parens(function() { - self.condition.print(output); - }); - output.space(); - self._do_print_body(output); - }); - DEFPRINT(AST_For, function(self, output) { - output.print("for"); - output.space(); - output.with_parens(function() { - if (self.init) { - if (self.init instanceof AST_Definitions) { - self.init.print(output); - } else { - parenthesize_for_noin(self.init, output, true); - } - output.print(";"); - output.space(); - } else { - output.print(";"); - } - if (self.condition) { - self.condition.print(output); - output.print(";"); - output.space(); - } else { - output.print(";"); - } - if (self.step) { - self.step.print(output); - } - }); - output.space(); - self._do_print_body(output); - }); - DEFPRINT(AST_ForIn, function(self, output) { - output.print("for"); - output.space(); - output.with_parens(function() { - self.init.print(output); - output.space(); - output.print("in"); - output.space(); - self.object.print(output); - }); - output.space(); - self._do_print_body(output); - }); - DEFPRINT(AST_With, function(self, output) { - output.print("with"); - output.space(); - output.with_parens(function() { - self.expression.print(output); - }); - output.space(); - self._do_print_body(output); - }); - AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) { - var self = this; - if (!nokeyword) { - output.print("function"); - } - if (self.name) { - output.space(); - self.name.print(output); + DEFPRINT(AST_Directive, function(self, output){ + output.print_string(self.value); + output.semicolon(); + }); + DEFPRINT(AST_Debugger, function(self, output){ + output.print("debugger"); + output.semicolon(); + }); + + /* -----[ statements ]----- */ + + function display_body(body, is_toplevel, output) { + var last = body.length - 1; + body.forEach(function(stmt, i){ + if (!(stmt instanceof AST_EmptyStatement)) { + output.indent(); + stmt.print(output); + if (!(i == last && is_toplevel)) { + output.newline(); + if (is_toplevel) output.newline(); + } } - output.with_parens(function() { - self.argnames.forEach(function(arg, i) { - if (i) output.comma(); - arg.print(output); - }); - }); - output.space(); - print_bracketed(self.body, output); - }); - DEFPRINT(AST_Lambda, function(self, output) { - self._do_print(output); }); - AST_Exit.DEFMETHOD("_do_print", function(output, kind) { - output.print(kind); - if (this.value) { - output.space(); - this.value.print(output); - } - output.semicolon(); + }; + + AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ + force_statement(this.body, output); + }); + + DEFPRINT(AST_Statement, function(self, output){ + self.body.print(output); + output.semicolon(); + }); + DEFPRINT(AST_Toplevel, function(self, output){ + display_body(self.body, true, output); + output.print(""); + }); + DEFPRINT(AST_LabeledStatement, function(self, output){ + self.label.print(output); + output.colon(); + self.body.print(output); + }); + DEFPRINT(AST_SimpleStatement, function(self, output){ + self.body.print(output); + output.semicolon(); + }); + function print_bracketed(body, output) { + if (body.length > 0) output.with_block(function(){ + display_body(body, false, output); }); - DEFPRINT(AST_Return, function(self, output) { - self._do_print(output, "return"); + else output.print("{}"); + }; + DEFPRINT(AST_BlockStatement, function(self, output){ + print_bracketed(self.body, output); + }); + DEFPRINT(AST_EmptyStatement, function(self, output){ + output.semicolon(); + }); + DEFPRINT(AST_Do, function(self, output){ + output.print("do"); + output.space(); + self._do_print_body(output); + output.space(); + output.print("while"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); }); - DEFPRINT(AST_Throw, function(self, output) { - self._do_print(output, "throw"); + output.semicolon(); + }); + DEFPRINT(AST_While, function(self, output){ + output.print("while"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); }); - AST_LoopControl.DEFMETHOD("_do_print", function(output, kind) { - output.print(kind); - if (this.label) { + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_For, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + if (self.init) { + if (self.init instanceof AST_Definitions) { + self.init.print(output); + } else { + parenthesize_for_noin(self.init, output, true); + } + output.print(";"); output.space(); - this.label.print(output); - } - output.semicolon(); - }); - DEFPRINT(AST_Break, function(self, output) { - self._do_print(output, "break"); - }); - DEFPRINT(AST_Continue, function(self, output) { - self._do_print(output, "continue"); - }); - function make_then(self, output) { - if (output.option("bracketize")) { - make_block(self.body, output); - return; - } - if (!self.body) return output.force_semicolon(); - if (self.body instanceof AST_Do && !output.option("screw_ie8")) { - make_block(self.body, output); - return; - } - var b = self.body; - while (true) { - if (b instanceof AST_If) { - if (!b.alternative) { - make_block(self.body, output); - return; - } - b = b.alternative; - } else if (b instanceof AST_StatementWithBody) { - b = b.body; - } else break; + } else { + output.print(";"); } - force_statement(self.body, output); - } - DEFPRINT(AST_If, function(self, output) { - output.print("if"); - output.space(); - output.with_parens(function() { + if (self.condition) { self.condition.print(output); - }); - output.space(); - if (self.alternative) { - make_then(self, output); - output.space(); - output.print("else"); + output.print(";"); output.space(); - force_statement(self.alternative, output); } else { - self._do_print_body(output); - } - }); - DEFPRINT(AST_Switch, function(self, output) { - output.print("switch"); - output.space(); - output.with_parens(function() { - self.expression.print(output); - }); - output.space(); - if (self.body.length > 0) output.with_block(function() { - self.body.forEach(function(stmt, i) { - if (i) output.newline(); - output.indent(true); - stmt.print(output); - }); - }); else output.print("{}"); - }); - AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) { - if (this.body.length > 0) { - output.newline(); - this.body.forEach(function(stmt) { - output.indent(); - stmt.print(output); - output.newline(); - }); - } - }); - DEFPRINT(AST_Default, function(self, output) { - output.print("default:"); - self._do_print_body(output); - }); - DEFPRINT(AST_Case, function(self, output) { - output.print("case"); - output.space(); - self.expression.print(output); - output.print(":"); - self._do_print_body(output); - }); - DEFPRINT(AST_Try, function(self, output) { - output.print("try"); - output.space(); - print_bracketed(self.body, output); - if (self.bcatch) { - output.space(); - self.bcatch.print(output); + output.print(";"); } - if (self.bfinally) { - output.space(); - self.bfinally.print(output); + if (self.step) { + self.step.print(output); } }); - DEFPRINT(AST_Catch, function(self, output) { - output.print("catch"); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_ForIn, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + self.init.print(output); output.space(); - output.with_parens(function() { - self.argname.print(output); - }); + output.print("in"); output.space(); - print_bracketed(self.body, output); + self.object.print(output); }); - DEFPRINT(AST_Finally, function(self, output) { - output.print("finally"); - output.space(); - print_bracketed(self.body, output); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_With, function(self, output){ + output.print("with"); + output.space(); + output.with_parens(function(){ + self.expression.print(output); }); - AST_Definitions.DEFMETHOD("_do_print", function(output, kind) { - output.print(kind); + output.space(); + self._do_print_body(output); + }); + + /* -----[ functions ]----- */ + AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ + var self = this; + if (!nokeyword) { + output.print("function"); + } + if (self.name) { output.space(); - this.definitions.forEach(function(def, i) { + self.name.print(output); + } + output.with_parens(function(){ + self.argnames.forEach(function(arg, i){ if (i) output.comma(); - def.print(output); + arg.print(output); }); - var p = output.parent(); - var in_for = p instanceof AST_For || p instanceof AST_ForIn; - var avoid_semicolon = in_for && p.init === this; - if (!avoid_semicolon) output.semicolon(); }); - DEFPRINT(AST_Var, function(self, output) { - self._do_print(output, "var"); - }); - DEFPRINT(AST_Const, function(self, output) { - self._do_print(output, "const"); - }); - function parenthesize_for_noin(node, output, noin) { - if (!noin) node.print(output); else try { - node.walk(new TreeWalker(function(node) { - if (node instanceof AST_Binary && node.operator == "in") throw output; - })); - node.print(output); - } catch (ex) { - if (ex !== output) throw ex; - node.print(output, true); - } + output.space(); + print_bracketed(self.body, output); + }); + DEFPRINT(AST_Lambda, function(self, output){ + self._do_print(output); + }); + + /* -----[ exits ]----- */ + AST_Exit.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + if (this.value) { + output.space(); + this.value.print(output); } - DEFPRINT(AST_VarDef, function(self, output) { - self.name.print(output); - if (self.value) { - output.space(); - output.print("="); - output.space(); - var p = output.parent(1); - var noin = p instanceof AST_For || p instanceof AST_ForIn; - parenthesize_for_noin(self.value, output, noin); - } - }); - DEFPRINT(AST_Call, function(self, output) { - self.expression.print(output); - if (self instanceof AST_New && no_constructor_parens(self, output)) return; - output.with_parens(function() { - self.args.forEach(function(expr, i) { - if (i) output.comma(); - expr.print(output); - }); - }); - }); - DEFPRINT(AST_New, function(self, output) { - output.print("new"); + output.semicolon(); + }); + DEFPRINT(AST_Return, function(self, output){ + self._do_print(output, "return"); + }); + DEFPRINT(AST_Throw, function(self, output){ + self._do_print(output, "throw"); + }); + + /* -----[ loop control ]----- */ + AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + if (this.label) { output.space(); - AST_Call.prototype._codegen(self, output); - }); - AST_Seq.DEFMETHOD("_do_print", function(output) { - this.car.print(output); - if (this.cdr) { - output.comma(); - if (output.should_break()) { - output.newline(); - output.indent(); + this.label.print(output); + } + output.semicolon(); + }); + DEFPRINT(AST_Break, function(self, output){ + self._do_print(output, "break"); + }); + DEFPRINT(AST_Continue, function(self, output){ + self._do_print(output, "continue"); + }); + + /* -----[ if ]----- */ + function make_then(self, output) { + if (output.option("bracketize")) { + make_block(self.body, output); + return; + } + // The squeezer replaces "block"-s that contain only a single + // statement with the statement itself; technically, the AST + // is correct, but this can create problems when we output an + // IF having an ELSE clause where the THEN clause ends in an + // IF *without* an ELSE block (then the outer ELSE would refer + // to the inner IF). This function checks for this case and + // adds the block brackets if needed. + if (!self.body) + return output.force_semicolon(); + if (self.body instanceof AST_Do + && !output.option("screw_ie8")) { + // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE + // croaks with "syntax error" on code like this: if (foo) + // do ... while(cond); else ... we need block brackets + // around do/while + make_block(self.body, output); + return; + } + var b = self.body; + while (true) { + if (b instanceof AST_If) { + if (!b.alternative) { + make_block(self.body, output); + return; } - this.cdr.print(output); + b = b.alternative; } - }); - DEFPRINT(AST_Seq, function(self, output) { - self._do_print(output); - }); - DEFPRINT(AST_Dot, function(self, output) { - var expr = self.expression; - expr.print(output); - if (expr instanceof AST_Number && expr.getValue() >= 0) { - if (!/[xa-f.]/i.test(output.last())) { - output.print("."); - } + else if (b instanceof AST_StatementWithBody) { + b = b.body; } - output.print("."); - output.add_mapping(self.end); - output.print_name(self.property); - }); - DEFPRINT(AST_Sub, function(self, output) { - self.expression.print(output); - output.print("["); - self.property.print(output); - output.print("]"); + else break; + } + force_statement(self.body, output); + }; + DEFPRINT(AST_If, function(self, output){ + output.print("if"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); }); - DEFPRINT(AST_UnaryPrefix, function(self, output) { - var op = self.operator; - output.print(op); - if (/^[a-z]/i.test(op)) output.space(); + output.space(); + if (self.alternative) { + make_then(self, output); + output.space(); + output.print("else"); + output.space(); + force_statement(self.alternative, output); + } else { + self._do_print_body(output); + } + }); + + /* -----[ switch ]----- */ + DEFPRINT(AST_Switch, function(self, output){ + output.print("switch"); + output.space(); + output.with_parens(function(){ self.expression.print(output); }); - DEFPRINT(AST_UnaryPostfix, function(self, output) { - self.expression.print(output); - output.print(self.operator); + output.space(); + if (self.body.length > 0) output.with_block(function(){ + self.body.forEach(function(stmt, i){ + if (i) output.newline(); + output.indent(true); + stmt.print(output); + }); }); - DEFPRINT(AST_Binary, function(self, output) { - self.left.print(output); + else output.print("{}"); + }); + AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ + if (this.body.length > 0) { + output.newline(); + this.body.forEach(function(stmt){ + output.indent(); + stmt.print(output); + output.newline(); + }); + } + }); + DEFPRINT(AST_Default, function(self, output){ + output.print("default:"); + self._do_print_body(output); + }); + DEFPRINT(AST_Case, function(self, output){ + output.print("case"); + output.space(); + self.expression.print(output); + output.print(":"); + self._do_print_body(output); + }); + + /* -----[ exceptions ]----- */ + DEFPRINT(AST_Try, function(self, output){ + output.print("try"); + output.space(); + print_bracketed(self.body, output); + if (self.bcatch) { output.space(); - output.print(self.operator); + self.bcatch.print(output); + } + if (self.bfinally) { output.space(); - self.right.print(output); + self.bfinally.print(output); + } + }); + DEFPRINT(AST_Catch, function(self, output){ + output.print("catch"); + output.space(); + output.with_parens(function(){ + self.argname.print(output); }); - DEFPRINT(AST_Conditional, function(self, output) { - self.condition.print(output); - output.space(); - output.print("?"); + output.space(); + print_bracketed(self.body, output); + }); + DEFPRINT(AST_Finally, function(self, output){ + output.print("finally"); + output.space(); + print_bracketed(self.body, output); + }); + + /* -----[ var/const ]----- */ + AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + output.space(); + this.definitions.forEach(function(def, i){ + if (i) output.comma(); + def.print(output); + }); + var p = output.parent(); + var in_for = p instanceof AST_For || p instanceof AST_ForIn; + var avoid_semicolon = in_for && p.init === this; + if (!avoid_semicolon) + output.semicolon(); + }); + DEFPRINT(AST_Var, function(self, output){ + self._do_print(output, "var"); + }); + DEFPRINT(AST_Const, function(self, output){ + self._do_print(output, "const"); + }); + + function parenthesize_for_noin(node, output, noin) { + if (!noin) node.print(output); + else try { + // need to take some precautions here: + // https://github.com/mishoo/UglifyJS2/issues/60 + node.walk(new TreeWalker(function(node){ + if (node instanceof AST_Binary && node.operator == "in") + throw output; + })); + node.print(output); + } catch(ex) { + if (ex !== output) throw ex; + node.print(output, true); + } + }; + + DEFPRINT(AST_VarDef, function(self, output){ + self.name.print(output); + if (self.value) { output.space(); - self.consequent.print(output); + output.print("="); output.space(); - output.colon(); - self.alternative.print(output); - }); - DEFPRINT(AST_Array, function(self, output) { - output.with_square(function() { - var a = self.elements, len = a.length; - if (len > 0) output.space(); - a.forEach(function(exp, i) { - if (i) output.comma(); - exp.print(output); - if (i === len - 1 && exp instanceof AST_Hole) output.comma(); - }); - if (len > 0) output.space(); + var p = output.parent(1); + var noin = p instanceof AST_For || p instanceof AST_ForIn; + parenthesize_for_noin(self.value, output, noin); + } + }); + + /* -----[ other expressions ]----- */ + DEFPRINT(AST_Call, function(self, output){ + self.expression.print(output); + if (self instanceof AST_New && no_constructor_parens(self, output)) + return; + output.with_parens(function(){ + self.args.forEach(function(expr, i){ + if (i) output.comma(); + expr.print(output); }); }); - DEFPRINT(AST_Object, function(self, output) { - if (self.properties.length > 0) output.with_block(function() { - self.properties.forEach(function(prop, i) { - if (i) { - output.print(","); - output.newline(); - } - output.indent(); - prop.print(output); - }); + }); + DEFPRINT(AST_New, function(self, output){ + output.print("new"); + output.space(); + AST_Call.prototype._codegen(self, output); + }); + + AST_Seq.DEFMETHOD("_do_print", function(output){ + this.car.print(output); + if (this.cdr) { + output.comma(); + if (output.should_break()) { output.newline(); - }); else output.print("{}"); - }); - DEFPRINT(AST_ObjectKeyVal, function(self, output) { - var key = self.key; - if (output.option("quote_keys")) { - output.print_string(key + ""); - } else if ((typeof key == "number" || !output.option("beautify") && +key + "" == key) && parseFloat(key) >= 0) { - output.print(make_num(key)); - } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) { - output.print_name(key); - } else { - output.print_string(key); + output.indent(); } - output.colon(); - self.value.print(output); - }); - DEFPRINT(AST_ObjectSetter, function(self, output) { - output.print("set"); - self.value._do_print(output, true); - }); - DEFPRINT(AST_ObjectGetter, function(self, output) { - output.print("get"); - self.value._do_print(output, true); - }); - DEFPRINT(AST_Symbol, function(self, output) { - var def = self.definition(); - output.print_name(def ? def.mangled_name || def.name : self.name); - }); - DEFPRINT(AST_Undefined, function(self, output) { - output.print("void 0"); - }); - DEFPRINT(AST_Hole, noop); - DEFPRINT(AST_Infinity, function(self, output) { - output.print("1/0"); - }); - DEFPRINT(AST_NaN, function(self, output) { - output.print("0/0"); - }); - DEFPRINT(AST_This, function(self, output) { - output.print("this"); - }); - DEFPRINT(AST_Constant, function(self, output) { - output.print(self.getValue()); - }); - DEFPRINT(AST_String, function(self, output) { - output.print_string(self.getValue()); - }); - DEFPRINT(AST_Number, function(self, output) { - output.print(make_num(self.getValue())); - }); - DEFPRINT(AST_RegExp, function(self, output) { - var str = self.getValue().toString(); - if (output.option("ascii_only")) str = output.to_ascii(str); - output.print(str); - var p = output.parent(); - if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) output.print(" "); + this.cdr.print(output); + } + }); + DEFPRINT(AST_Seq, function(self, output){ + self._do_print(output); + // var p = output.parent(); + // if (p instanceof AST_Statement) { + // output.with_indent(output.next_indent(), function(){ + // self._do_print(output); + // }); + // } else { + // self._do_print(output); + // } + }); + DEFPRINT(AST_Dot, function(self, output){ + var expr = self.expression; + expr.print(output); + if (expr instanceof AST_Number && expr.getValue() >= 0) { + if (!/[xa-f.]/i.test(output.last())) { + output.print("."); + } + } + output.print("."); + // the name after dot would be mapped about here. + output.add_mapping(self.end); + output.print_name(self.property); + }); + DEFPRINT(AST_Sub, function(self, output){ + self.expression.print(output); + output.print("["); + self.property.print(output); + output.print("]"); + }); + DEFPRINT(AST_UnaryPrefix, function(self, output){ + var op = self.operator; + output.print(op); + if (/^[a-z]/i.test(op)) + output.space(); + self.expression.print(output); + }); + DEFPRINT(AST_UnaryPostfix, function(self, output){ + self.expression.print(output); + output.print(self.operator); + }); + DEFPRINT(AST_Binary, function(self, output){ + self.left.print(output); + output.space(); + output.print(self.operator); + if (self.operator == "<" + && self.right instanceof AST_UnaryPrefix + && self.right.operator == "!" + && self.right.expression instanceof AST_UnaryPrefix + && self.right.expression.operator == "--") { + // space is mandatory to avoid outputting x&&y?z:a + if (consequent instanceof AST_Conditional + && consequent.alternative.equivalent_to(alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: self.condition, + operator: "&&", + right: consequent.condition + }), + consequent: consequent.consequent, + alternative: alternative + }); + } + return self; + }); + + OPT(AST_Boolean, function(self, compressor){ + if (compressor.option("booleans")) { + var p = compressor.parent(); + if (p instanceof AST_Binary && (p.operator == "==" + || p.operator == "!=")) { + compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { + operator : p.operator, + value : self.value, + file : p.start.file, + line : p.start.line, + col : p.start.col, + }); + return make_node(AST_Number, self, { + value: +self.value }); } - return self; - }); - OPT(AST_Boolean, function(self, compressor) { - if (compressor.option("booleans")) { - var p = compressor.parent(); - if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) { - compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { - operator: p.operator, - value: self.value, - file: p.start.file, - line: p.start.line, - col: p.start.col - }); - return make_node(AST_Number, self, { - value: +self.value - }); - } - return make_node(AST_UnaryPrefix, self, { - operator: "!", - expression: make_node(AST_Number, self, { - value: 1 - self.value - }) + return make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: make_node(AST_Number, self, { + value: 1 - self.value + }) + }); + } + return self; + }); + + OPT(AST_Sub, function(self, compressor){ + var prop = self.property; + if (prop instanceof AST_String && compressor.option("properties")) { + prop = prop.getValue(); + if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { + return make_node(AST_Dot, self, { + expression : self.expression, + property : prop }); } - return self; - }); - OPT(AST_Sub, function(self, compressor) { - var prop = self.property; - if (prop instanceof AST_String && compressor.option("properties")) { - prop = prop.getValue(); - if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { - return make_node(AST_Dot, self, { - expression: self.expression, - property: prop - }); - } + var v = parseFloat(prop); + if (!isNaN(v) && v.toString() == prop) { + self.property = make_node(AST_Number, self.property, { + value: v + }); } - return self; - }); - function literals_in_boolean_context(self, compressor) { - if (compressor.option("booleans") && compressor.in_boolean_context()) { - return make_node(AST_True, self); + } + return self; + }); + + function literals_in_boolean_context(self, compressor) { + if (compressor.option("booleans") && compressor.in_boolean_context()) { + return make_node(AST_True, self); + } + return self; + }; + OPT(AST_Array, literals_in_boolean_context); + OPT(AST_Object, literals_in_boolean_context); + OPT(AST_RegExp, literals_in_boolean_context); + +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. + + ***********************************************************************/ + +"use strict"; + +// a small wrapper around fitzgen's source-map library +function SourceMap(options) { + options = defaults(options, { + file : null, + root : null, + orig : null, + + orig_line_diff : 0, + dest_line_diff : 0, + }); + var generator = new MOZ_SourceMap.SourceMapGenerator({ + file : options.file, + sourceRoot : options.root + }); + var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); + function add(source, gen_line, gen_col, orig_line, orig_col, name) { + if (orig_map) { + var info = orig_map.originalPositionFor({ + line: orig_line, + column: orig_col + }); + if (info.source === null) { + return; } - return self; + source = info.source; + orig_line = info.line; + orig_col = info.column; + name = info.name; } - OPT(AST_Array, literals_in_boolean_context); - OPT(AST_Object, literals_in_boolean_context); - OPT(AST_RegExp, literals_in_boolean_context); - })(); - "use strict"; - function SourceMap(options) { - options = defaults(options, { - file: null, - root: null, - orig: null - }); - var generator = new MOZ_SourceMap.SourceMapGenerator({ - file: options.file, - sourceRoot: options.root + generator.addMapping({ + generated : { line: gen_line + options.dest_line_diff, column: gen_col }, + original : { line: orig_line + options.orig_line_diff, column: orig_col }, + source : source, + name : name }); - var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); - function add(source, gen_line, gen_col, orig_line, orig_col, name) { - if (orig_map) { - var info = orig_map.originalPositionFor({ - line: orig_line, - column: orig_col - }); - source = info.source; - orig_line = info.line; - orig_col = info.column; - name = info.name; - } - generator.addMapping({ - generated: { - line: gen_line, - column: gen_col - }, - original: { - line: orig_line, - column: orig_col - }, - source: source, - name: name - }); - } - return { - add: add, - get: function() { - return generator; - }, - toString: function() { - return generator.toString(); - } - }; - } - "use strict"; - (function() { - var MOZ_TO_ME = { - TryStatement: function(M) { - return new AST_Try({ - start: my_start_token(M), - end: my_end_token(M), - body: from_moz(M.block).body, - bcatch: from_moz(M.handlers[0]), - bfinally: M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null - }); - }, - CatchClause: function(M) { - return new AST_Catch({ - start: my_start_token(M), - end: my_end_token(M), - argname: from_moz(M.param), - body: from_moz(M.body).body - }); - }, - ObjectExpression: function(M) { - return new AST_Object({ - start: my_start_token(M), - end: my_end_token(M), - properties: M.properties.map(function(prop) { - var key = prop.key; - var name = key.type == "Identifier" ? key.name : key.value; - var args = { - start: my_start_token(key), - end: my_end_token(prop.value), - key: name, - value: from_moz(prop.value) - }; - switch (prop.kind) { - case "init": - return new AST_ObjectKeyVal(args); + }; + return { + add : add, + get : function() { return generator }, + toString : function() { return generator.toString() } + }; +}; - case "set": - args.value.name = from_moz(key); - return new AST_ObjectSetter(args); +/*********************************************************************** - case "get": - args.value.name = from_moz(key); - return new AST_ObjectGetter(args); - } - }) - }); - }, - SequenceExpression: function(M) { - return AST_Seq.from_array(M.expressions.map(from_moz)); - }, - MemberExpression: function(M) { - return new (M.computed ? AST_Sub : AST_Dot)({ - start: my_start_token(M), - end: my_end_token(M), - property: M.computed ? from_moz(M.property) : M.property.name, - expression: from_moz(M.object) - }); - }, - SwitchCase: function(M) { - return new (M.test ? AST_Case : AST_Default)({ - start: my_start_token(M), - end: my_end_token(M), - expression: from_moz(M.test), - body: M.consequent.map(from_moz) - }); - }, - Literal: function(M) { - var val = M.value, args = { - start: my_start_token(M), - end: my_end_token(M) - }; - if (val === null) return new AST_Null(args); - switch (typeof val) { - case "string": - args.value = val; - return new AST_String(args); + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 - case "number": - args.value = val; - return new AST_Number(args); + -------------------------------- (C) --------------------------------- - case "boolean": - return new (val ? AST_True : AST_False)(args); + Author: Mihai Bazon + + http://mihai.bazon.net/blog - default: - args.value = val; - return new AST_RegExp(args); - } - }, - UnaryExpression: From_Moz_Unary, - UpdateExpression: From_Moz_Unary, - Identifier: function(M) { - var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; - return new (M.name == "this" ? AST_This : p.type == "LabeledStatement" ? AST_Label : p.type == "VariableDeclarator" && p.id === M ? p.kind == "const" ? AST_SymbolConst : AST_SymbolVar : p.type == "FunctionExpression" ? p.id === M ? AST_SymbolLambda : AST_SymbolFunarg : p.type == "FunctionDeclaration" ? p.id === M ? AST_SymbolDefun : AST_SymbolFunarg : p.type == "CatchClause" ? AST_SymbolCatch : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef : AST_SymbolRef)({ - start: my_start_token(M), - end: my_end_token(M), - name: M.name - }); - } - }; - function From_Moz_Unary(M) { - var prefix = "prefix" in M ? M.prefix : M.type == "UnaryExpression" ? true : false; - return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ - start: my_start_token(M), - end: my_end_token(M), - operator: M.operator, - expression: from_moz(M.argument) + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + 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. + + ***********************************************************************/ + +"use strict"; + +(function(){ + + var MOZ_TO_ME = { + TryStatement : function(M) { + return new AST_Try({ + start : my_start_token(M), + end : my_end_token(M), + body : from_moz(M.block).body, + bcatch : from_moz(M.handlers[0]), + bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null }); - } - var ME_TO_MOZ = {}; - map("Node", AST_Node); - map("Program", AST_Toplevel, "body@body"); - map("Function", AST_Function, "id>name, params@argnames, body%body"); - map("EmptyStatement", AST_EmptyStatement); - map("BlockStatement", AST_BlockStatement, "body@body"); - map("ExpressionStatement", AST_SimpleStatement, "expression>body"); - map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); - map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); - map("BreakStatement", AST_Break, "label>label"); - map("ContinueStatement", AST_Continue, "label>label"); - map("WithStatement", AST_With, "object>expression, body>body"); - map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); - map("ReturnStatement", AST_Return, "argument>value"); - map("ThrowStatement", AST_Throw, "argument>value"); - map("WhileStatement", AST_While, "test>condition, body>body"); - map("DoWhileStatement", AST_Do, "test>condition, body>body"); - map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); - map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); - map("DebuggerStatement", AST_Debugger); - map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body"); - map("VariableDeclaration", AST_Var, "declarations@definitions"); - map("VariableDeclarator", AST_VarDef, "id>name, init>value"); - map("ThisExpression", AST_This); - map("ArrayExpression", AST_Array, "elements@elements"); - map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body"); - map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); - map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); - map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); - map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); - map("NewExpression", AST_New, "callee>expression, arguments@args"); - map("CallExpression", AST_Call, "callee>expression, arguments@args"); - function my_start_token(moznode) { - return new AST_Token({ - file: moznode.loc && moznode.loc.source, - line: moznode.loc && moznode.loc.start.line, - col: moznode.loc && moznode.loc.start.column, - pos: moznode.start, - endpos: moznode.start + }, + CatchClause : function(M) { + return new AST_Catch({ + start : my_start_token(M), + end : my_end_token(M), + argname : from_moz(M.param), + body : from_moz(M.body).body }); - } - function my_end_token(moznode) { - return new AST_Token({ - file: moznode.loc && moznode.loc.source, - line: moznode.loc && moznode.loc.end.line, - col: moznode.loc && moznode.loc.end.column, - pos: moznode.end, - endpos: moznode.end + }, + ObjectExpression : function(M) { + return new AST_Object({ + start : my_start_token(M), + end : my_end_token(M), + properties : M.properties.map(function(prop){ + var key = prop.key; + var name = key.type == "Identifier" ? key.name : key.value; + var args = { + start : my_start_token(key), + end : my_end_token(prop.value), + key : name, + value : from_moz(prop.value) + }; + switch (prop.kind) { + case "init": + return new AST_ObjectKeyVal(args); + case "set": + args.value.name = from_moz(key); + return new AST_ObjectSetter(args); + case "get": + args.value.name = from_moz(key); + return new AST_ObjectGetter(args); + } + }) }); - } - function map(moztype, mytype, propmap) { - var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; - moz_to_me += "return new mytype({\n" + "start: my_start_token(M),\n" + "end: my_end_token(M)"; - if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop) { - var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); - if (!m) throw new Error("Can't understand property map: " + prop); - var moz = "M." + m[1], how = m[2], my = m[3]; - moz_to_me += ",\n" + my + ": "; - if (how == "@") { - moz_to_me += moz + ".map(from_moz)"; - } else if (how == ">") { - moz_to_me += "from_moz(" + moz + ")"; - } else if (how == "=") { - moz_to_me += moz; - } else if (how == "%") { - moz_to_me += "from_moz(" + moz + ").body"; - } else throw new Error("Can't understand operator in propmap: " + prop); + }, + SequenceExpression : function(M) { + return AST_Seq.from_array(M.expressions.map(from_moz)); + }, + MemberExpression : function(M) { + return new (M.computed ? AST_Sub : AST_Dot)({ + start : my_start_token(M), + end : my_end_token(M), + property : M.computed ? from_moz(M.property) : M.property.name, + expression : from_moz(M.object) }); - moz_to_me += "\n})}"; - moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(mytype, my_start_token, my_end_token, from_moz); - return MOZ_TO_ME[moztype] = moz_to_me; - } - var FROM_MOZ_STACK = null; - function from_moz(node) { - FROM_MOZ_STACK.push(node); - var ret = node != null ? MOZ_TO_ME[node.type](node) : null; - FROM_MOZ_STACK.pop(); - return ret; + }, + SwitchCase : function(M) { + return new (M.test ? AST_Case : AST_Default)({ + start : my_start_token(M), + end : my_end_token(M), + expression : from_moz(M.test), + body : M.consequent.map(from_moz) + }); + }, + Literal : function(M) { + var val = M.value, args = { + start : my_start_token(M), + end : my_end_token(M) + }; + if (val === null) return new AST_Null(args); + switch (typeof val) { + case "string": + args.value = val; + return new AST_String(args); + case "number": + args.value = val; + return new AST_Number(args); + case "boolean": + return new (val ? AST_True : AST_False)(args); + default: + args.value = val; + return new AST_RegExp(args); + } + }, + UnaryExpression: From_Moz_Unary, + UpdateExpression: From_Moz_Unary, + Identifier: function(M) { + var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; + return new (M.name == "this" ? AST_This + : p.type == "LabeledStatement" ? AST_Label + : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) + : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) + : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) + : p.type == "CatchClause" ? AST_SymbolCatch + : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef + : AST_SymbolRef)({ + start : my_start_token(M), + end : my_end_token(M), + name : M.name + }); } - AST_Node.from_mozilla_ast = function(node) { - var save_stack = FROM_MOZ_STACK; - FROM_MOZ_STACK = []; - var ast = from_moz(node); - FROM_MOZ_STACK = save_stack; - return ast; - }; - })(); - exports["array_to_hash"] = array_to_hash; - exports["slice"] = slice; - exports["characters"] = characters; - exports["member"] = member; - exports["find_if"] = find_if; - exports["repeat_string"] = repeat_string; - exports["DefaultsError"] = DefaultsError; - exports["defaults"] = defaults; - exports["merge"] = merge; - exports["noop"] = noop; - exports["MAP"] = MAP; - exports["push_uniq"] = push_uniq; - exports["string_template"] = string_template; - exports["remove"] = remove; - exports["mergeSort"] = mergeSort; - exports["set_difference"] = set_difference; - exports["set_intersection"] = set_intersection; - exports["makePredicate"] = makePredicate; - exports["all"] = all; - exports["Dictionary"] = Dictionary; - exports["DEFNODE"] = DEFNODE; - exports["AST_Token"] = AST_Token; - exports["AST_Node"] = AST_Node; - exports["AST_Statement"] = AST_Statement; - exports["AST_Debugger"] = AST_Debugger; - exports["AST_Directive"] = AST_Directive; - exports["AST_SimpleStatement"] = AST_SimpleStatement; - exports["walk_body"] = walk_body; - exports["AST_Block"] = AST_Block; - exports["AST_BlockStatement"] = AST_BlockStatement; - exports["AST_EmptyStatement"] = AST_EmptyStatement; - exports["AST_StatementWithBody"] = AST_StatementWithBody; - exports["AST_LabeledStatement"] = AST_LabeledStatement; - exports["AST_DWLoop"] = AST_DWLoop; - exports["AST_Do"] = AST_Do; - exports["AST_While"] = AST_While; - exports["AST_For"] = AST_For; - exports["AST_ForIn"] = AST_ForIn; - exports["AST_With"] = AST_With; - exports["AST_Scope"] = AST_Scope; - exports["AST_Toplevel"] = AST_Toplevel; - exports["AST_Lambda"] = AST_Lambda; - exports["AST_Accessor"] = AST_Accessor; - exports["AST_Function"] = AST_Function; - exports["AST_Defun"] = AST_Defun; - exports["AST_Jump"] = AST_Jump; - exports["AST_Exit"] = AST_Exit; - exports["AST_Return"] = AST_Return; - exports["AST_Throw"] = AST_Throw; - exports["AST_LoopControl"] = AST_LoopControl; - exports["AST_Break"] = AST_Break; - exports["AST_Continue"] = AST_Continue; - exports["AST_If"] = AST_If; - exports["AST_Switch"] = AST_Switch; - exports["AST_SwitchBranch"] = AST_SwitchBranch; - exports["AST_Default"] = AST_Default; - exports["AST_Case"] = AST_Case; - exports["AST_Try"] = AST_Try; - exports["AST_Catch"] = AST_Catch; - exports["AST_Finally"] = AST_Finally; - exports["AST_Definitions"] = AST_Definitions; - exports["AST_Var"] = AST_Var; - exports["AST_Const"] = AST_Const; - exports["AST_VarDef"] = AST_VarDef; - exports["AST_Call"] = AST_Call; - exports["AST_New"] = AST_New; - exports["AST_Seq"] = AST_Seq; - exports["AST_PropAccess"] = AST_PropAccess; - exports["AST_Dot"] = AST_Dot; - exports["AST_Sub"] = AST_Sub; - exports["AST_Unary"] = AST_Unary; - exports["AST_UnaryPrefix"] = AST_UnaryPrefix; - exports["AST_UnaryPostfix"] = AST_UnaryPostfix; - exports["AST_Binary"] = AST_Binary; - exports["AST_Conditional"] = AST_Conditional; - exports["AST_Assign"] = AST_Assign; - exports["AST_Array"] = AST_Array; - exports["AST_Object"] = AST_Object; - exports["AST_ObjectProperty"] = AST_ObjectProperty; - exports["AST_ObjectKeyVal"] = AST_ObjectKeyVal; - exports["AST_ObjectSetter"] = AST_ObjectSetter; - exports["AST_ObjectGetter"] = AST_ObjectGetter; - exports["AST_Symbol"] = AST_Symbol; - exports["AST_SymbolAccessor"] = AST_SymbolAccessor; - exports["AST_SymbolDeclaration"] = AST_SymbolDeclaration; - exports["AST_SymbolVar"] = AST_SymbolVar; - exports["AST_SymbolConst"] = AST_SymbolConst; - exports["AST_SymbolFunarg"] = AST_SymbolFunarg; - exports["AST_SymbolDefun"] = AST_SymbolDefun; - exports["AST_SymbolLambda"] = AST_SymbolLambda; - exports["AST_SymbolCatch"] = AST_SymbolCatch; - exports["AST_Label"] = AST_Label; - exports["AST_SymbolRef"] = AST_SymbolRef; - exports["AST_LabelRef"] = AST_LabelRef; - exports["AST_This"] = AST_This; - exports["AST_Constant"] = AST_Constant; - exports["AST_String"] = AST_String; - exports["AST_Number"] = AST_Number; - exports["AST_RegExp"] = AST_RegExp; - exports["AST_Atom"] = AST_Atom; - exports["AST_Null"] = AST_Null; - exports["AST_NaN"] = AST_NaN; - exports["AST_Undefined"] = AST_Undefined; - exports["AST_Hole"] = AST_Hole; - exports["AST_Infinity"] = AST_Infinity; - exports["AST_Boolean"] = AST_Boolean; - exports["AST_False"] = AST_False; - exports["AST_True"] = AST_True; - exports["TreeWalker"] = TreeWalker; - exports["KEYWORDS"] = KEYWORDS; - exports["KEYWORDS_ATOM"] = KEYWORDS_ATOM; - exports["RESERVED_WORDS"] = RESERVED_WORDS; - exports["KEYWORDS_BEFORE_EXPRESSION"] = KEYWORDS_BEFORE_EXPRESSION; - exports["OPERATOR_CHARS"] = OPERATOR_CHARS; - exports["RE_HEX_NUMBER"] = RE_HEX_NUMBER; - exports["RE_OCT_NUMBER"] = RE_OCT_NUMBER; - exports["RE_DEC_NUMBER"] = RE_DEC_NUMBER; - exports["OPERATORS"] = OPERATORS; - exports["WHITESPACE_CHARS"] = WHITESPACE_CHARS; - exports["PUNC_BEFORE_EXPRESSION"] = PUNC_BEFORE_EXPRESSION; - exports["PUNC_CHARS"] = PUNC_CHARS; - exports["REGEXP_MODIFIERS"] = REGEXP_MODIFIERS; - exports["UNICODE"] = UNICODE; - exports["is_letter"] = is_letter; - exports["is_digit"] = is_digit; - exports["is_alphanumeric_char"] = is_alphanumeric_char; - exports["is_unicode_combining_mark"] = is_unicode_combining_mark; - exports["is_unicode_connector_punctuation"] = is_unicode_connector_punctuation; - exports["is_identifier"] = is_identifier; - exports["is_identifier_start"] = is_identifier_start; - exports["is_identifier_char"] = is_identifier_char; - exports["is_identifier_string"] = is_identifier_string; - exports["parse_js_number"] = parse_js_number; - exports["JS_Parse_Error"] = JS_Parse_Error; - exports["js_error"] = js_error; - exports["is_token"] = is_token; - exports["EX_EOF"] = EX_EOF; - exports["tokenizer"] = tokenizer; - exports["UNARY_PREFIX"] = UNARY_PREFIX; - exports["UNARY_POSTFIX"] = UNARY_POSTFIX; - exports["ASSIGNMENT"] = ASSIGNMENT; - exports["PRECEDENCE"] = PRECEDENCE; - exports["STATEMENTS_WITH_LABELS"] = STATEMENTS_WITH_LABELS; - exports["ATOMIC_START_TOKEN"] = ATOMIC_START_TOKEN; - exports["parse"] = parse; - exports["TreeTransformer"] = TreeTransformer; - exports["SymbolDef"] = SymbolDef; - exports["base54"] = base54; - exports["OutputStream"] = OutputStream; - exports["Compressor"] = Compressor; - exports["SourceMap"] = SourceMap; -})({}, function() { - return exports; -}()); + }; + + function From_Moz_Unary(M) { + var prefix = "prefix" in M ? M.prefix + : M.type == "UnaryExpression" ? true : false; + return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ + start : my_start_token(M), + end : my_end_token(M), + operator : M.operator, + expression : from_moz(M.argument) + }); + }; + + var ME_TO_MOZ = {}; + + map("Node", AST_Node); + map("Program", AST_Toplevel, "body@body"); + map("Function", AST_Function, "id>name, params@argnames, body%body"); + map("EmptyStatement", AST_EmptyStatement); + map("BlockStatement", AST_BlockStatement, "body@body"); + map("ExpressionStatement", AST_SimpleStatement, "expression>body"); + map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); + map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); + map("BreakStatement", AST_Break, "label>label"); + map("ContinueStatement", AST_Continue, "label>label"); + map("WithStatement", AST_With, "object>expression, body>body"); + map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); + map("ReturnStatement", AST_Return, "argument>value"); + map("ThrowStatement", AST_Throw, "argument>value"); + map("WhileStatement", AST_While, "test>condition, body>body"); + map("DoWhileStatement", AST_Do, "test>condition, body>body"); + map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); + map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); + map("DebuggerStatement", AST_Debugger); + map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body"); + map("VariableDeclaration", AST_Var, "declarations@definitions"); + map("VariableDeclarator", AST_VarDef, "id>name, init>value"); + + map("ThisExpression", AST_This); + map("ArrayExpression", AST_Array, "elements@elements"); + map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body"); + map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); + map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); + map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); + map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); + map("NewExpression", AST_New, "callee>expression, arguments@args"); + map("CallExpression", AST_Call, "callee>expression, arguments@args"); + + /* -----[ tools ]----- */ + + function my_start_token(moznode) { + return new AST_Token({ + file : moznode.loc && moznode.loc.source, + line : moznode.loc && moznode.loc.start.line, + col : moznode.loc && moznode.loc.start.column, + pos : moznode.start, + endpos : moznode.start + }); + }; -var UglifyJS = exports.UglifyJS; + function my_end_token(moznode) { + return new AST_Token({ + file : moznode.loc && moznode.loc.source, + line : moznode.loc && moznode.loc.end.line, + col : moznode.loc && moznode.loc.end.column, + pos : moznode.end, + endpos : moznode.end + }); + }; -UglifyJS.AST_Node.warn_function = function(txt) { - logger.error("uglifyjs2 WARN: " + txt); -}; + function map(moztype, mytype, propmap) { + var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; + moz_to_me += "return new mytype({\n" + + "start: my_start_token(M),\n" + + "end: my_end_token(M)"; + + if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ + var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); + if (!m) throw new Error("Can't understand property map: " + prop); + var moz = "M." + m[1], how = m[2], my = m[3]; + moz_to_me += ",\n" + my + ": "; + if (how == "@") { + moz_to_me += moz + ".map(from_moz)"; + } else if (how == ">") { + moz_to_me += "from_moz(" + moz + ")"; + } else if (how == "=") { + moz_to_me += moz; + } else if (how == "%") { + moz_to_me += "from_moz(" + moz + ").body"; + } else throw new Error("Can't understand operator in propmap: " + prop); + }); + moz_to_me += "\n})}"; + + // moz_to_me = parse(moz_to_me).print_to_string({ beautify: true }); + // console.log(moz_to_me); + + moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( + mytype, my_start_token, my_end_token, from_moz + ); + return MOZ_TO_ME[moztype] = moz_to_me; + }; + + var FROM_MOZ_STACK = null; + + function from_moz(node) { + FROM_MOZ_STACK.push(node); + var ret = node != null ? MOZ_TO_ME[node.type](node) : null; + FROM_MOZ_STACK.pop(); + return ret; + }; + + AST_Node.from_mozilla_ast = function(node){ + var save_stack = FROM_MOZ_STACK; + FROM_MOZ_STACK = []; + var ast = from_moz(node); + FROM_MOZ_STACK = save_stack; + return ast; + }; + +})(); -//JRB: MODIFIED FROM UGLIFY SOURCE -//to take a name for the file, and then set toplevel.filename to be that name. +AST_Node.warn_function = function(txt) { logger.error("uglifyjs2 WARN: " + txt); }; exports.minify = function(files, options, name) { - options = UglifyJS.defaults(options, { + options = defaults(options, { + spidermonkey : false, outSourceMap : null, sourceRoot : null, inSourceMap : null, @@ -21019,29 +22615,35 @@ exports.minify = function(files, options, name) { output : null, compress : {} }); - if (typeof files == "string") - files = [ files ]; - - UglifyJS.base54.reset(); + base54.reset(); // 1. parse - var toplevel = null; - files.forEach(function(file){ - var code = options.fromString - ? file - : rjsFile.readFile(file, "utf8"); - toplevel = UglifyJS.parse(code, { - filename: options.fromString ? name : file, - toplevel: toplevel + var toplevel = null, + sourcesContent = {}; + + if (options.spidermonkey) { + toplevel = AST_Node.from_mozilla_ast(files); + } else { + if (typeof files == "string") + files = [ files ]; + files.forEach(function(file){ + var code = options.fromString + ? file + : rjsFile.readFile(file, "utf8"); + sourcesContent[file] = code; + toplevel = parse(code, { + filename: options.fromString ? name : file, + toplevel: toplevel + }); }); - }); + } // 2. compress if (options.compress) { var compress = { warnings: options.warnings }; - UglifyJS.merge(compress, options.compress); + merge(compress, options.compress); toplevel.figure_out_scope(); - var sq = UglifyJS.Compressor(compress); + var sq = Compressor(compress); toplevel = toplevel.transform(sq); } @@ -21059,16 +22661,24 @@ exports.minify = function(files, options, name) { inMap = rjsFile.readFile(options.inSourceMap, "utf8"); } if (options.outSourceMap) { - output.source_map = UglifyJS.SourceMap({ + output.source_map = SourceMap({ file: options.outSourceMap, orig: inMap, root: options.sourceRoot }); + if (options.sourceMapIncludeSources) { + for (var file in sourcesContent) { + if (sourcesContent.hasOwnProperty(file)) { + options.source_map.get().setSourceContent(file, sourcesContent[file]); + } + } + } + } if (options.output) { - UglifyJS.merge(output, options.output); + merge(output, options.output); } - var stream = UglifyJS.OutputStream(output); + var stream = OutputStream(output); toplevel.print(stream); return { code : stream + "", @@ -21087,11 +22697,11 @@ exports.minify = function(files, options, name) { // if (ctor.SUBCLASSES.length > 0) ret.sub = sub; // return ret; // } -// return doitem(UglifyJS.AST_Node).sub; +// return doitem(AST_Node).sub; // } exports.describe_ast = function() { - var out = UglifyJS.OutputStream({ beautify: true }); + var out = OutputStream({ beautify: true }); function doitem(ctor) { out.print("AST_" + ctor.TYPE); var props = ctor.SELF_PROPS.filter(function(prop){ @@ -21121,13 +22731,14 @@ exports.describe_ast = function() { }); } }; - doitem(UglifyJS.AST_Node); + doitem(AST_Node); return out + ""; }; + }); /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -21152,7 +22763,11 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { //This string is saved off because JSLint complains //about obj.arguments use, as 'reserved word' - var argPropName = 'arguments'; + var argPropName = 'arguments', + //Default object to use for "scope" checking for UMD identifiers. + emptyScope = {}, + mixin = lang.mixin, + hasProp = lang.hasProp; //From an esprima example for traversing its ast. function traverse(object, visitor) { @@ -21194,7 +22809,7 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { if (object.hasOwnProperty(key)) { child = object[key]; if (typeof child === 'object' && child !== null) { - traverse(child, visitor); + traverseBroad(child, visitor); } } } @@ -21254,7 +22869,7 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { needsDefine = true, astRoot = esprima.parse(fileContents); - parse.recurse(astRoot, function (callName, config, name, deps) { + parse.recurse(astRoot, function (callName, config, name, deps, node, factoryIdentifier, fnExpScope) { if (!deps) { deps = []; } @@ -21274,6 +22889,10 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { }); } + if (callName === 'define' && factoryIdentifier && hasProp(fnExpScope, factoryIdentifier)) { + return factoryIdentifier; + } + //If define was found, no need to dive deeper, unless //the config explicitly wants to dig deeper. return !!options.findNestedDependencies; @@ -21323,14 +22942,18 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { * @param {Function} onMatch function to call on a parse match. * @param {Object} [options] This is normally the build config options if * it is passed. + * @param {Object} [fnExpScope] holds list of function expresssion + * argument identifiers, set up internally, not passed in */ - parse.recurse = function (object, onMatch, options) { + parse.recurse = function (object, onMatch, options, fnExpScope) { //Like traverse, but skips if branches that would not be processed //after has application that results in tests of true or false boolean //literal values. - var key, child, + var key, child, result, i, params, param, hasHas = options && options.has; + fnExpScope = fnExpScope || emptyScope; + if (!object) { return; } @@ -21341,23 +22964,63 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { object.test.type === 'Literal') { if (object.test.value) { //Take the if branch - this.recurse(object.consequent, onMatch, options); + this.recurse(object.consequent, onMatch, options, fnExpScope); } else { //Take the else branch - this.recurse(object.alternate, onMatch, options); + this.recurse(object.alternate, onMatch, options, fnExpScope); } } else { - if (this.parseNode(object, onMatch) === false) { + result = this.parseNode(object, onMatch, fnExpScope); + if (result === false) { return; + } else if (typeof result === 'string') { + return result; } + + //Build up a "scope" object that informs nested recurse calls if + //the define call references an identifier that is likely a UMD + //wrapped function expresion argument. + if (object.type === 'ExpressionStatement' && object.expression && + object.expression.type === 'CallExpression' && object.expression.callee && + object.expression.callee.type === 'FunctionExpression') { + object = object.expression.callee; + + if (object.params && object.params.length) { + params = object.params; + fnExpScope = mixin({}, fnExpScope, true); + for (i = 0; i < params.length; i++) { + param = params[i]; + if (param.type === 'Identifier') { + fnExpScope[param.name] = true; + } + } + } + } + for (key in object) { if (object.hasOwnProperty(key)) { child = object[key]; if (typeof child === 'object' && child !== null) { - this.recurse(child, onMatch, options); + result = this.recurse(child, onMatch, options, fnExpScope); + if (typeof result === 'string') { + break; + } } } } + + //Check for an identifier for a factory function identifier being + //passed in as a function expression, indicating a UMD-type of + //wrapping. + if (typeof result === 'string') { + if (hasProp(fnExpScope, result)) { + //Just a plain return, parsing can continue past this + //point. + return; + } + + return result; + } } }; @@ -21831,11 +23494,14 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { * @param {Function} onMatch a function to call when a match is found. * It is passed the match name, and the config, name, deps possible args. * The config, name and deps args are not normalized. + * @param {Object} fnExpScope an object whose keys are all function + * expression identifiers that should be in scope. Useful for UMD wrapper + * detection to avoid parsing more into the wrapped UMD code. * * @returns {String} a JS source string with the valid require/define call. * Otherwise null. */ - parse.parseNode = function (node, onMatch) { + parse.parseNode = function (node, onMatch, fnExpScope) { var name, deps, cjsDeps, arg, factory, exp, refsDefine, bodyNode, args = node && node[argPropName], callName = parse.hasRequire(node); @@ -21908,7 +23574,9 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { name = name.value; } - return onMatch("define", null, name, deps, node); + return onMatch("define", null, name, deps, node, + (factory && factory.type === 'Identifier' ? factory.name : undefined), + fnExpScope); } else if (node.type === 'CallExpression' && node.callee && node.callee.type === 'FunctionExpression' && node.callee.body && node.callee.body.body && @@ -21935,7 +23603,8 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { }); if (refsDefine) { - return onMatch("define", null, null, null, exp.expression); + return onMatch("define", null, null, null, exp.expression, + exp.expression.arguments[0].name, fnExpScope); } } } @@ -22053,7 +23722,7 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { return parse; }); /** - * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -22085,7 +23754,16 @@ function (esprima, parse, logger, lang) { foundAnon, scanCount = 0, scanReset = false, - defineInfos = []; + defineInfos = [], + applySourceUrl = function (contents) { + if (options.useSourceUrl) { + contents = 'eval("' + lang.jsEscape(contents) + + '\\n//# sourceURL=' + (path.indexOf('/') === 0 ? '' : '/') + + path + + '");\n'; + } + return contents; + }; try { astRoot = esprima.parse(contents, { @@ -22098,12 +23776,35 @@ function (esprima, parse, logger, lang) { } //Find the define calls and their position in the files. - parse.traverseBroad(astRoot, function (node) { + parse.traverse(astRoot, function (node) { var args, firstArg, firstArgLoc, factoryNode, - needsId, depAction, foundId, + needsId, depAction, foundId, init, sourceUrlData, range, namespaceExists = false; + // If a bundle script with a define declaration, do not + // parse any further at this level. Likely a built layer + // by some other tool. + if (node.type === 'VariableDeclarator' && + node.id && node.id.name === 'define' && + node.id.type === 'Identifier') { + init = node.init; + if (init && init.callee && + init.callee.type === 'CallExpression' && + init.callee.callee && + init.callee.callee.type === 'Identifier' && + init.callee.callee.name === 'require' && + init.callee.arguments && init.callee.arguments.length === 1 && + init.callee.arguments[0].type === 'Literal' && + init.callee.arguments[0].value && + init.callee.arguments[0].value.indexOf('amdefine') !== -1) { + // the var define = require('amdefine')(module) case, + // keep going in that case. + } else { + return false; + } + } + namespaceExists = namespace && node.type === 'CallExpression' && node.callee && node.callee.object && @@ -22228,8 +23929,9 @@ function (esprima, parse, logger, lang) { } }); + if (!defineInfos.length) { - return contents; + return applySourceUrl(contents); } //Reverse the matches, need to start from the bottom of @@ -22301,14 +24003,7 @@ function (esprima, parse, logger, lang) { contents = contentLines.join('\n'); - if (options.useSourceUrl) { - contents = 'eval("' + lang.jsEscape(contents) + - '\\n//# sourceURL=' + (path.indexOf('/') === 0 ? '' : '/') + - path + - '");\n'; - } - - return contents; + return applySourceUrl(contents); }, /** @@ -22471,7 +24166,7 @@ function (esprima, parse, logger, lang) { return transform; }); /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -22508,12 +24203,12 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { configRegExp: /(^|[^\.])(requirejs|require)(\.config)\s*\(/g, nsWrapRegExp: /\/\*requirejs namespace: true \*\//, apiDefRegExp: /var requirejs,\s*require,\s*define;/, - defineCheckRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\.\s*amd/g, - defineStringCheckRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\[\s*["']amd["']\s*\]/g, + defineCheckRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\.\s*amd/g, + defineStringCheckRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\[\s*["']amd["']\s*\]/g, defineTypeFirstCheckRegExp: /\s*["']function["']\s*==(=?)\s*typeof\s+define\s*&&\s*define\s*\.\s*amd/g, - defineJQueryRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\.\s*amd\s*&&\s*define\s*\.\s*amd\s*\.\s*jQuery/g, + defineJQueryRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\.\s*amd\s*&&\s*define\s*\.\s*amd\s*\.\s*jQuery/g, defineHasRegExp: /typeof\s+define\s*==(=)?\s*['"]function['"]\s*&&\s*typeof\s+define\.amd\s*==(=)?\s*['"]object['"]\s*&&\s*define\.amd/g, - defineTernaryRegExp: /typeof\s+define\s*===\s*['"]function["']\s*&&\s*define\s*\.\s*amd\s*\?\s*define/, + defineTernaryRegExp: /typeof\s+define\s*===?\s*['"]function["']\s*&&\s*define\s*\.\s*amd\s*\?\s*define/, amdefineRegExp: /if\s*\(\s*typeof define\s*\!==\s*'function'\s*\)\s*\{\s*[^\{\}]+amdefine[^\{\}]+\}/g, removeStrict: function (contents, config) { @@ -22720,7 +24415,9 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { } //Strip amdefine use for node-shared modules. - fileContents = fileContents.replace(pragma.amdefineRegExp, ''); + if (!config.keepAmdefine) { + fileContents = fileContents.replace(pragma.amdefineRegExp, ''); + } //Do namespacing if (onLifecycleName === 'OnSave' && config.namespace) { @@ -22736,7 +24433,7 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { }); if(env === 'browser') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -22750,7 +24447,7 @@ define('browser/optimize', {}); if(env === 'node') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -22764,7 +24461,7 @@ define('node/optimize', {}); if(env === 'rhino') { /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -22809,8 +24506,14 @@ define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { //Bind to Closure compiler, but if it is not available, do not sweat it. try { + // Try older closure compiler that worked on Java 6 JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.JSSourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]); - } catch (e) {} + } catch (e) { + try { + // Try for newer closure compiler that needs Java 7+ + JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.SourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]); + } catch (e) {} + } //Helper for closure compiler, because of weird Java-JavaScript interactions. function closurefromCode(filename, content) { @@ -22940,7 +24643,7 @@ if(env === 'xpconnect') { define('xpconnect/optimize', {}); } /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -22960,6 +24663,7 @@ function (lang, logger, envOptimize, file, parse, cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/ig, cssCommentImportRegExp = /\/\*[^\*]*@import[^\*]*\*\//g, cssUrlRegExp = /\url\(\s*([^\)]+)\s*\)?/g, + protocolRegExp = /^\w+:/, SourceMapGenerator = sourceMap.SourceMapGenerator, SourceMapConsumer =sourceMap.SourceMapConsumer; @@ -22984,19 +24688,19 @@ function (lang, logger, envOptimize, file, parse, function fixCssUrlPaths(fileName, path, contents, cssPrefix) { return contents.replace(cssUrlRegExp, function (fullMatch, urlMatch) { - var colonIndex, parts, i, + var firstChar, hasProtocol, parts, i, fixedUrlMatch = cleanCssUrlQuotes(urlMatch); fixedUrlMatch = fixedUrlMatch.replace(lang.backSlashRegExp, "/"); - //Only do the work for relative URLs. Skip things that start with / or have + //Only do the work for relative URLs. Skip things that start with / or #, or have //a protocol. - colonIndex = fixedUrlMatch.indexOf(":"); - if (fixedUrlMatch.charAt(0) !== "/" && (colonIndex === -1 || colonIndex > fixedUrlMatch.indexOf("/"))) { + firstChar = fixedUrlMatch.charAt(0); + hasProtocol = protocolRegExp.test(fixedUrlMatch); + if (firstChar !== "/" && firstChar !== "#" && !hasProtocol) { //It is a relative URL, tack on the cssPrefix and path prefix urlMatch = cssPrefix + path + fixedUrlMatch; - - } else { + } else if (!hasProtocol) { logger.trace(fileName + "\n URL not a relative URL, skipping: " + urlMatch); } @@ -23180,6 +24884,7 @@ function (lang, logger, envOptimize, file, parse, optConfig = config[optimizerName] || {}; if (config.generateSourceMaps) { optConfig.generateSourceMaps = !!config.generateSourceMaps; + optConfig._buildSourceMap = config._buildSourceMap; } try { @@ -23197,6 +24902,9 @@ function (lang, logger, envOptimize, file, parse, outFileName, keepLines, optConfig); + if (optConfig._buildSourceMap && optConfig._buildSourceMap !== config._buildSourceMap) { + config._buildSourceMap = optConfig._buildSourceMap; + } } catch (e) { if (config.throwWhen && config.throwWhen.optimize) { throw e; @@ -23204,6 +24912,10 @@ function (lang, logger, envOptimize, file, parse, logger.error(e); } } + } else { + if (config._buildSourceMap) { + config._buildSourceMap = null; + } } return fileContents; @@ -23267,6 +24979,20 @@ function (lang, logger, envOptimize, file, parse, fileContents = fileContents.replace(/(\r\n)+/g, "\r\n"); fileContents = fileContents.replace(/(\n)+/g, "\n"); } + //Remove unnecessary whitespace + if (config.optimizeCss.indexOf(".keepWhitespace") === -1) { + //Remove leading and trailing whitespace from lines + fileContents = fileContents.replace(/^[ \t]+/gm, ""); + fileContents = fileContents.replace(/[ \t]+$/gm, ""); + //Remove whitespace after semicolon, colon, curly brackets and commas + fileContents = fileContents.replace(/(;|:|\{|}|,)[ \t]+/g, "$1"); + //Remove whitespace before opening curly brackets + fileContents = fileContents.replace(/[ \t]+(\{)/g, "$1"); + //Truncate double whitespace + fileContents = fileContents.replace(/([ \t])+/g, "$1"); + //Remove empty lines + fileContents = fileContents.replace(/^[ \t]*[\r\n]/gm,''); + } } catch (e) { fileContents = originalFileContents; logger.error("Could not optimized CSS file: " + fileName + ", error: " + e); @@ -23372,10 +25098,13 @@ function (lang, logger, envOptimize, file, parse, uconfig.fromString = true; - if (config.generateSourceMaps && outFileName) { + if (config.generateSourceMaps && (outFileName || config._buildSourceMap)) { uconfig.outSourceMap = baseName; - if (file.exists(existingMapPath)) { + if (config._buildSourceMap) { + existingMap = JSON.parse(config._buildSourceMap); + uconfig.inSourceMap = existingMap; + } else if (file.exists(existingMapPath)) { uconfig.inSourceMap = existingMapPath; existingMap = JSON.parse(file.readFile(existingMapPath)); } @@ -23393,11 +25122,18 @@ function (lang, logger, envOptimize, file, parse, finalMap = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(resultMap)); finalMap.applySourceMap(new SourceMapConsumer(existingMap)); resultMap = finalMap.toString(); - } else { + } else if (!config._buildSourceMap) { file.saveFile(outFileName + '.src.js', fileContents); } - file.saveFile(outFileName + '.map', resultMap); - fileContents = result.code + "\n//# sourceMappingURL=" + baseName + ".map"; + + fileContents = result.code; + + if (config._buildSourceMap) { + config._buildSourceMap = resultMap; + } else { + file.saveFile(outFileName + '.map', resultMap); + fileContents += "\n//# sourceMappingURL=" + baseName + ".map"; + } } else { fileContents = result.code; } @@ -23412,7 +25148,7 @@ function (lang, logger, envOptimize, file, parse, return optimize; }); /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -23443,6 +25179,11 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c falseProp = lang.falseProp, getOwn = lang.getOwn; + //Turn off throwing on resolution conflict, that was just an older prim + //idea about finding errors early, but does not comply with how promises + //should operate. + prim.hideResolutionConflict = true; + //This method should be called when the patches to require should take hold. return function () { if (!allowRun) { @@ -23534,18 +25275,45 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c //spit out strings that can be used in the stringified //build output. context.makeShimExports = function (value) { - function fn() { - return '(function (global) {\n' + - ' return function () {\n' + - ' var ret, fn;\n' + - (value.init ? - (' fn = ' + value.init.toString() + ';\n' + - ' ret = fn.apply(global, arguments);\n') : '') + - (value.exports ? - ' return ret || global.' + value.exports + ';\n' : - ' return ret;\n') + - ' };\n' + - '}(this))'; + var fn; + if (context.config.wrapShim) { + fn = function () { + var str = 'return '; + // If specifies an export that is just a global + // name, no dot for a `this.` and such, then also + // attach to the global, for `var a = {}` files + // where the function closure would hide that from + // the global object. + if (value.exports && value.exports.indexOf('.') === -1) { + str += 'root.' + value.exports + ' = '; + } + + if (value.init) { + str += '(' + value.init.toString() + '.apply(this, arguments))'; + } + if (value.init && value.exports) { + str += ' || '; + } + if (value.exports) { + str += value.exports; + } + str += ';'; + return str; + }; + } else { + fn = function () { + return '(function (global) {\n' + + ' return function () {\n' + + ' var ret, fn;\n' + + (value.init ? + (' fn = ' + value.init.toString() + ';\n' + + ' ret = fn.apply(global, arguments);\n') : '') + + (value.exports ? + ' return ret || global.' + value.exports + ';\n' : + ' return ret;\n') + + ' };\n' + + '}(this))'; + }; } return fn; @@ -23560,7 +25328,7 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c if (mod && !mod.defined) { if (parentId && getOwn(needFullExec, parentId)) { - needFullExec[id] = true; + needFullExec[id] = depMap; } } else if ((getOwn(needFullExec, id) && falseProp(fullExec, id)) || @@ -23765,7 +25533,7 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c pluginMod = getOwn(context.registry, pluginId); context.plugins[pluginId] = true; - context.needFullExec[pluginId] = true; + context.needFullExec[pluginId] = map; //If the module is not waiting to finish being defined, //undef it and start over, to get full execution. @@ -23842,13 +25610,26 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c var id = map.id, url; + // Fix up any maps that need to be normalized as part of the fullExec + // plumbing for plugins to participate in the build. + if (context.plugins && lang.hasProp(context.plugins, id)) { + lang.eachProp(context.needFullExec, function(value, prop) { + // For plugin entries themselves, they do not have a map + // value in needFullExec, just a "true" entry. + if (value !== true && value.prefix === id && value.unnormalized) { + var map = context.makeModuleMap(value.originalName, value.parentMap); + context.needFullExec[map.id] = map; + } + }); + } + //If build needed a full execution, indicate it //has been done now. But only do it if the context is tracking //that. Only valid for the context used in a build, not for //other contexts being run, like for useLib, plain requirejs //use in node/rhino. if (context.needFullExec && getOwn(context.needFullExec, id)) { - context.fullExec[id] = true; + context.fullExec[id] = map; } //A plugin. @@ -23884,7 +25665,7 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c }; }); /** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license RequireJS Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -23988,19 +25769,19 @@ define('commonJs', ['env!env/file', 'parse'], function (file, parse) { return commonJs; }); /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ /*jslint plusplus: true, nomen: true, regexp: true */ -/*global define, requirejs */ +/*global define, requirejs, java, process, console */ define('build', function (require) { 'use strict'; - var build, buildBaseConfig, + var build, lang = require('lang'), prim = require('prim'), logger = require('logger'), @@ -24017,6 +25798,7 @@ define('build', function (require) { getOwn = lang.getOwn, falseProp = lang.falseProp, endsWithSemiColonRegExp = /;\s*$/, + endsWithSlashRegExp = /[\/\\]$/, resourceIsModuleIdRegExp = /^[\w\/\\\.]+$/; prim.nextTick = function (fn) { @@ -24046,22 +25828,24 @@ define('build', function (require) { } }; - buildBaseConfig = { - appDir: "", - pragmas: {}, - paths: {}, - optimize: "uglify", - optimizeCss: "standard.keepLines", - inlineText: true, - isBuild: true, - optimizeAllPluginResources: false, - findNestedDependencies: false, - preserveLicenseComments: true, - //By default, all files/directories are copied, unless - //they match this regexp, by default just excludes .folders - dirExclusionRegExp: file.dirExclusionRegExp, - _buildPathToModuleIndex: {} - }; + function makeBuildBaseConfig() { + return { + appDir: "", + pragmas: {}, + paths: {}, + optimize: "uglify", + optimizeCss: "standard.keepLines.keepWhitespace", + inlineText: true, + isBuild: true, + optimizeAllPluginResources: false, + findNestedDependencies: false, + preserveLicenseComments: true, + //By default, all files/directories are copied, unless + //they match this regexp, by default just excludes .folders + dirExclusionRegExp: file.dirExclusionRegExp, + _buildPathToModuleIndex: {} + }; + } /** * Some JS may not be valid if concatenated with other JS, in particular @@ -24333,6 +26117,18 @@ define('build', function (require) { modules.forEach(function (module) { if (module.name) { module._buildPath = buildContext.nameToUrl(module.name, null); + + //If buildPath and sourcePath are the same, throw since this + //would result in modifying source. This condition can happen + //with some more tricky paths: config and appDir/baseUrl + //setting, which is a sign of incorrect config. + if (module._buildPath === module._sourcePath) { + throw new Error('Module ID \'' + module.name + + '\' has a source path that is same as output path: ' + + module._sourcePath + + '. Stopping, config is malformed.'); + } + if (!module.create) { file.copyFile(module._sourcePath, module._buildPath); } @@ -24455,7 +26251,7 @@ define('build', function (require) { })); } }).then(function () { - var moduleName; + var moduleName, outOrigSourceMap; if (modules) { //Now move the build layers to their final position. modules.forEach(function (module) { @@ -24505,10 +26301,18 @@ define('build', function (require) { //Just need to worry about one JS file. fileName = config.modules[0]._buildPath; if (fileName === 'FUNCTION') { - config.modules[0]._buildText = optimize.js(fileName, + outOrigSourceMap = config.modules[0]._buildSourceMap; + config._buildSourceMap = outOrigSourceMap; + config.modules[0]._buildText = optimize.js((config.modules[0].name || + config.modules[0].include[0] || + fileName) + '.build.js', config.modules[0]._buildText, null, config); + if (config._buildSourceMap && config._buildSourceMap !== outOrigSourceMap) { + config.modules[0]._buildSourceMap = config._buildSourceMap; + config._buildSourceMap = null; + } } else { optimize.jsFile(fileName, null, fileName, config); } @@ -24659,7 +26463,7 @@ define('build', function (require) { } if (typeof config.out === 'function') { - config.out(config.modules[0]._buildText); + config.out(config.modules[0]._buildText, config.modules[0]._buildSourceMap); } //Print out what was built into which layers. @@ -24829,7 +26633,8 @@ define('build', function (require) { } } - build.makeAbsObject(["out", "cssIn"], config, absFilePath); + build.makeAbsObject((config.out === "stdout" ? ["cssIn"] : ["out", "cssIn"]), + config, absFilePath); build.makeAbsObject(["startFile", "endFile"], config.wrap, absFilePath); }; @@ -24839,13 +26644,19 @@ define('build', function (require) { * make sure paths end in a trailing '/'. */ build.makeRelativeFilePath = function (refPath, targetPath) { - var i, dotLength, finalParts, length, + var i, dotLength, finalParts, length, targetParts, targetName, refParts = refPath.split('/'), - targetParts = targetPath.split('/'), - //Pull off file name - targetName = targetParts.pop(), + hasEndSlash = endsWithSlashRegExp.test(targetPath), dotParts = []; + targetPath = file.normalize(targetPath); + if (hasEndSlash && !endsWithSlashRegExp.test(targetPath)) { + targetPath += '/'; + } + targetParts = targetPath.split('/'); + //Pull off file name + targetName = targetParts.pop(); + //Also pop off the ref file name to make the matches against //targetParts equivalent. refParts.pop(); @@ -24883,18 +26694,43 @@ define('build', function (require) { * Mixes additional source config into target config, and merges some * nested config, like paths, correctly. */ - function mixConfig(target, source) { - var prop, value; + function mixConfig(target, source, skipArrays) { + var prop, value, isArray, targetValue; for (prop in source) { if (hasProp(source, prop)) { //If the value of the property is a plain object, then //allow a one-level-deep mixing of it. value = source[prop]; + isArray = lang.isArray(value); if (typeof value === 'object' && value && - !lang.isArray(value) && !lang.isFunction(value) && + !isArray && !lang.isFunction(value) && !lang.isRegExp(value)) { - target[prop] = lang.mixin({}, target[prop], value, true); + + // TODO: need to generalize this work, maybe also reuse + // the work done in requirejs configure, perhaps move to + // just a deep copy/merge overall. However, given the + // amount of observable change, wait for a dot release. + // This change is in relation to #645 + if (prop === 'map') { + if (!target.map) { + target.map = {}; + } + lang.deepMix(target.map, source.map); + } else { + target[prop] = lang.mixin({}, target[prop], value, true); + } + } else if (isArray) { + if (!skipArrays) { + // Some config, like packages, are arrays. For those, + // just merge the results. + targetValue = target[prop]; + if (lang.isArray(targetValue)) { + target[prop] = targetValue.concat(value); + } else { + target[prop] = value; + } + } } else { target[prop] = value; } @@ -24965,8 +26801,10 @@ define('build', function (require) { */ build.createConfig = function (cfg) { /*jslint evil: true */ - var config = {}, buildFileContents, buildFileConfig, mainConfig, - mainConfigFile, mainConfigPath, buildFile, absFilePath; + var buildFileContents, buildFileConfig, mainConfig, + mainConfigFile, mainConfigPath, buildFile, absFilePath, + config = {}, + buildBaseConfig = makeBuildBaseConfig(); //Make sure all paths are relative to current directory. absFilePath = file.absPath('.'); @@ -25013,50 +26851,58 @@ define('build', function (require) { mainConfigFile = config.mainConfigFile || (buildFileConfig && buildFileConfig.mainConfigFile); if (mainConfigFile) { - mainConfigFile = build.makeAbsPath(mainConfigFile, absFilePath); - if (!file.exists(mainConfigFile)) { - throw new Error(mainConfigFile + ' does not exist.'); + if (typeof mainConfigFile === 'string') { + mainConfigFile = [mainConfigFile]; } - try { - mainConfig = parse.findConfig(file.readFile(mainConfigFile)).config; - } catch (configError) { - throw new Error('The config in mainConfigFile ' + - mainConfigFile + - ' cannot be used because it cannot be evaluated' + - ' correctly while running in the optimizer. Try only' + - ' using a config that is also valid JSON, or do not use' + - ' mainConfigFile and instead copy the config values needed' + - ' into a build file or command line arguments given to the optimizer.\n' + - 'Source error from parsing: ' + mainConfigFile + ': ' + configError); - } - if (mainConfig) { - mainConfigPath = mainConfigFile.substring(0, mainConfigFile.lastIndexOf('/')); - //Add in some existing config, like appDir, since they can be - //used inside the mainConfigFile -- paths and baseUrl are - //relative to them. - if (config.appDir && !mainConfig.appDir) { - mainConfig.appDir = config.appDir; + mainConfigFile.forEach(function (configFile) { + configFile = build.makeAbsPath(configFile, absFilePath); + if (!file.exists(configFile)) { + throw new Error(configFile + ' does not exist.'); } + try { + mainConfig = parse.findConfig(file.readFile(configFile)).config; + } catch (configError) { + throw new Error('The config in mainConfigFile ' + + configFile + + ' cannot be used because it cannot be evaluated' + + ' correctly while running in the optimizer. Try only' + + ' using a config that is also valid JSON, or do not use' + + ' mainConfigFile and instead copy the config values needed' + + ' into a build file or command line arguments given to the optimizer.\n' + + 'Source error from parsing: ' + configFile + ': ' + configError); + } + if (mainConfig) { + mainConfigPath = configFile.substring(0, configFile.lastIndexOf('/')); + + //Add in some existing config, like appDir, since they can be + //used inside the configFile -- paths and baseUrl are + //relative to them. + if (config.appDir && !mainConfig.appDir) { + mainConfig.appDir = config.appDir; + } - //If no baseUrl, then use the directory holding the main config. - if (!mainConfig.baseUrl) { - mainConfig.baseUrl = mainConfigPath; - } + //If no baseUrl, then use the directory holding the main config. + if (!mainConfig.baseUrl) { + mainConfig.baseUrl = mainConfigPath; + } - build.makeAbsConfig(mainConfig, mainConfigPath); - mixConfig(config, mainConfig); - } + build.makeAbsConfig(mainConfig, mainConfigPath); + mixConfig(config, mainConfig); + } + }); } //Mix in build file config, but only after mainConfig has been mixed in. + //Since this is a re-application, skip array merging. if (buildFileConfig) { - mixConfig(config, buildFileConfig); + mixConfig(config, buildFileConfig, true); } //Re-apply the override config values. Command line //args should take precedence over build file values. - mixConfig(config, cfg); + //Since this is a re-application, skip array merging. + mixConfig(config, cfg, true); //Fix paths to full paths so that they can be adjusted consistently //lately to be in the output area. @@ -25072,6 +26918,9 @@ define('build', function (require) { //Set final output dir if (hasProp(config, "baseUrl")) { if (config.appDir) { + if (!config.originalBaseUrl) { + throw new Error('Please set a baseUrl in the build config'); + } config.dirBaseUrl = build.makeAbsPath(config.originalBaseUrl, config.dir); } else { config.dirBaseUrl = config.dir || config.baseUrl; @@ -25081,6 +26930,23 @@ define('build', function (require) { config.dirBaseUrl = endsWithSlash(config.dirBaseUrl); } + + //If out=stdout, write output to STDOUT instead of a file. + if (config.out && config.out === 'stdout') { + config.out = function (content) { + var e = env.get(); + if (e === 'rhino') { + var out = new java.io.PrintStream(java.lang.System.out, true, 'UTF-8'); + out.println(content); + } else if (e === 'node') { + process.stdout.setEncoding('utf8'); + process.stdout.write(content); + } else { + console.log(content); + } + }; + } + //Check for errors in config if (config.main) { throw new Error('"main" passed as an option, but the ' + @@ -25118,7 +26984,7 @@ define('build', function (require) { } if (config.out && config.dir) { throw new Error('The "out" and "dir" options are incompatible.' + - ' Use "out" if you are targeting a single file for' + + ' Use "out" if you are targeting a single file' + ' for optimization, and "dir" if you want the appDir' + ' or baseUrl directories optimized.'); } @@ -25127,15 +26993,20 @@ define('build', function (require) { // Make sure the output dir is not set to a parent of the // source dir or the same dir, as it will result in source // code deletion. - if (config.dir === config.baseUrl || + if (!config.allowSourceOverwrites && (config.dir === config.baseUrl || config.dir === config.appDir || (config.baseUrl && build.makeRelativeFilePath(config.dir, config.baseUrl).indexOf('..') !== 0) || (config.appDir && - build.makeRelativeFilePath(config.dir, config.appDir).indexOf('..') !== 0)) { + build.makeRelativeFilePath(config.dir, config.appDir).indexOf('..') !== 0))) { throw new Error('"dir" is set to a parent or same directory as' + ' "appDir" or "baseUrl". This can result in' + - ' the deletion of source code. Stopping.'); + ' the deletion of source code. Stopping. If' + + ' you want to allow possible overwriting of' + + ' source code, set "allowSourceOverwrites"' + + ' to true in the build config, but do so at' + + ' your own risk. In that case, you may want' + + ' to also set "keepBuildDir" to true.'); } } @@ -25213,10 +27084,11 @@ define('build', function (require) { config.cssPrefix = ''; } - //Cycle through modules and combine any local stubModules with - //global values. + //Cycle through modules and normalize if (config.modules && config.modules.length) { config.modules.forEach(function (mod) { + + //Combine any local stubModules with global values. if (config.stubModules) { mod.stubModules = config.stubModules.concat(mod.stubModules || []); } @@ -25230,6 +27102,12 @@ define('build', function (require) { }); } + // Legacy command support, which allowed a single string ID + // for include. + if (typeof mod.include === 'string') { + mod.include = [mod.include]; + } + //Allow wrap config in overrides, but normalize it. if (mod.override) { normalizeWrapConfig(mod.override, absFilePath); @@ -25270,7 +27148,19 @@ define('build', function (require) { file.exclusionRegExp = config.dirExclusionRegExp; } + //Track the deps, but in a different key, so that they are not loaded + //as part of config seeding before all config is in play (#648). Was + //going to merge this in with "include", but include is added after + //the "name" target. To preserve what r.js has done previously, make + //sure "deps" comes before the "name". + if (config.deps) { + config._depsInclude = config.deps; + } + + //Remove things that may cause problems in the build. + //deps already merged above + delete config.deps; delete config.jQuery; delete config.enforceDefine; delete config.urlArgs; @@ -25347,7 +27237,8 @@ define('build', function (require) { logger.trace("\nTracing dependencies for: " + (module.name || (typeof module.out === 'function' ? 'FUNCTION' : module.out))); - include = module.name && !module.create ? [module.name] : []; + include = config._depsInclude || []; + include = include.concat(module.name && !module.create ? [module.name] : []); if (module.include) { include = include.concat(module.include); } @@ -25424,7 +27315,7 @@ define('build', function (require) { build.checkForErrors = function (context) { //Check to see if it all loaded. If not, then throw, and give //a message on what is left. - var id, prop, mod, idParts, pluginId, + var id, prop, mod, idParts, pluginId, pluginResources, errMessage = '', failedPluginMap = {}, failedPluginIds = [], @@ -25437,6 +27328,11 @@ define('build', function (require) { registry = context.registry; function populateErrUrlMap(id, errUrl, skipNew) { + // Loader plugins do not have an errUrl, so skip them. + if (!errUrl) { + return; + } + if (!skipNew) { errIds.push(id); } @@ -25460,17 +27356,24 @@ define('build', function (require) { if (hasProp(registry, id) && id.indexOf('_@r') !== 0) { hasUndefined = true; mod = getOwn(registry, id); + idParts = id.split('!'); + pluginId = idParts[0]; if (id.indexOf('_unnormalized') === -1 && mod && mod.enabled) { populateErrUrlMap(id, mod.map.url); } //Look for plugins that did not call load() - idParts = id.split('!'); - pluginId = idParts[0]; - if (idParts.length > 1 && falseProp(failedPluginMap, pluginId)) { - failedPluginIds.push(pluginId); - failedPluginMap[pluginId] = true; + + if (idParts.length > 1) { + if (falseProp(failedPluginMap, pluginId)) { + failedPluginIds.push(pluginId); + } + pluginResources = failedPluginMap[pluginId]; + if (!pluginResources) { + pluginResources = failedPluginMap[pluginId] = []; + } + pluginResources.push(id + (mod.error ? ': ' + mod.error : '')); } } } @@ -25490,8 +27393,11 @@ define('build', function (require) { errMessage += 'Loader plugin' + (failedPluginIds.length === 1 ? '' : 's') + ' did not call ' + - 'the load callback in the build: ' + - failedPluginIds.join(', ') + '\n'; + 'the load callback in the build:\n' + + failedPluginIds.map(function (pluginId) { + var pluginResources = failedPluginMap[pluginId]; + return pluginId + ':\n ' + pluginResources.join('\n '); + }).join('\n') + '\n'; } errMessage += 'Module loading did not complete for: ' + errIds.join(', '); @@ -25549,8 +27455,8 @@ define('build', function (require) { buildFileContents = ''; return prim().start(function () { - var reqIndex, currContents, - moduleName, shim, packageConfig, nonPackageName, + var reqIndex, currContents, fileForSourceMap, + moduleName, shim, packageMain, packageName, parts, builder, writeApi, namespace, namespaceWithDot, stubModulesByName, context = layer.context, @@ -25589,8 +27495,11 @@ define('build', function (require) { if (config.generateSourceMaps) { sourceMapBase = config.dir || config.baseUrl; + fileForSourceMap = module._buildPath === 'FUNCTION' ? + (module.name || module.include[0] || 'FUNCTION') + '.build.js' : + module._buildPath.replace(sourceMapBase, ''); sourceMapGenerator = new SourceMapGenerator.SourceMapGenerator({ - file: module._buildPath.replace(sourceMapBase, '') + file: fileForSourceMap }); } @@ -25602,13 +27511,15 @@ define('build', function (require) { singleContents = ''; moduleName = layer.buildFileToModule[path]; + packageName = moduleName.split('/').shift(); + //If the moduleName is for a package main, then update it to the //real main value. - packageConfig = layer.context.config.pkgs && - getOwn(layer.context.config.pkgs, moduleName); - if (packageConfig) { - nonPackageName = moduleName; - moduleName += '/' + packageConfig.main; + packageMain = layer.context.config.pkgs && + getOwn(layer.context.config.pkgs, packageName); + if (packageMain !== moduleName) { + // Not a match, clear packageMain + packageMain = undefined; } return prim().start(function () { @@ -25671,8 +27582,8 @@ define('build', function (require) { currContents = config.onBuildRead(moduleName, path, currContents); } - if (packageConfig) { - hasPackageName = (nonPackageName === parse.getNamedDefine(currContents)); + if (packageMain) { + hasPackageName = (packageName === parse.getNamedDefine(currContents)); } if (namespace) { @@ -25683,10 +27594,10 @@ define('build', function (require) { useSourceUrl: config.useSourceUrl }); - if (packageConfig && !hasPackageName) { + if (packageMain && !hasPackageName) { currContents = addSemiColon(currContents, config) + '\n'; currContents += namespaceWithDot + "define('" + - packageConfig.name + "', ['" + moduleName + + packageName + "', ['" + moduleName + "'], function (main) { return main; });\n"; } @@ -25696,7 +27607,7 @@ define('build', function (require) { //Semicolon is for files that are not well formed when //concatenated with other content. - singleContents += "\n" + addSemiColon(currContents, config); + singleContents += addSemiColon(currContents, config); }); } }).then(function () { @@ -25712,18 +27623,41 @@ define('build', function (require) { //after the module is processed. //If we have a name, but no defined module, then add in the placeholder. if (moduleName && falseProp(layer.modulesWithNames, moduleName) && !config.skipModuleInsertion) { - shim = config.shim && (getOwn(config.shim, moduleName) || (packageConfig && getOwn(config.shim, nonPackageName))); + shim = config.shim && (getOwn(config.shim, moduleName) || (packageMain && getOwn(config.shim, moduleName) || getOwn(config.shim, packageName))); if (shim) { - singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", ' + - (shim.deps && shim.deps.length ? - build.makeJsArrayString(shim.deps) + ', ' : '') + - (shim.exportsFn ? shim.exportsFn() : 'function(){}') + - ');\n'; + if (config.wrapShim) { + singleContents = '(function(root) {\n' + + namespaceWithDot + 'define("' + moduleName + '", ' + + (shim.deps && shim.deps.length ? + build.makeJsArrayString(shim.deps) + ', ' : '[], ') + + 'function() {\n' + + ' return (function() {\n' + + singleContents + + // Start with a \n in case last line is a comment + // in the singleContents, like a sourceURL comment. + '\n' + (shim.exportsFn ? shim.exportsFn() : '') + + '\n' + + ' }).apply(root, arguments);\n' + + '});\n' + + '}(this));\n'; + } else { + singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", ' + + (shim.deps && shim.deps.length ? + build.makeJsArrayString(shim.deps) + ', ' : '') + + (shim.exportsFn ? shim.exportsFn() : 'function(){}') + + ');\n'; + } } else { singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", function(){});\n'; } } + //Add line break at end of file, instead of at beginning, + //so source map line numbers stay correct, but still allow + //for some space separation between files in case ASI issues + //for concatenation would cause an error otherwise. + singleContents += '\n'; + //Add to the source map if (sourceMapGenerator) { refPath = config.out ? config.baseUrl : module._buildPath; @@ -25983,7 +27917,7 @@ define('build', function (require) { loadLib(); /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * @license Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -26016,7 +27950,7 @@ function (args, quit, logger, build) { } else if (commandOption === 'v') { console.log('r.js: ' + version + ', RequireJS: ' + this.requirejsVars.require.version + - ', UglifyJS2: 2.4.0, UglifyJS: 1.3.4'); + ', UglifyJS2: 2.4.13, UglifyJS: 1.3.4'); } else if (commandOption === 'convert') { loadLib();