uglifier 1.2.3 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of uglifier might be problematic. Click here for more details.

Files changed (6) hide show
  1. data/README.md +77 -0
  2. data/VERSION +1 -1
  3. data/lib/uglify.js +2620 -13
  4. data/uglifier.gemspec +4 -4
  5. metadata +17 -17
  6. data/README.rdoc +0 -67
@@ -0,0 +1,77 @@
1
+ # Uglifier [![Build Status](https://secure.travis-ci.org/lautis/uglifier.png?branch=master)](http://travis-ci.org/lautis/uglifier) [![Dependency Status](https://gemnasium.com/lautis/uglifier.png?travis)](https://gemnasium.com/lautis/uglifier)
2
+
3
+ Ruby wrapper for [UglifyJS](https://github.com/mishoo/UglifyJS) JavaScript compressor.
4
+
5
+ ## Installation
6
+
7
+ Uglifier is available as ruby gem.
8
+
9
+ $ gem install uglifier
10
+
11
+ Ensure that your environment has a JavaScript interpreter supported by ExecJS[https://github.com/sstephenson/execjs]. Usually, installing therubyracer gem is the best alternative.
12
+
13
+ ## Usage
14
+
15
+ require 'uglifier'
16
+
17
+ Uglifier.new.compile(File.read("source.js"))
18
+ # => js file minified
19
+
20
+ # Or alternatively
21
+ Uglifier.compile(File.read("source.js"))
22
+
23
+ When initializing UglifyJS, you can tune the behavior of UglifyJS by passing options. For example, if you want top-level variable names to be mangled:
24
+
25
+ Uglifier.new(:toplevel => true).compile(source)
26
+
27
+ # Or
28
+ Uglifier.compile(source, :toplevel => true)
29
+
30
+ Available options and their defaults are
31
+
32
+ {
33
+ :mangle => true, # Mangle variable and function names, use :variables to skip function mangling
34
+ :toplevel => false, # Mangle top-level variable names
35
+ :except => [], # Variable names to be excluded from mangling
36
+ :max_line_length => 32 * 1024, # Maximum line length
37
+ :squeeze => true, # Squeeze code resulting in smaller, but less-readable code
38
+ :seqs => true, # Reduce consecutive statements in blocks into single statement
39
+ :dead_code => true, # Remove dead code (e.g. after return)
40
+ :lift_vars => false, # Lift all var declarations at the start of the scope
41
+ :unsafe => false, # Optimizations known to be unsafe in some situations
42
+ :copyright => true, # Show copyright message
43
+ :ascii_only => false, # Encode non-ASCII characters as Unicode code points
44
+ :inline_script => false, # Escape </script
45
+ :quote_keys => false, # Quote keys in object literals
46
+ :beautify => false, # Ouput indented code
47
+ :beautify_options => {
48
+ :indent_level => 4,
49
+ :indent_start => 0,
50
+ :space_colon => false
51
+ }
52
+ }
53
+
54
+ ## Development
55
+
56
+ Uglifier uses [stitch](https://github.com/sstephenson/stitch) to compile UglifyJs for non-node JS runtimes. If you need to update or patch UglifyJS, you can stitch UglifyJS using
57
+
58
+ node build.js
59
+
60
+ ## Submitting an issue
61
+
62
+ Uglifier uses the [GitHub issue tracker](https://github.com/lautis/uglifier/issues) to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. You can indicate support for an existing issuse by voting it up. When submitting a bug report, please include a Gist that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, **MultiJSON engine** and **ExecJS runtime**. Ideally, a bug report should include a pull request with failing specs.
63
+
64
+ ## Contributing to uglifier
65
+
66
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
67
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
68
+ * Fork the project
69
+ * Start a feature/bugfix branch
70
+ * Commit and push until you are happy with your contribution
71
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
72
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
73
+
74
+
75
+ ## Copyright
76
+
77
+ © Ville Lautanala, [Flowdock](https://flowdock.com/). Released under MIT license, see [LICENSE.txt](https://github.com/lautis/uglifier/blob/master/LICENSE.txt) for more details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.3
1
+ 1.2.4
@@ -48,7 +48,2606 @@
48
48
  };
49
49
  }
50
50
  return this.require.define;
51
- }).call(this)({"parse-js": function(exports, require, module) {/***********************************************************************
51
+ }).call(this)({"consolidator": function(exports, require, module) {/**
52
+ * @preserve Copyright 2012 Robert Gust-Bardon <http://robert.gust-bardon.org/>.
53
+ * All rights reserved.
54
+ *
55
+ * Redistribution and use in source and binary forms, with or without
56
+ * modification, are permitted provided that the following conditions
57
+ * are met:
58
+ *
59
+ * * Redistributions of source code must retain the above
60
+ * copyright notice, this list of conditions and the following
61
+ * disclaimer.
62
+ *
63
+ * * Redistributions in binary form must reproduce the above
64
+ * copyright notice, this list of conditions and the following
65
+ * disclaimer in the documentation and/or other materials
66
+ * provided with the distribution.
67
+ *
68
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
69
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
70
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
71
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
72
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
73
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
74
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
75
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
76
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
77
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
78
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
79
+ * SUCH DAMAGE.
80
+ */
81
+
82
+ /**
83
+ * @fileoverview Enhances <a href="https://github.com/mishoo/UglifyJS/"
84
+ * >UglifyJS</a> with consolidation of null, Boolean, and String values.
85
+ * <p>Also known as aliasing, this feature has been deprecated in <a href=
86
+ * "http://closure-compiler.googlecode.com/">the Closure Compiler</a> since its
87
+ * initial release, where it is unavailable from the <abbr title=
88
+ * "command line interface">CLI</a>. The Closure Compiler allows one to log and
89
+ * influence this process. In contrast, this implementation does not introduce
90
+ * any variable declarations in global code and derives String values from
91
+ * identifier names used as property accessors.</p>
92
+ * <p>Consolidating literals may worsen the data compression ratio when an <a
93
+ * href="http://tools.ietf.org/html/rfc2616#section-3.5">encoding
94
+ * transformation</a> is applied. For instance, <a href=
95
+ * "http://code.jquery.com/jquery-1.7.1.js">jQuery 1.7.1</a> takes 248235 bytes.
96
+ * Building it with <a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">
97
+ * UglifyJS v1.2.5</a> results in 93647 bytes (37.73% of the original) which are
98
+ * then compressed to 33154 bytes (13.36% of the original) using <a href=
99
+ * "http://linux.die.net/man/1/gzip">gzip(1)</a>. Building it with the same
100
+ * version of UglifyJS 1.2.5 patched with the implementation of consolidation
101
+ * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison
102
+ * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes
103
+ * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned
104
+ * 33154 bytes).</p>
105
+ * <p>Written in <a href="http://es5.github.com/#x4.2.2">the strict variant</a>
106
+ * of <a href="http://es5.github.com/">ECMA-262 5.1 Edition</a>. Encoded in <a
107
+ * href="http://tools.ietf.org/html/rfc3629">UTF-8</a>. Follows <a href=
108
+ * "http://google-styleguide.googlecode.com/svn-history/r76/trunk/javascriptguide.xml"
109
+ * >Revision 2.28 of the Google JavaScript Style Guide</a> (except for the
110
+ * discouraged use of the {@code function} tag and the {@code namespace} tag).
111
+ * 100% typed for the <a href=
112
+ * "http://closure-compiler.googlecode.com/files/compiler-20120123.tar.gz"
113
+ * >Closure Compiler Version 1741</a>.</p>
114
+ * <p>Should you find this software useful, please consider <a href=
115
+ * "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JZLW72X8FD4WG"
116
+ * >a donation</a>.</p>
117
+ * @author follow.me@RGustBardon (Robert Gust-Bardon)
118
+ * @supported Tested with:
119
+ * <ul>
120
+ * <li><a href="http://nodejs.org/dist/v0.6.10/">Node v0.6.10</a>,</li>
121
+ * <li><a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">UglifyJS
122
+ * v1.2.5</a>.</li>
123
+ * </ul>
124
+ */
125
+
126
+ /*global console:false, exports:true, module:false, require:false */
127
+ /*jshint sub:true */
128
+ /**
129
+ * Consolidates null, Boolean, and String values found inside an <abbr title=
130
+ * "abstract syntax tree">AST</abbr>.
131
+ * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object
132
+ * representing an <abbr title="abstract syntax tree">AST</abbr>.
133
+ * @return {!TSyntacticCodeUnit} An array-like object representing an <abbr
134
+ * title="abstract syntax tree">AST</abbr> with its null, Boolean, and
135
+ * String values consolidated.
136
+ */
137
+ // TODO(user) Consolidation of mathematical values found in numeric literals.
138
+ // TODO(user) Unconsolidation.
139
+ // TODO(user) Consolidation of ECMA-262 6th Edition programs.
140
+ // TODO(user) Rewrite in ECMA-262 6th Edition.
141
+ exports['ast_consolidate'] = function(oAbstractSyntaxTree) {
142
+ 'use strict';
143
+ /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
144
+ latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
145
+ onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
146
+ sub:false, trailing:true */
147
+
148
+ var _,
149
+ /**
150
+ * A record consisting of data about one or more source elements.
151
+ * @constructor
152
+ * @nosideeffects
153
+ */
154
+ TSourceElementsData = function() {
155
+ /**
156
+ * The category of the elements.
157
+ * @type {number}
158
+ * @see ESourceElementCategories
159
+ */
160
+ this.nCategory = ESourceElementCategories.N_OTHER;
161
+ /**
162
+ * The number of occurrences (within the elements) of each primitive
163
+ * value that could be consolidated.
164
+ * @type {!Array.<!Object.<string, number>>}
165
+ */
166
+ this.aCount = [];
167
+ this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {};
168
+ this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {};
169
+ this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] =
170
+ {};
171
+ /**
172
+ * Identifier names found within the elements.
173
+ * @type {!Array.<string>}
174
+ */
175
+ this.aIdentifiers = [];
176
+ /**
177
+ * Prefixed representation Strings of each primitive value that could be
178
+ * consolidated within the elements.
179
+ * @type {!Array.<string>}
180
+ */
181
+ this.aPrimitiveValues = [];
182
+ },
183
+ /**
184
+ * A record consisting of data about a primitive value that could be
185
+ * consolidated.
186
+ * @constructor
187
+ * @nosideeffects
188
+ */
189
+ TPrimitiveValue = function() {
190
+ /**
191
+ * The difference in the number of terminal symbols between the original
192
+ * source text and the one with the primitive value consolidated. If the
193
+ * difference is positive, the primitive value is considered worthwhile.
194
+ * @type {number}
195
+ */
196
+ this.nSaving = 0;
197
+ /**
198
+ * An identifier name of the variable that will be declared and assigned
199
+ * the primitive value if the primitive value is consolidated.
200
+ * @type {string}
201
+ */
202
+ this.sName = '';
203
+ },
204
+ /**
205
+ * A record consisting of data on what to consolidate within the range of
206
+ * source elements that is currently being considered.
207
+ * @constructor
208
+ * @nosideeffects
209
+ */
210
+ TSolution = function() {
211
+ /**
212
+ * An object whose keys are prefixed representation Strings of each
213
+ * primitive value that could be consolidated within the elements and
214
+ * whose values are corresponding data about those primitive values.
215
+ * @type {!Object.<string, {nSaving: number, sName: string}>}
216
+ * @see TPrimitiveValue
217
+ */
218
+ this.oPrimitiveValues = {};
219
+ /**
220
+ * The difference in the number of terminal symbols between the original
221
+ * source text and the one with all the worthwhile primitive values
222
+ * consolidated.
223
+ * @type {number}
224
+ * @see TPrimitiveValue#nSaving
225
+ */
226
+ this.nSavings = 0;
227
+ },
228
+ /**
229
+ * The processor of <abbr title="abstract syntax tree">AST</abbr>s found
230
+ * in UglifyJS.
231
+ * @namespace
232
+ * @type {!TProcessor}
233
+ */
234
+ oProcessor = (/** @type {!TProcessor} */ require('./process')),
235
+ /**
236
+ * A record consisting of a number of constants that represent the
237
+ * difference in the number of terminal symbols between a source text with
238
+ * a modified syntactic code unit and the original one.
239
+ * @namespace
240
+ * @type {!Object.<string, number>}
241
+ */
242
+ oWeights = {
243
+ /**
244
+ * The difference in the number of punctuators required by the bracket
245
+ * notation and the dot notation.
246
+ * <p><code>'[]'.length - '.'.length</code></p>
247
+ * @const
248
+ * @type {number}
249
+ */
250
+ N_PROPERTY_ACCESSOR: 1,
251
+ /**
252
+ * The number of punctuators required by a variable declaration with an
253
+ * initialiser.
254
+ * <p><code>':'.length + ';'.length</code></p>
255
+ * @const
256
+ * @type {number}
257
+ */
258
+ N_VARIABLE_DECLARATION: 2,
259
+ /**
260
+ * The number of terminal symbols required to introduce a variable
261
+ * statement (excluding its variable declaration list).
262
+ * <p><code>'var '.length</code></p>
263
+ * @const
264
+ * @type {number}
265
+ */
266
+ N_VARIABLE_STATEMENT_AFFIXATION: 4,
267
+ /**
268
+ * The number of terminal symbols needed to enclose source elements
269
+ * within a function call with no argument values to a function with an
270
+ * empty parameter list.
271
+ * <p><code>'(function(){}());'.length</code></p>
272
+ * @const
273
+ * @type {number}
274
+ */
275
+ N_CLOSURE: 17
276
+ },
277
+ /**
278
+ * Categories of primary expressions from which primitive values that
279
+ * could be consolidated are derivable.
280
+ * @namespace
281
+ * @enum {number}
282
+ */
283
+ EPrimaryExpressionCategories = {
284
+ /**
285
+ * Identifier names used as property accessors.
286
+ * @type {number}
287
+ */
288
+ N_IDENTIFIER_NAMES: 0,
289
+ /**
290
+ * String literals.
291
+ * @type {number}
292
+ */
293
+ N_STRING_LITERALS: 1,
294
+ /**
295
+ * Null and Boolean literals.
296
+ * @type {number}
297
+ */
298
+ N_NULL_AND_BOOLEAN_LITERALS: 2
299
+ },
300
+ /**
301
+ * Prefixes of primitive values that could be consolidated.
302
+ * The String values of the prefixes must have same number of characters.
303
+ * The prefixes must not be used in any properties defined in any version
304
+ * of <a href=
305
+ * "http://www.ecma-international.org/publications/standards/Ecma-262.htm"
306
+ * >ECMA-262</a>.
307
+ * @namespace
308
+ * @enum {string}
309
+ */
310
+ EValuePrefixes = {
311
+ /**
312
+ * Identifies String values.
313
+ * @type {string}
314
+ */
315
+ S_STRING: '#S',
316
+ /**
317
+ * Identifies null and Boolean values.
318
+ * @type {string}
319
+ */
320
+ S_SYMBOLIC: '#O'
321
+ },
322
+ /**
323
+ * Categories of source elements in terms of their appropriateness of
324
+ * having their primitive values consolidated.
325
+ * @namespace
326
+ * @enum {number}
327
+ */
328
+ ESourceElementCategories = {
329
+ /**
330
+ * Identifies a source element that includes the <a href=
331
+ * "http://es5.github.com/#x12.10">{@code with}</a> statement.
332
+ * @type {number}
333
+ */
334
+ N_WITH: 0,
335
+ /**
336
+ * Identifies a source element that includes the <a href=
337
+ * "http://es5.github.com/#x15.1.2.1">{@code eval}</a> identifier name.
338
+ * @type {number}
339
+ */
340
+ N_EVAL: 1,
341
+ /**
342
+ * Identifies a source element that must be excluded from the process
343
+ * unless its whole scope is examined.
344
+ * @type {number}
345
+ */
346
+ N_EXCLUDABLE: 2,
347
+ /**
348
+ * Identifies source elements not posing any problems.
349
+ * @type {number}
350
+ */
351
+ N_OTHER: 3
352
+ },
353
+ /**
354
+ * The list of literals (other than the String ones) whose primitive
355
+ * values can be consolidated.
356
+ * @const
357
+ * @type {!Array.<string>}
358
+ */
359
+ A_OTHER_SUBSTITUTABLE_LITERALS = [
360
+ 'null', // The null literal.
361
+ 'false', // The Boolean literal {@code false}.
362
+ 'true' // The Boolean literal {@code true}.
363
+ ];
364
+
365
+ (/**
366
+ * Consolidates all worthwhile primitive values in a syntactic code unit.
367
+ * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object
368
+ * representing the branch of the abstract syntax tree representing the
369
+ * syntactic code unit along with its scope.
370
+ * @see TPrimitiveValue#nSaving
371
+ */
372
+ function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) {
373
+ var _,
374
+ /**
375
+ * Indicates whether the syntactic code unit represents global code.
376
+ * @type {boolean}
377
+ */
378
+ bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0],
379
+ /**
380
+ * Indicates whether the whole scope is being examined.
381
+ * @type {boolean}
382
+ */
383
+ bIsWhollyExaminable = !bIsGlobal,
384
+ /**
385
+ * An array-like object representing source elements that constitute a
386
+ * syntactic code unit.
387
+ * @type {!TSyntacticCodeUnit}
388
+ */
389
+ oSourceElements,
390
+ /**
391
+ * A record consisting of data about the source element that is
392
+ * currently being examined.
393
+ * @type {!TSourceElementsData}
394
+ */
395
+ oSourceElementData,
396
+ /**
397
+ * The scope of the syntactic code unit.
398
+ * @type {!TScope}
399
+ */
400
+ oScope,
401
+ /**
402
+ * An instance of an object that allows the traversal of an <abbr
403
+ * title="abstract syntax tree">AST</abbr>.
404
+ * @type {!TWalker}
405
+ */
406
+ oWalker,
407
+ /**
408
+ * An object encompassing collections of functions used during the
409
+ * traversal of an <abbr title="abstract syntax tree">AST</abbr>.
410
+ * @namespace
411
+ * @type {!Object.<string, !Object.<string, function(...[*])>>}
412
+ */
413
+ oWalkers = {
414
+ /**
415
+ * A collection of functions used during the surveyance of source
416
+ * elements.
417
+ * @namespace
418
+ * @type {!Object.<string, function(...[*])>}
419
+ */
420
+ oSurveySourceElement: {
421
+ /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
422
+ /**
423
+ * Classifies the source element as excludable if it does not
424
+ * contain a {@code with} statement or the {@code eval} identifier
425
+ * name. Adds the identifier of the function and its formal
426
+ * parameters to the list of identifier names found.
427
+ * @param {string} sIdentifier The identifier of the function.
428
+ * @param {!Array.<string>} aFormalParameterList Formal parameters.
429
+ * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
430
+ */
431
+ 'defun': function(
432
+ sIdentifier,
433
+ aFormalParameterList,
434
+ oFunctionBody) {
435
+ fClassifyAsExcludable();
436
+ fAddIdentifier(sIdentifier);
437
+ aFormalParameterList.forEach(fAddIdentifier);
438
+ },
439
+ /**
440
+ * Increments the count of the number of occurrences of the String
441
+ * value that is equivalent to the sequence of terminal symbols
442
+ * that constitute the encountered identifier name.
443
+ * @param {!TSyntacticCodeUnit} oExpression The nonterminal
444
+ * MemberExpression.
445
+ * @param {string} sIdentifierName The identifier name used as the
446
+ * property accessor.
447
+ * @return {!Array} The encountered branch of an <abbr title=
448
+ * "abstract syntax tree">AST</abbr> with its nonterminal
449
+ * MemberExpression traversed.
450
+ */
451
+ 'dot': function(oExpression, sIdentifierName) {
452
+ fCountPrimaryExpression(
453
+ EPrimaryExpressionCategories.N_IDENTIFIER_NAMES,
454
+ EValuePrefixes.S_STRING + sIdentifierName);
455
+ return ['dot', oWalker.walk(oExpression), sIdentifierName];
456
+ },
457
+ /**
458
+ * Adds the optional identifier of the function and its formal
459
+ * parameters to the list of identifier names found.
460
+ * @param {?string} sIdentifier The optional identifier of the
461
+ * function.
462
+ * @param {!Array.<string>} aFormalParameterList Formal parameters.
463
+ * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
464
+ */
465
+ 'function': function(
466
+ sIdentifier,
467
+ aFormalParameterList,
468
+ oFunctionBody) {
469
+ if ('string' === typeof sIdentifier) {
470
+ fAddIdentifier(sIdentifier);
471
+ }
472
+ aFormalParameterList.forEach(fAddIdentifier);
473
+ },
474
+ /**
475
+ * Either increments the count of the number of occurrences of the
476
+ * encountered null or Boolean value or classifies a source element
477
+ * as containing the {@code eval} identifier name.
478
+ * @param {string} sIdentifier The identifier encountered.
479
+ */
480
+ 'name': function(sIdentifier) {
481
+ if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) {
482
+ fCountPrimaryExpression(
483
+ EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS,
484
+ EValuePrefixes.S_SYMBOLIC + sIdentifier);
485
+ } else {
486
+ if ('eval' === sIdentifier) {
487
+ oSourceElementData.nCategory =
488
+ ESourceElementCategories.N_EVAL;
489
+ }
490
+ fAddIdentifier(sIdentifier);
491
+ }
492
+ },
493
+ /**
494
+ * Classifies the source element as excludable if it does not
495
+ * contain a {@code with} statement or the {@code eval} identifier
496
+ * name.
497
+ * @param {TSyntacticCodeUnit} oExpression The expression whose
498
+ * value is to be returned.
499
+ */
500
+ 'return': function(oExpression) {
501
+ fClassifyAsExcludable();
502
+ },
503
+ /**
504
+ * Increments the count of the number of occurrences of the
505
+ * encountered String value.
506
+ * @param {string} sStringValue The String value of the string
507
+ * literal encountered.
508
+ */
509
+ 'string': function(sStringValue) {
510
+ if (sStringValue.length > 0) {
511
+ fCountPrimaryExpression(
512
+ EPrimaryExpressionCategories.N_STRING_LITERALS,
513
+ EValuePrefixes.S_STRING + sStringValue);
514
+ }
515
+ },
516
+ /**
517
+ * Adds the identifier reserved for an exception to the list of
518
+ * identifier names found.
519
+ * @param {!TSyntacticCodeUnit} oTry A block of code in which an
520
+ * exception can occur.
521
+ * @param {Array} aCatch The identifier reserved for an exception
522
+ * and a block of code to handle the exception.
523
+ * @param {TSyntacticCodeUnit} oFinally An optional block of code
524
+ * to be evaluated regardless of whether an exception occurs.
525
+ */
526
+ 'try': function(oTry, aCatch, oFinally) {
527
+ if (Array.isArray(aCatch)) {
528
+ fAddIdentifier(aCatch[0]);
529
+ }
530
+ },
531
+ /**
532
+ * Classifies the source element as excludable if it does not
533
+ * contain a {@code with} statement or the {@code eval} identifier
534
+ * name. Adds the identifier of each declared variable to the list
535
+ * of identifier names found.
536
+ * @param {!Array.<!Array>} aVariableDeclarationList Variable
537
+ * declarations.
538
+ */
539
+ 'var': function(aVariableDeclarationList) {
540
+ fClassifyAsExcludable();
541
+ aVariableDeclarationList.forEach(fAddVariable);
542
+ },
543
+ /**
544
+ * Classifies a source element as containing the {@code with}
545
+ * statement.
546
+ * @param {!TSyntacticCodeUnit} oExpression An expression whose
547
+ * value is to be converted to a value of type Object and
548
+ * become the binding object of a new object environment
549
+ * record of a new lexical environment in which the statement
550
+ * is to be executed.
551
+ * @param {!TSyntacticCodeUnit} oStatement The statement to be
552
+ * executed in the augmented lexical environment.
553
+ * @return {!Array} An empty array to stop the traversal.
554
+ */
555
+ 'with': function(oExpression, oStatement) {
556
+ oSourceElementData.nCategory = ESourceElementCategories.N_WITH;
557
+ return [];
558
+ }
559
+ /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
560
+ },
561
+ /**
562
+ * A collection of functions used while looking for nested functions.
563
+ * @namespace
564
+ * @type {!Object.<string, function(...[*])>}
565
+ */
566
+ oExamineFunctions: {
567
+ /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
568
+ /**
569
+ * Orders an examination of a nested function declaration.
570
+ * @this {!TSyntacticCodeUnit} An array-like object representing
571
+ * the branch of an <abbr title="abstract syntax tree"
572
+ * >AST</abbr> representing the syntactic code unit along with
573
+ * its scope.
574
+ * @return {!Array} An empty array to stop the traversal.
575
+ */
576
+ 'defun': function() {
577
+ fExamineSyntacticCodeUnit(this);
578
+ return [];
579
+ },
580
+ /**
581
+ * Orders an examination of a nested function expression.
582
+ * @this {!TSyntacticCodeUnit} An array-like object representing
583
+ * the branch of an <abbr title="abstract syntax tree"
584
+ * >AST</abbr> representing the syntactic code unit along with
585
+ * its scope.
586
+ * @return {!Array} An empty array to stop the traversal.
587
+ */
588
+ 'function': function() {
589
+ fExamineSyntacticCodeUnit(this);
590
+ return [];
591
+ }
592
+ /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
593
+ }
594
+ },
595
+ /**
596
+ * Records containing data about source elements.
597
+ * @type {Array.<TSourceElementsData>}
598
+ */
599
+ aSourceElementsData = [],
600
+ /**
601
+ * The index (in the source text order) of the source element
602
+ * immediately following a <a href="http://es5.github.com/#x14.1"
603
+ * >Directive Prologue</a>.
604
+ * @type {number}
605
+ */
606
+ nAfterDirectivePrologue = 0,
607
+ /**
608
+ * The index (in the source text order) of the source element that is
609
+ * currently being considered.
610
+ * @type {number}
611
+ */
612
+ nPosition,
613
+ /**
614
+ * The index (in the source text order) of the source element that is
615
+ * the last element of the range of source elements that is currently
616
+ * being considered.
617
+ * @type {(undefined|number)}
618
+ */
619
+ nTo,
620
+ /**
621
+ * Initiates the traversal of a source element.
622
+ * @param {!TWalker} oWalker An instance of an object that allows the
623
+ * traversal of an abstract syntax tree.
624
+ * @param {!TSyntacticCodeUnit} oSourceElement A source element from
625
+ * which the traversal should commence.
626
+ * @return {function(): !TSyntacticCodeUnit} A function that is able to
627
+ * initiate the traversal from a given source element.
628
+ */
629
+ cContext = function(oWalker, oSourceElement) {
630
+ /**
631
+ * @return {!TSyntacticCodeUnit} A function that is able to
632
+ * initiate the traversal from a given source element.
633
+ */
634
+ var fLambda = function() {
635
+ return oWalker.walk(oSourceElement);
636
+ };
637
+
638
+ return fLambda;
639
+ },
640
+ /**
641
+ * Classifies the source element as excludable if it does not
642
+ * contain a {@code with} statement or the {@code eval} identifier
643
+ * name.
644
+ */
645
+ fClassifyAsExcludable = function() {
646
+ if (oSourceElementData.nCategory ===
647
+ ESourceElementCategories.N_OTHER) {
648
+ oSourceElementData.nCategory =
649
+ ESourceElementCategories.N_EXCLUDABLE;
650
+ }
651
+ },
652
+ /**
653
+ * Adds an identifier to the list of identifier names found.
654
+ * @param {string} sIdentifier The identifier to be added.
655
+ */
656
+ fAddIdentifier = function(sIdentifier) {
657
+ if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) {
658
+ oSourceElementData.aIdentifiers.push(sIdentifier);
659
+ }
660
+ },
661
+ /**
662
+ * Adds the identifier of a variable to the list of identifier names
663
+ * found.
664
+ * @param {!Array} aVariableDeclaration A variable declaration.
665
+ */
666
+ fAddVariable = function(aVariableDeclaration) {
667
+ fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]);
668
+ },
669
+ /**
670
+ * Increments the count of the number of occurrences of the prefixed
671
+ * String representation attributed to the primary expression.
672
+ * @param {number} nCategory The category of the primary expression.
673
+ * @param {string} sName The prefixed String representation attributed
674
+ * to the primary expression.
675
+ */
676
+ fCountPrimaryExpression = function(nCategory, sName) {
677
+ if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) {
678
+ oSourceElementData.aCount[nCategory][sName] = 0;
679
+ if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) {
680
+ oSourceElementData.aPrimitiveValues.push(sName);
681
+ }
682
+ }
683
+ oSourceElementData.aCount[nCategory][sName] += 1;
684
+ },
685
+ /**
686
+ * Consolidates all worthwhile primitive values in a range of source
687
+ * elements.
688
+ * @param {number} nFrom The index (in the source text order) of the
689
+ * source element that is the first element of the range.
690
+ * @param {number} nTo The index (in the source text order) of the
691
+ * source element that is the last element of the range.
692
+ * @param {boolean} bEnclose Indicates whether the range should be
693
+ * enclosed within a function call with no argument values to a
694
+ * function with an empty parameter list if any primitive values
695
+ * are consolidated.
696
+ * @see TPrimitiveValue#nSaving
697
+ */
698
+ fExamineSourceElements = function(nFrom, nTo, bEnclose) {
699
+ var _,
700
+ /**
701
+ * The index of the last mangled name.
702
+ * @type {number}
703
+ */
704
+ nIndex = oScope.cname,
705
+ /**
706
+ * The index of the source element that is currently being
707
+ * considered.
708
+ * @type {number}
709
+ */
710
+ nPosition,
711
+ /**
712
+ * A collection of functions used during the consolidation of
713
+ * primitive values and identifier names used as property
714
+ * accessors.
715
+ * @namespace
716
+ * @type {!Object.<string, function(...[*])>}
717
+ */
718
+ oWalkersTransformers = {
719
+ /**
720
+ * If the String value that is equivalent to the sequence of
721
+ * terminal symbols that constitute the encountered identifier
722
+ * name is worthwhile, a syntactic conversion from the dot
723
+ * notation to the bracket notation ensues with that sequence
724
+ * being substituted by an identifier name to which the value
725
+ * is assigned.
726
+ * Applies to property accessors that use the dot notation.
727
+ * @param {!TSyntacticCodeUnit} oExpression The nonterminal
728
+ * MemberExpression.
729
+ * @param {string} sIdentifierName The identifier name used as
730
+ * the property accessor.
731
+ * @return {!Array} A syntactic code unit that is equivalent to
732
+ * the one encountered.
733
+ * @see TPrimitiveValue#nSaving
734
+ */
735
+ 'dot': function(oExpression, sIdentifierName) {
736
+ /**
737
+ * The prefixed String value that is equivalent to the
738
+ * sequence of terminal symbols that constitute the
739
+ * encountered identifier name.
740
+ * @type {string}
741
+ */
742
+ var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName;
743
+
744
+ return oSolutionBest.oPrimitiveValues.hasOwnProperty(
745
+ sPrefixed) &&
746
+ oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
747
+ ['sub',
748
+ oWalker.walk(oExpression),
749
+ ['name',
750
+ oSolutionBest.oPrimitiveValues[sPrefixed].sName]] :
751
+ ['dot', oWalker.walk(oExpression), sIdentifierName];
752
+ },
753
+ /**
754
+ * If the encountered identifier is a null or Boolean literal
755
+ * and its value is worthwhile, the identifier is substituted
756
+ * by an identifier name to which that value is assigned.
757
+ * Applies to identifier names.
758
+ * @param {string} sIdentifier The identifier encountered.
759
+ * @return {!Array} A syntactic code unit that is equivalent to
760
+ * the one encountered.
761
+ * @see TPrimitiveValue#nSaving
762
+ */
763
+ 'name': function(sIdentifier) {
764
+ /**
765
+ * The prefixed representation String of the identifier.
766
+ * @type {string}
767
+ */
768
+ var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier;
769
+
770
+ return [
771
+ 'name',
772
+ oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) &&
773
+ oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
774
+ oSolutionBest.oPrimitiveValues[sPrefixed].sName :
775
+ sIdentifier
776
+ ];
777
+ },
778
+ /**
779
+ * If the encountered String value is worthwhile, it is
780
+ * substituted by an identifier name to which that value is
781
+ * assigned.
782
+ * Applies to String values.
783
+ * @param {string} sStringValue The String value of the string
784
+ * literal encountered.
785
+ * @return {!Array} A syntactic code unit that is equivalent to
786
+ * the one encountered.
787
+ * @see TPrimitiveValue#nSaving
788
+ */
789
+ 'string': function(sStringValue) {
790
+ /**
791
+ * The prefixed representation String of the primitive value
792
+ * of the literal.
793
+ * @type {string}
794
+ */
795
+ var sPrefixed =
796
+ EValuePrefixes.S_STRING + sStringValue;
797
+
798
+ return oSolutionBest.oPrimitiveValues.hasOwnProperty(
799
+ sPrefixed) &&
800
+ oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
801
+ ['name',
802
+ oSolutionBest.oPrimitiveValues[sPrefixed].sName] :
803
+ ['string', sStringValue];
804
+ }
805
+ },
806
+ /**
807
+ * Such data on what to consolidate within the range of source
808
+ * elements that is currently being considered that lead to the
809
+ * greatest known reduction of the number of the terminal symbols
810
+ * in comparison to the original source text.
811
+ * @type {!TSolution}
812
+ */
813
+ oSolutionBest = new TSolution(),
814
+ /**
815
+ * Data representing an ongoing attempt to find a better
816
+ * reduction of the number of the terminal symbols in comparison
817
+ * to the original source text than the best one that is
818
+ * currently known.
819
+ * @type {!TSolution}
820
+ * @see oSolutionBest
821
+ */
822
+ oSolutionCandidate = new TSolution(),
823
+ /**
824
+ * A record consisting of data about the range of source elements
825
+ * that is currently being examined.
826
+ * @type {!TSourceElementsData}
827
+ */
828
+ oSourceElementsData = new TSourceElementsData(),
829
+ /**
830
+ * Variable declarations for each primitive value that is to be
831
+ * consolidated within the elements.
832
+ * @type {!Array.<!Array>}
833
+ */
834
+ aVariableDeclarations = [],
835
+ /**
836
+ * Augments a list with a prefixed representation String.
837
+ * @param {!Array.<string>} aList A list that is to be augmented.
838
+ * @return {function(string)} A function that augments a list
839
+ * with a prefixed representation String.
840
+ */
841
+ cAugmentList = function(aList) {
842
+ /**
843
+ * @param {string} sPrefixed Prefixed representation String of
844
+ * a primitive value that could be consolidated within the
845
+ * elements.
846
+ */
847
+ var fLambda = function(sPrefixed) {
848
+ if (-1 === aList.indexOf(sPrefixed)) {
849
+ aList.push(sPrefixed);
850
+ }
851
+ };
852
+
853
+ return fLambda;
854
+ },
855
+ /**
856
+ * Adds the number of occurrences of a primitive value of a given
857
+ * category that could be consolidated in the source element with
858
+ * a given index to the count of occurrences of that primitive
859
+ * value within the range of source elements that is currently
860
+ * being considered.
861
+ * @param {number} nPosition The index (in the source text order)
862
+ * of a source element.
863
+ * @param {number} nCategory The category of the primary
864
+ * expression from which the primitive value is derived.
865
+ * @return {function(string)} A function that performs the
866
+ * addition.
867
+ * @see cAddOccurrencesInCategory
868
+ */
869
+ cAddOccurrences = function(nPosition, nCategory) {
870
+ /**
871
+ * @param {string} sPrefixed The prefixed representation String
872
+ * of a primitive value.
873
+ */
874
+ var fLambda = function(sPrefixed) {
875
+ if (!oSourceElementsData.aCount[nCategory].hasOwnProperty(
876
+ sPrefixed)) {
877
+ oSourceElementsData.aCount[nCategory][sPrefixed] = 0;
878
+ }
879
+ oSourceElementsData.aCount[nCategory][sPrefixed] +=
880
+ aSourceElementsData[nPosition].aCount[nCategory][
881
+ sPrefixed];
882
+ };
883
+
884
+ return fLambda;
885
+ },
886
+ /**
887
+ * Adds the number of occurrences of each primitive value of a
888
+ * given category that could be consolidated in the source
889
+ * element with a given index to the count of occurrences of that
890
+ * primitive values within the range of source elements that is
891
+ * currently being considered.
892
+ * @param {number} nPosition The index (in the source text order)
893
+ * of a source element.
894
+ * @return {function(number)} A function that performs the
895
+ * addition.
896
+ * @see fAddOccurrences
897
+ */
898
+ cAddOccurrencesInCategory = function(nPosition) {
899
+ /**
900
+ * @param {number} nCategory The category of the primary
901
+ * expression from which the primitive value is derived.
902
+ */
903
+ var fLambda = function(nCategory) {
904
+ Object.keys(
905
+ aSourceElementsData[nPosition].aCount[nCategory]
906
+ ).forEach(cAddOccurrences(nPosition, nCategory));
907
+ };
908
+
909
+ return fLambda;
910
+ },
911
+ /**
912
+ * Adds the number of occurrences of each primitive value that
913
+ * could be consolidated in the source element with a given index
914
+ * to the count of occurrences of that primitive values within
915
+ * the range of source elements that is currently being
916
+ * considered.
917
+ * @param {number} nPosition The index (in the source text order)
918
+ * of a source element.
919
+ */
920
+ fAddOccurrences = function(nPosition) {
921
+ Object.keys(aSourceElementsData[nPosition].aCount).forEach(
922
+ cAddOccurrencesInCategory(nPosition));
923
+ },
924
+ /**
925
+ * Creates a variable declaration for a primitive value if that
926
+ * primitive value is to be consolidated within the elements.
927
+ * @param {string} sPrefixed Prefixed representation String of a
928
+ * primitive value that could be consolidated within the
929
+ * elements.
930
+ * @see aVariableDeclarations
931
+ */
932
+ cAugmentVariableDeclarations = function(sPrefixed) {
933
+ if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) {
934
+ aVariableDeclarations.push([
935
+ oSolutionBest.oPrimitiveValues[sPrefixed].sName,
936
+ [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ?
937
+ 'name' : 'string',
938
+ sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)]
939
+ ]);
940
+ }
941
+ },
942
+ /**
943
+ * Sorts primitive values with regard to the difference in the
944
+ * number of terminal symbols between the original source text
945
+ * and the one with those primitive values consolidated.
946
+ * @param {string} sPrefixed0 The prefixed representation String
947
+ * of the first of the two primitive values that are being
948
+ * compared.
949
+ * @param {string} sPrefixed1 The prefixed representation String
950
+ * of the second of the two primitive values that are being
951
+ * compared.
952
+ * @return {number}
953
+ * <dl>
954
+ * <dt>-1</dt>
955
+ * <dd>if the first primitive value must be placed before
956
+ * the other one,</dd>
957
+ * <dt>0</dt>
958
+ * <dd>if the first primitive value may be placed before
959
+ * the other one,</dd>
960
+ * <dt>1</dt>
961
+ * <dd>if the first primitive value must not be placed
962
+ * before the other one.</dd>
963
+ * </dl>
964
+ * @see TSolution.oPrimitiveValues
965
+ */
966
+ cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) {
967
+ /**
968
+ * The difference between:
969
+ * <ol>
970
+ * <li>the difference in the number of terminal symbols
971
+ * between the original source text and the one with the
972
+ * first primitive value consolidated, and</li>
973
+ * <li>the difference in the number of terminal symbols
974
+ * between the original source text and the one with the
975
+ * second primitive value consolidated.</li>
976
+ * </ol>
977
+ * @type {number}
978
+ */
979
+ var nDifference =
980
+ oSolutionCandidate.oPrimitiveValues[sPrefixed0].nSaving -
981
+ oSolutionCandidate.oPrimitiveValues[sPrefixed1].nSaving;
982
+
983
+ return nDifference > 0 ? -1 : nDifference < 0 ? 1 : 0;
984
+ },
985
+ /**
986
+ * Assigns an identifier name to a primitive value and calculates
987
+ * whether instances of that primitive value are worth
988
+ * consolidating.
989
+ * @param {string} sPrefixed The prefixed representation String
990
+ * of a primitive value that is being evaluated.
991
+ */
992
+ fEvaluatePrimitiveValue = function(sPrefixed) {
993
+ var _,
994
+ /**
995
+ * The index of the last mangled name.
996
+ * @type {number}
997
+ */
998
+ nIndex,
999
+ /**
1000
+ * The representation String of the primitive value that is
1001
+ * being evaluated.
1002
+ * @type {string}
1003
+ */
1004
+ sName =
1005
+ sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length),
1006
+ /**
1007
+ * The number of source characters taken up by the
1008
+ * representation String of the primitive value that is
1009
+ * being evaluated.
1010
+ * @type {number}
1011
+ */
1012
+ nLengthOriginal = sName.length,
1013
+ /**
1014
+ * The number of source characters taken up by the
1015
+ * identifier name that could substitute the primitive
1016
+ * value that is being evaluated.
1017
+ * substituted.
1018
+ * @type {number}
1019
+ */
1020
+ nLengthSubstitution,
1021
+ /**
1022
+ * The number of source characters taken up by by the
1023
+ * representation String of the primitive value that is
1024
+ * being evaluated when it is represented by a string
1025
+ * literal.
1026
+ * @type {number}
1027
+ */
1028
+ nLengthString = oProcessor.make_string(sName).length;
1029
+
1030
+ oSolutionCandidate.oPrimitiveValues[sPrefixed] =
1031
+ new TPrimitiveValue();
1032
+ do { // Find an identifier unused in this or any nested scope.
1033
+ nIndex = oScope.cname;
1034
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].sName =
1035
+ oScope.next_mangled();
1036
+ } while (-1 !== oSourceElementsData.aIdentifiers.indexOf(
1037
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].sName));
1038
+ nLengthSubstitution = oSolutionCandidate.oPrimitiveValues[
1039
+ sPrefixed].sName.length;
1040
+ if (0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC)) {
1041
+ // foo:null, or foo:null;
1042
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
1043
+ nLengthSubstitution + nLengthOriginal +
1044
+ oWeights.N_VARIABLE_DECLARATION;
1045
+ // null vs foo
1046
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
1047
+ oSourceElementsData.aCount[
1048
+ EPrimaryExpressionCategories.
1049
+ N_NULL_AND_BOOLEAN_LITERALS][sPrefixed] *
1050
+ (nLengthOriginal - nLengthSubstitution);
1051
+ } else {
1052
+ // foo:'fromCharCode';
1053
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
1054
+ nLengthSubstitution + nLengthString +
1055
+ oWeights.N_VARIABLE_DECLARATION;
1056
+ // .fromCharCode vs [foo]
1057
+ if (oSourceElementsData.aCount[
1058
+ EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
1059
+ ].hasOwnProperty(sPrefixed)) {
1060
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
1061
+ oSourceElementsData.aCount[
1062
+ EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
1063
+ ][sPrefixed] *
1064
+ (nLengthOriginal - nLengthSubstitution -
1065
+ oWeights.N_PROPERTY_ACCESSOR);
1066
+ }
1067
+ // 'fromCharCode' vs foo
1068
+ if (oSourceElementsData.aCount[
1069
+ EPrimaryExpressionCategories.N_STRING_LITERALS
1070
+ ].hasOwnProperty(sPrefixed)) {
1071
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
1072
+ oSourceElementsData.aCount[
1073
+ EPrimaryExpressionCategories.N_STRING_LITERALS
1074
+ ][sPrefixed] *
1075
+ (nLengthString - nLengthSubstitution);
1076
+ }
1077
+ }
1078
+ if (oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving >
1079
+ 0) {
1080
+ oSolutionCandidate.nSavings +=
1081
+ oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving;
1082
+ } else {
1083
+ oScope.cname = nIndex; // Free the identifier name.
1084
+ }
1085
+ },
1086
+ /**
1087
+ * Adds a variable declaration to an existing variable statement.
1088
+ * @param {!Array} aVariableDeclaration A variable declaration
1089
+ * with an initialiser.
1090
+ */
1091
+ cAddVariableDeclaration = function(aVariableDeclaration) {
1092
+ (/** @type {!Array} */ oSourceElements[nFrom][1]).unshift(
1093
+ aVariableDeclaration);
1094
+ };
1095
+
1096
+ if (nFrom > nTo) {
1097
+ return;
1098
+ }
1099
+ // If the range is a closure, reuse the closure.
1100
+ if (nFrom === nTo &&
1101
+ 'stat' === oSourceElements[nFrom][0] &&
1102
+ 'call' === oSourceElements[nFrom][1][0] &&
1103
+ 'function' === oSourceElements[nFrom][1][1][0]) {
1104
+ fExamineSyntacticCodeUnit(oSourceElements[nFrom][1][1]);
1105
+ return;
1106
+ }
1107
+ // Create a list of all derived primitive values within the range.
1108
+ for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
1109
+ aSourceElementsData[nPosition].aPrimitiveValues.forEach(
1110
+ cAugmentList(oSourceElementsData.aPrimitiveValues));
1111
+ }
1112
+ if (0 === oSourceElementsData.aPrimitiveValues.length) {
1113
+ return;
1114
+ }
1115
+ for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
1116
+ // Add the number of occurrences to the total count.
1117
+ fAddOccurrences(nPosition);
1118
+ // Add identifiers of this or any nested scope to the list.
1119
+ aSourceElementsData[nPosition].aIdentifiers.forEach(
1120
+ cAugmentList(oSourceElementsData.aIdentifiers));
1121
+ }
1122
+ // Distribute identifier names among derived primitive values.
1123
+ do { // If there was any progress, find a better distribution.
1124
+ oSolutionBest = oSolutionCandidate;
1125
+ if (Object.keys(oSolutionCandidate.oPrimitiveValues).length > 0) {
1126
+ // Sort primitive values descending by their worthwhileness.
1127
+ oSourceElementsData.aPrimitiveValues.sort(cSortPrimitiveValues);
1128
+ }
1129
+ oSolutionCandidate = new TSolution();
1130
+ oSourceElementsData.aPrimitiveValues.forEach(
1131
+ fEvaluatePrimitiveValue);
1132
+ oScope.cname = nIndex;
1133
+ } while (oSolutionCandidate.nSavings > oSolutionBest.nSavings);
1134
+ // Take the necessity of adding a variable statement into account.
1135
+ if ('var' !== oSourceElements[nFrom][0]) {
1136
+ oSolutionBest.nSavings -= oWeights.N_VARIABLE_STATEMENT_AFFIXATION;
1137
+ }
1138
+ if (bEnclose) {
1139
+ // Take the necessity of forming a closure into account.
1140
+ oSolutionBest.nSavings -= oWeights.N_CLOSURE;
1141
+ }
1142
+ if (oSolutionBest.nSavings > 0) {
1143
+ // Create variable declarations suitable for UglifyJS.
1144
+ Object.keys(oSolutionBest.oPrimitiveValues).forEach(
1145
+ cAugmentVariableDeclarations);
1146
+ // Rewrite expressions that contain worthwhile primitive values.
1147
+ for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
1148
+ oWalker = oProcessor.ast_walker();
1149
+ oSourceElements[nPosition] =
1150
+ oWalker.with_walkers(
1151
+ oWalkersTransformers,
1152
+ cContext(oWalker, oSourceElements[nPosition]));
1153
+ }
1154
+ if ('var' === oSourceElements[nFrom][0]) { // Reuse the statement.
1155
+ (/** @type {!Array.<!Array>} */ aVariableDeclarations.reverse(
1156
+ )).forEach(cAddVariableDeclaration);
1157
+ } else { // Add a variable statement.
1158
+ Array.prototype.splice.call(
1159
+ oSourceElements,
1160
+ nFrom,
1161
+ 0,
1162
+ ['var', aVariableDeclarations]);
1163
+ nTo += 1;
1164
+ }
1165
+ if (bEnclose) {
1166
+ // Add a closure.
1167
+ Array.prototype.splice.call(
1168
+ oSourceElements,
1169
+ nFrom,
1170
+ 0,
1171
+ ['stat', ['call', ['function', null, [], []], []]]);
1172
+ // Copy source elements into the closure.
1173
+ for (nPosition = nTo + 1; nPosition > nFrom; nPosition -= 1) {
1174
+ Array.prototype.unshift.call(
1175
+ oSourceElements[nFrom][1][1][3],
1176
+ oSourceElements[nPosition]);
1177
+ }
1178
+ // Remove source elements outside the closure.
1179
+ Array.prototype.splice.call(
1180
+ oSourceElements,
1181
+ nFrom + 1,
1182
+ nTo - nFrom + 1);
1183
+ }
1184
+ }
1185
+ if (bEnclose) {
1186
+ // Restore the availability of identifier names.
1187
+ oScope.cname = nIndex;
1188
+ }
1189
+ };
1190
+
1191
+ oSourceElements = (/** @type {!TSyntacticCodeUnit} */
1192
+ oSyntacticCodeUnit[bIsGlobal ? 1 : 3]);
1193
+ if (0 === oSourceElements.length) {
1194
+ return;
1195
+ }
1196
+ oScope = bIsGlobal ? oSyntacticCodeUnit.scope : oSourceElements.scope;
1197
+ // Skip a Directive Prologue.
1198
+ while (nAfterDirectivePrologue < oSourceElements.length &&
1199
+ 'stat' === oSourceElements[nAfterDirectivePrologue][0] &&
1200
+ 'string' === oSourceElements[nAfterDirectivePrologue][1][0]) {
1201
+ nAfterDirectivePrologue += 1;
1202
+ aSourceElementsData.push(null);
1203
+ }
1204
+ if (oSourceElements.length === nAfterDirectivePrologue) {
1205
+ return;
1206
+ }
1207
+ for (nPosition = nAfterDirectivePrologue;
1208
+ nPosition < oSourceElements.length;
1209
+ nPosition += 1) {
1210
+ oSourceElementData = new TSourceElementsData();
1211
+ oWalker = oProcessor.ast_walker();
1212
+ // Classify a source element.
1213
+ // Find its derived primitive values and count their occurrences.
1214
+ // Find all identifiers used (including nested scopes).
1215
+ oWalker.with_walkers(
1216
+ oWalkers.oSurveySourceElement,
1217
+ cContext(oWalker, oSourceElements[nPosition]));
1218
+ // Establish whether the scope is still wholly examinable.
1219
+ bIsWhollyExaminable = bIsWhollyExaminable &&
1220
+ ESourceElementCategories.N_WITH !== oSourceElementData.nCategory &&
1221
+ ESourceElementCategories.N_EVAL !== oSourceElementData.nCategory;
1222
+ aSourceElementsData.push(oSourceElementData);
1223
+ }
1224
+ if (bIsWhollyExaminable) { // Examine the whole scope.
1225
+ fExamineSourceElements(
1226
+ nAfterDirectivePrologue,
1227
+ oSourceElements.length - 1,
1228
+ false);
1229
+ } else { // Examine unexcluded ranges of source elements.
1230
+ for (nPosition = oSourceElements.length - 1;
1231
+ nPosition >= nAfterDirectivePrologue;
1232
+ nPosition -= 1) {
1233
+ oSourceElementData = (/** @type {!TSourceElementsData} */
1234
+ aSourceElementsData[nPosition]);
1235
+ if (ESourceElementCategories.N_OTHER ===
1236
+ oSourceElementData.nCategory) {
1237
+ if ('undefined' === typeof nTo) {
1238
+ nTo = nPosition; // Indicate the end of a range.
1239
+ }
1240
+ // Examine the range if it immediately follows a Directive Prologue.
1241
+ if (nPosition === nAfterDirectivePrologue) {
1242
+ fExamineSourceElements(nPosition, nTo, true);
1243
+ }
1244
+ } else {
1245
+ if ('undefined' !== typeof nTo) {
1246
+ // Examine the range that immediately follows this source element.
1247
+ fExamineSourceElements(nPosition + 1, nTo, true);
1248
+ nTo = void 0; // Obliterate the range.
1249
+ }
1250
+ // Examine nested functions.
1251
+ oWalker = oProcessor.ast_walker();
1252
+ oWalker.with_walkers(
1253
+ oWalkers.oExamineFunctions,
1254
+ cContext(oWalker, oSourceElements[nPosition]));
1255
+ }
1256
+ }
1257
+ }
1258
+ }(oAbstractSyntaxTree = oProcessor.ast_add_scope(oAbstractSyntaxTree)));
1259
+ return oAbstractSyntaxTree;
1260
+ };
1261
+ /*jshint sub:false */
1262
+
1263
+
1264
+ if (require.main === module) {
1265
+ (function() {
1266
+ 'use strict';
1267
+ /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
1268
+ latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
1269
+ onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
1270
+ sub:false, trailing:true */
1271
+
1272
+ var _,
1273
+ /**
1274
+ * NodeJS module for unit testing.
1275
+ * @namespace
1276
+ * @type {!TAssert}
1277
+ * @see http://nodejs.org/docs/v0.6.10/api/all.html#assert
1278
+ */
1279
+ oAssert = (/** @type {!TAssert} */ require('assert')),
1280
+ /**
1281
+ * The parser of ECMA-262 found in UglifyJS.
1282
+ * @namespace
1283
+ * @type {!TParser}
1284
+ */
1285
+ oParser = (/** @type {!TParser} */ require('./parse-js')),
1286
+ /**
1287
+ * The processor of <abbr title="abstract syntax tree">AST</abbr>s
1288
+ * found in UglifyJS.
1289
+ * @namespace
1290
+ * @type {!TProcessor}
1291
+ */
1292
+ oProcessor = (/** @type {!TProcessor} */ require('./process')),
1293
+ /**
1294
+ * An instance of an object that allows the traversal of an <abbr
1295
+ * title="abstract syntax tree">AST</abbr>.
1296
+ * @type {!TWalker}
1297
+ */
1298
+ oWalker,
1299
+ /**
1300
+ * A collection of functions for the removal of the scope information
1301
+ * during the traversal of an <abbr title="abstract syntax tree"
1302
+ * >AST</abbr>.
1303
+ * @namespace
1304
+ * @type {!Object.<string, function(...[*])>}
1305
+ */
1306
+ oWalkersPurifiers = {
1307
+ /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
1308
+ /**
1309
+ * Deletes the scope information from the branch of the abstract
1310
+ * syntax tree representing the encountered function declaration.
1311
+ * @param {string} sIdentifier The identifier of the function.
1312
+ * @param {!Array.<string>} aFormalParameterList Formal parameters.
1313
+ * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
1314
+ */
1315
+ 'defun': function(
1316
+ sIdentifier,
1317
+ aFormalParameterList,
1318
+ oFunctionBody) {
1319
+ delete oFunctionBody.scope;
1320
+ },
1321
+ /**
1322
+ * Deletes the scope information from the branch of the abstract
1323
+ * syntax tree representing the encountered function expression.
1324
+ * @param {?string} sIdentifier The optional identifier of the
1325
+ * function.
1326
+ * @param {!Array.<string>} aFormalParameterList Formal parameters.
1327
+ * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
1328
+ */
1329
+ 'function': function(
1330
+ sIdentifier,
1331
+ aFormalParameterList,
1332
+ oFunctionBody) {
1333
+ delete oFunctionBody.scope;
1334
+ }
1335
+ /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
1336
+ },
1337
+ /**
1338
+ * Initiates the traversal of a source element.
1339
+ * @param {!TWalker} oWalker An instance of an object that allows the
1340
+ * traversal of an abstract syntax tree.
1341
+ * @param {!TSyntacticCodeUnit} oSourceElement A source element from
1342
+ * which the traversal should commence.
1343
+ * @return {function(): !TSyntacticCodeUnit} A function that is able to
1344
+ * initiate the traversal from a given source element.
1345
+ */
1346
+ cContext = function(oWalker, oSourceElement) {
1347
+ /**
1348
+ * @return {!TSyntacticCodeUnit} A function that is able to
1349
+ * initiate the traversal from a given source element.
1350
+ */
1351
+ var fLambda = function() {
1352
+ return oWalker.walk(oSourceElement);
1353
+ };
1354
+
1355
+ return fLambda;
1356
+ },
1357
+ /**
1358
+ * A record consisting of configuration for the code generation phase.
1359
+ * @type {!Object}
1360
+ */
1361
+ oCodeGenerationOptions = {
1362
+ beautify: true
1363
+ },
1364
+ /**
1365
+ * Tests whether consolidation of an ECMAScript program yields expected
1366
+ * results.
1367
+ * @param {{
1368
+ * sTitle: string,
1369
+ * sInput: string,
1370
+ * sOutput: string
1371
+ * }} oUnitTest A record consisting of data about a unit test: its
1372
+ * name, an ECMAScript program, and, if consolidation is to take
1373
+ * place, the resulting ECMAScript program.
1374
+ */
1375
+ cAssert = function(oUnitTest) {
1376
+ var _,
1377
+ /**
1378
+ * An array-like object representing the <abbr title=
1379
+ * "abstract syntax tree">AST</abbr> obtained after consolidation.
1380
+ * @type {!TSyntacticCodeUnit}
1381
+ */
1382
+ oSyntacticCodeUnitActual =
1383
+ exports.ast_consolidate(oParser.parse(oUnitTest.sInput)),
1384
+ /**
1385
+ * An array-like object representing the expected <abbr title=
1386
+ * "abstract syntax tree">AST</abbr>.
1387
+ * @type {!TSyntacticCodeUnit}
1388
+ */
1389
+ oSyntacticCodeUnitExpected = oParser.parse(
1390
+ oUnitTest.hasOwnProperty('sOutput') ?
1391
+ oUnitTest.sOutput : oUnitTest.sInput);
1392
+
1393
+ delete oSyntacticCodeUnitActual.scope;
1394
+ oWalker = oProcessor.ast_walker();
1395
+ oWalker.with_walkers(
1396
+ oWalkersPurifiers,
1397
+ cContext(oWalker, oSyntacticCodeUnitActual));
1398
+ try {
1399
+ oAssert.deepEqual(
1400
+ oSyntacticCodeUnitActual,
1401
+ oSyntacticCodeUnitExpected);
1402
+ } catch (oException) {
1403
+ console.error(
1404
+ '########## A unit test has failed.\n' +
1405
+ oUnitTest.sTitle + '\n' +
1406
+ '##### actual code (' +
1407
+ oProcessor.gen_code(oSyntacticCodeUnitActual).length +
1408
+ ' bytes)\n' +
1409
+ oProcessor.gen_code(
1410
+ oSyntacticCodeUnitActual,
1411
+ oCodeGenerationOptions) + '\n' +
1412
+ '##### expected code (' +
1413
+ oProcessor.gen_code(oSyntacticCodeUnitExpected).length +
1414
+ ' bytes)\n' +
1415
+ oProcessor.gen_code(
1416
+ oSyntacticCodeUnitExpected,
1417
+ oCodeGenerationOptions));
1418
+ }
1419
+ };
1420
+
1421
+ [
1422
+ // 7.6.1 Reserved Words.
1423
+ {
1424
+ sTitle:
1425
+ 'Omission of keywords while choosing an identifier name.',
1426
+ sInput:
1427
+ '(function() {' +
1428
+ ' var a, b, c, d, e, f, g, h, i, j, k, l, m,' +
1429
+ ' n, o, p, q, r, s, t, u, v, w, x, y, z,' +
1430
+ ' A, B, C, D, E, F, G, H, I, J, K, L, M,' +
1431
+ ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
1432
+ ' $, _,' +
1433
+ ' aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am,' +
1434
+ ' an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az,' +
1435
+ ' aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM,' +
1436
+ ' aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ,' +
1437
+ ' a$, a_,' +
1438
+ ' ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm,' +
1439
+ ' bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz,' +
1440
+ ' bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM,' +
1441
+ ' bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ,' +
1442
+ ' b$, b_,' +
1443
+ ' ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm,' +
1444
+ ' cn, co, cp, cq, cr, cs, ct, cu, cv, cw, cx, cy, cz,' +
1445
+ ' cA, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM,' +
1446
+ ' cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ,' +
1447
+ ' c$, c_,' +
1448
+ ' da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm,' +
1449
+ ' dn, dq, dr, ds, dt, du, dv, dw, dx, dy, dz,' +
1450
+ ' dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM,' +
1451
+ ' dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ,' +
1452
+ ' d$, d_;' +
1453
+ ' void ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",' +
1454
+ ' "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];' +
1455
+ '}());',
1456
+ sOutput:
1457
+ '(function() {' +
1458
+ ' var dp =' +
1459
+ ' "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",' +
1460
+ ' a, b, c, d, e, f, g, h, i, j, k, l, m,' +
1461
+ ' n, o, p, q, r, s, t, u, v, w, x, y, z,' +
1462
+ ' A, B, C, D, E, F, G, H, I, J, K, L, M,' +
1463
+ ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
1464
+ ' $, _,' +
1465
+ ' aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am,' +
1466
+ ' an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az,' +
1467
+ ' aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM,' +
1468
+ ' aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ,' +
1469
+ ' a$, a_,' +
1470
+ ' ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm,' +
1471
+ ' bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz,' +
1472
+ ' bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM,' +
1473
+ ' bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ,' +
1474
+ ' b$, b_,' +
1475
+ ' ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm,' +
1476
+ ' cn, co, cp, cq, cr, cs, ct, cu, cv, cw, cx, cy, cz,' +
1477
+ ' cA, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM,' +
1478
+ ' cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ,' +
1479
+ ' c$, c_,' +
1480
+ ' da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm,' +
1481
+ ' dn, dq, dr, ds, dt, du, dv, dw, dx, dy, dz,' +
1482
+ ' dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM,' +
1483
+ ' dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ,' +
1484
+ ' d$, d_;' +
1485
+ ' void [dp, dp];' +
1486
+ '}());'
1487
+ },
1488
+ // 7.8.1 Null Literals.
1489
+ {
1490
+ sTitle:
1491
+ 'Evaluation with regard to the null value.',
1492
+ sInput:
1493
+ '/*jshint evil:true */' +
1494
+ '(function() {' +
1495
+ ' var foo;' +
1496
+ ' void [null, null, null];' +
1497
+ '}());' +
1498
+ 'eval("");' +
1499
+ '(function() {' +
1500
+ ' var foo;' +
1501
+ ' void [null, null];' +
1502
+ '}());',
1503
+ sOutput:
1504
+ '/*jshint evil:true */' +
1505
+ '(function() {' +
1506
+ ' var a = null, foo;' +
1507
+ ' void [a, a, a];' +
1508
+ '}());' +
1509
+ 'eval("");' +
1510
+ '(function() {' +
1511
+ ' var foo;' +
1512
+ ' void [null, null];' +
1513
+ '}());'
1514
+ },
1515
+ // 7.8.2 Boolean Literals.
1516
+ {
1517
+ sTitle:
1518
+ 'Evaluation with regard to the false value.',
1519
+ sInput:
1520
+ '/*jshint evil:true */' +
1521
+ '(function() {' +
1522
+ ' var foo;' +
1523
+ ' void [false, false, false];' +
1524
+ '}());' +
1525
+ 'eval("");' +
1526
+ '(function() {' +
1527
+ ' var foo;' +
1528
+ ' void [false, false];' +
1529
+ '}());',
1530
+ sOutput:
1531
+ '/*jshint evil:true */' +
1532
+ '(function() {' +
1533
+ ' var a = false, foo;' +
1534
+ ' void [a, a, a];' +
1535
+ '}());' +
1536
+ 'eval("");' +
1537
+ '(function() {' +
1538
+ ' var foo;' +
1539
+ ' void [false, false];' +
1540
+ '}());'
1541
+ },
1542
+ {
1543
+ sTitle:
1544
+ 'Evaluation with regard to the true value.',
1545
+ sInput:
1546
+ '/*jshint evil:true */' +
1547
+ '(function() {' +
1548
+ ' var foo;' +
1549
+ ' void [true, true, true];' +
1550
+ '}());' +
1551
+ 'eval("");' +
1552
+ '(function() {' +
1553
+ ' var foo;' +
1554
+ ' void [true, true];' +
1555
+ '}());',
1556
+ sOutput:
1557
+ '/*jshint evil:true */' +
1558
+ '(function() {' +
1559
+ ' var a = true, foo;' +
1560
+ ' void [a, a, a];' +
1561
+ '}());' +
1562
+ 'eval("");' +
1563
+ '(function() {' +
1564
+ ' var foo;' +
1565
+ ' void [true, true];' +
1566
+ '}());'
1567
+ },
1568
+ // 7.8.4 String Literals.
1569
+ {
1570
+ sTitle:
1571
+ 'Evaluation with regard to the String value of a string literal.',
1572
+ sInput:
1573
+ '(function() {' +
1574
+ ' var foo;' +
1575
+ ' void ["abcd", "abcd", "abc", "abc"];' +
1576
+ '}());',
1577
+ sOutput:
1578
+ '(function() {' +
1579
+ ' var a = "abcd", foo;' +
1580
+ ' void [a, a, "abc", "abc"];' +
1581
+ '}());'
1582
+ },
1583
+ // 7.8.5 Regular Expression Literals.
1584
+ {
1585
+ sTitle:
1586
+ 'Preservation of the pattern of a regular expression literal.',
1587
+ sInput:
1588
+ 'void [/abcdefghijklmnopqrstuvwxyz/, /abcdefghijklmnopqrstuvwxyz/];'
1589
+ },
1590
+ {
1591
+ sTitle:
1592
+ 'Preservation of the flags of a regular expression literal.',
1593
+ sInput:
1594
+ 'void [/(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim,' +
1595
+ ' /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim,' +
1596
+ ' /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim];'
1597
+ },
1598
+ // 10.2 Lexical Environments.
1599
+ {
1600
+ sTitle:
1601
+ 'Preservation of identifier names in the same scope.',
1602
+ sInput:
1603
+ '/*jshint shadow:true */' +
1604
+ 'var a;' +
1605
+ 'function b(i) {' +
1606
+ '}' +
1607
+ 'for (var c; 0 === Math.random(););' +
1608
+ 'for (var d in {});' +
1609
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
1610
+ 'void [b(a), b(c), b(d)];' +
1611
+ 'void [typeof e];' +
1612
+ 'i: for (; 0 === Math.random();) {' +
1613
+ ' if (42 === (new Date()).getMinutes()) {' +
1614
+ ' continue i;' +
1615
+ ' } else {' +
1616
+ ' break i;' +
1617
+ ' }' +
1618
+ '}' +
1619
+ 'try {' +
1620
+ '} catch (f) {' +
1621
+ '} finally {' +
1622
+ '}' +
1623
+ '(function g(h) {' +
1624
+ '}());' +
1625
+ 'void [{' +
1626
+ ' i: 42,' +
1627
+ ' "j": 42,' +
1628
+ ' \'k\': 42' +
1629
+ '}];' +
1630
+ 'void ["abcdefghijklmnopqrstuvwxyz"];',
1631
+ sOutput:
1632
+ '/*jshint shadow:true */' +
1633
+ 'var a;' +
1634
+ 'function b(i) {' +
1635
+ '}' +
1636
+ 'for (var c; 0 === Math.random(););' +
1637
+ 'for (var d in {});' +
1638
+ '(function() {' +
1639
+ ' var i = "abcdefghijklmnopqrstuvwxyz";' +
1640
+ ' void [i];' +
1641
+ ' void [b(a), b(c), b(d)];' +
1642
+ ' void [typeof e];' +
1643
+ ' i: for (; 0 === Math.random();) {' +
1644
+ ' if (42 === (new Date()).getMinutes()) {' +
1645
+ ' continue i;' +
1646
+ ' } else {' +
1647
+ ' break i;' +
1648
+ ' }' +
1649
+ ' }' +
1650
+ ' try {' +
1651
+ ' } catch (f) {' +
1652
+ ' } finally {' +
1653
+ ' }' +
1654
+ ' (function g(h) {' +
1655
+ ' }());' +
1656
+ ' void [{' +
1657
+ ' i: 42,' +
1658
+ ' "j": 42,' +
1659
+ ' \'k\': 42' +
1660
+ ' }];' +
1661
+ ' void [i];' +
1662
+ '}());'
1663
+ },
1664
+ {
1665
+ sTitle:
1666
+ 'Preservation of identifier names in nested function code.',
1667
+ sInput:
1668
+ '(function() {' +
1669
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1670
+ ' (function() {' +
1671
+ ' var a;' +
1672
+ ' for (var b; 0 === Math.random(););' +
1673
+ ' for (var c in {});' +
1674
+ ' void [typeof d];' +
1675
+ ' h: for (; 0 === Math.random();) {' +
1676
+ ' if (42 === (new Date()).getMinutes()) {' +
1677
+ ' continue h;' +
1678
+ ' } else {' +
1679
+ ' break h;' +
1680
+ ' }' +
1681
+ ' }' +
1682
+ ' try {' +
1683
+ ' } catch (e) {' +
1684
+ ' } finally {' +
1685
+ ' }' +
1686
+ ' (function f(g) {' +
1687
+ ' }());' +
1688
+ ' void [{' +
1689
+ ' h: 42,' +
1690
+ ' "i": 42,' +
1691
+ ' \'j\': 42' +
1692
+ ' }];' +
1693
+ ' }());' +
1694
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1695
+ '}());',
1696
+ sOutput:
1697
+ '(function() {' +
1698
+ ' var h = "abcdefghijklmnopqrstuvwxyz";' +
1699
+ ' void [h];' +
1700
+ ' (function() {' +
1701
+ ' var a;' +
1702
+ ' for (var b; 0 === Math.random(););' +
1703
+ ' for (var c in {});' +
1704
+ ' void [typeof d];' +
1705
+ ' h: for (; 0 === Math.random();) {' +
1706
+ ' if (42 === (new Date()).getMinutes()) {' +
1707
+ ' continue h;' +
1708
+ ' } else {' +
1709
+ ' break h;' +
1710
+ ' }' +
1711
+ ' }' +
1712
+ ' try {' +
1713
+ ' } catch (e) {' +
1714
+ ' } finally {' +
1715
+ ' }' +
1716
+ ' (function f(g) {' +
1717
+ ' }());' +
1718
+ ' void [{' +
1719
+ ' h: 42,' +
1720
+ ' "i": 42,' +
1721
+ ' \'j\': 42' +
1722
+ ' }];' +
1723
+ ' }());' +
1724
+ ' void [h];' +
1725
+ '}());'
1726
+ },
1727
+ {
1728
+ sTitle:
1729
+ 'Consolidation of a closure with other source elements.',
1730
+ sInput:
1731
+ '(function(foo) {' +
1732
+ '}("abcdefghijklmnopqrstuvwxyz"));' +
1733
+ 'void ["abcdefghijklmnopqrstuvwxyz"];',
1734
+ sOutput:
1735
+ '(function() {' +
1736
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1737
+ ' (function(foo) {' +
1738
+ ' })(a);' +
1739
+ ' void [a];' +
1740
+ '}());'
1741
+ },
1742
+ {
1743
+ sTitle:
1744
+ 'Consolidation of function code instead of a sole closure.',
1745
+ sInput:
1746
+ '(function(foo, bar) {' +
1747
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
1748
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
1749
+ '}("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"));',
1750
+ sOutput:
1751
+ '(function(foo, bar) {' +
1752
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1753
+ ' void [a, a];' +
1754
+ '}("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"));'
1755
+ },
1756
+ // 11.1.5 Object Initialiser.
1757
+ {
1758
+ sTitle:
1759
+ 'Preservation of property names of an object initialiser.',
1760
+ sInput:
1761
+ 'var foo = {' +
1762
+ ' abcdefghijklmnopqrstuvwxyz: 42,' +
1763
+ ' "zyxwvutsrqponmlkjihgfedcba": 42,' +
1764
+ ' \'mlkjihgfedcbanopqrstuvwxyz\': 42' +
1765
+ '};' +
1766
+ 'void [' +
1767
+ ' foo.abcdefghijklmnopqrstuvwxyz,' +
1768
+ ' "zyxwvutsrqponmlkjihgfedcba",' +
1769
+ ' \'mlkjihgfedcbanopqrstuvwxyz\'' +
1770
+ '];'
1771
+ },
1772
+ {
1773
+ sTitle:
1774
+ 'Evaluation with regard to String values derived from identifier ' +
1775
+ 'names used as property accessors.',
1776
+ sInput:
1777
+ '(function() {' +
1778
+ ' var foo;' +
1779
+ ' void [' +
1780
+ ' Math.abcdefghij,' +
1781
+ ' Math.abcdefghij,' +
1782
+ ' Math.abcdefghi,' +
1783
+ ' Math.abcdefghi' +
1784
+ ' ];' +
1785
+ '}());',
1786
+ sOutput:
1787
+ '(function() {' +
1788
+ ' var a = "abcdefghij", foo;' +
1789
+ ' void [' +
1790
+ ' Math[a],' +
1791
+ ' Math[a],' +
1792
+ ' Math.abcdefghi,' +
1793
+ ' Math.abcdefghi' +
1794
+ ' ];' +
1795
+ '}());'
1796
+ },
1797
+ // 11.2.1 Property Accessors.
1798
+ {
1799
+ sTitle:
1800
+ 'Preservation of identifiers in the nonterminal MemberExpression.',
1801
+ sInput:
1802
+ 'void [' +
1803
+ ' Math.E,' +
1804
+ ' Math.LN10,' +
1805
+ ' Math.LN2,' +
1806
+ ' Math.LOG2E,' +
1807
+ ' Math.LOG10E,' +
1808
+ ' Math.PI,' +
1809
+ ' Math.SQRT1_2,' +
1810
+ ' Math.SQRT2,' +
1811
+ ' Math.abs,' +
1812
+ ' Math.acos' +
1813
+ '];'
1814
+ },
1815
+ // 12.2 Variable Statement.
1816
+ {
1817
+ sTitle:
1818
+ 'Preservation of the identifier of a variable that is being ' +
1819
+ 'declared in a variable statement.',
1820
+ sInput:
1821
+ '(function() {' +
1822
+ ' var abcdefghijklmnopqrstuvwxyz;' +
1823
+ ' void [abcdefghijklmnopqrstuvwxyz];' +
1824
+ '}());'
1825
+ },
1826
+ {
1827
+ sTitle:
1828
+ 'Exclusion of a variable statement in global code.',
1829
+ sInput:
1830
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
1831
+ 'var foo = "abcdefghijklmnopqrstuvwxyz",' +
1832
+ ' bar = "abcdefghijklmnopqrstuvwxyz";' +
1833
+ 'void ["abcdefghijklmnopqrstuvwxyz"];'
1834
+ },
1835
+ {
1836
+ sTitle:
1837
+ 'Exclusion of a variable statement in function code that ' +
1838
+ 'contains a with statement.',
1839
+ sInput:
1840
+ '(function() {' +
1841
+ ' with ({});' +
1842
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1843
+ ' var foo;' +
1844
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1845
+ '}());'
1846
+ },
1847
+ {
1848
+ sTitle:
1849
+ 'Exclusion of a variable statement in function code that ' +
1850
+ 'contains a direct call to the eval function.',
1851
+ sInput:
1852
+ '/*jshint evil:true */' +
1853
+ 'void [' +
1854
+ ' function() {' +
1855
+ ' eval("");' +
1856
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1857
+ ' var foo;' +
1858
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1859
+ ' }' +
1860
+ '];'
1861
+ },
1862
+ {
1863
+ sTitle:
1864
+ 'Consolidation within a variable statement in global code.',
1865
+ sInput:
1866
+ 'var foo = function() {' +
1867
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
1868
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
1869
+ '};',
1870
+ sOutput:
1871
+ 'var foo = function() {' +
1872
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1873
+ ' void [a, a];' +
1874
+ '};'
1875
+ },
1876
+ {
1877
+ sTitle:
1878
+ 'Consolidation within a variable statement excluded in function ' +
1879
+ 'code due to the presence of a with statement.',
1880
+ sInput:
1881
+ '(function() {' +
1882
+ ' with ({});' +
1883
+ ' var foo = function() {' +
1884
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
1885
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
1886
+ ' };' +
1887
+ '}());',
1888
+ sOutput:
1889
+ '(function() {' +
1890
+ ' with ({});' +
1891
+ ' var foo = function() {' +
1892
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1893
+ ' void [a, a];' +
1894
+ ' };' +
1895
+ '}());'
1896
+ },
1897
+ {
1898
+ sTitle:
1899
+ 'Consolidation within a variable statement excluded in function ' +
1900
+ 'code due to the presence of a direct call to the eval function.',
1901
+ sInput:
1902
+ '/*jshint evil:true */' +
1903
+ '(function() {' +
1904
+ ' eval("");' +
1905
+ ' var foo = function() {' +
1906
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
1907
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
1908
+ ' };' +
1909
+ '}());',
1910
+ sOutput:
1911
+ '/*jshint evil:true */' +
1912
+ '(function() {' +
1913
+ ' eval("");' +
1914
+ ' var foo = function() {' +
1915
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1916
+ ' void [a, a];' +
1917
+ ' };' +
1918
+ '}());'
1919
+ },
1920
+ {
1921
+ sTitle:
1922
+ 'Inclusion of a variable statement in function code that ' +
1923
+ 'contains no with statement and no direct call to the eval ' +
1924
+ 'function.',
1925
+ sInput:
1926
+ '(function() {' +
1927
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1928
+ ' var foo;' +
1929
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
1930
+ '}());',
1931
+ sOutput:
1932
+ '(function() {' +
1933
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1934
+ ' void [a];' +
1935
+ ' var foo;' +
1936
+ ' void [a];' +
1937
+ '}());'
1938
+ },
1939
+ {
1940
+ sTitle:
1941
+ 'Ignorance with regard to a variable statement in global code.',
1942
+ sInput:
1943
+ 'var foo = "abcdefghijklmnopqrstuvwxyz";' +
1944
+ 'void ["abcdefghijklmnopqrstuvwxyz",' +
1945
+ ' "abcdefghijklmnopqrstuvwxyz"];',
1946
+ sOutput:
1947
+ 'var foo = "abcdefghijklmnopqrstuvwxyz";' +
1948
+ '(function() {' +
1949
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
1950
+ ' void [a, a];' +
1951
+ '}());'
1952
+ },
1953
+ // 12.4 Expression Statement.
1954
+ {
1955
+ sTitle:
1956
+ 'Preservation of identifiers in an expression statement.',
1957
+ sInput:
1958
+ 'void [typeof abcdefghijklmnopqrstuvwxyz,' +
1959
+ ' typeof abcdefghijklmnopqrstuvwxyz];'
1960
+ },
1961
+ // 12.6.3 The {@code for} Statement.
1962
+ {
1963
+ sTitle:
1964
+ 'Preservation of identifiers in the variable declaration list of ' +
1965
+ 'a for statement.',
1966
+ sInput:
1967
+ 'for (var abcdefghijklmnopqrstuvwxyz; 0 === Math.random(););' +
1968
+ 'for (var abcdefghijklmnopqrstuvwxyz; 0 === Math.random(););'
1969
+ },
1970
+ // 12.6.4 The {@code for-in} Statement.
1971
+ {
1972
+ sTitle:
1973
+ 'Preservation of identifiers in the variable declaration list of ' +
1974
+ 'a for-in statement.',
1975
+ sInput:
1976
+ 'for (var abcdefghijklmnopqrstuvwxyz in {});' +
1977
+ 'for (var abcdefghijklmnopqrstuvwxyz in {});'
1978
+ },
1979
+ // 12.7 The {@code continue} Statement.
1980
+ {
1981
+ sTitle:
1982
+ 'Preservation of the identifier in a continue statement.',
1983
+ sInput:
1984
+ 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
1985
+ ' continue abcdefghijklmnopqrstuvwxyz;' +
1986
+ '}' +
1987
+ 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
1988
+ ' continue abcdefghijklmnopqrstuvwxyz;' +
1989
+ '}'
1990
+ },
1991
+ // 12.8 The {@code break} Statement.
1992
+ {
1993
+ sTitle:
1994
+ 'Preservation of the identifier in a break statement.',
1995
+ sInput:
1996
+ 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
1997
+ ' break abcdefghijklmnopqrstuvwxyz;' +
1998
+ '}' +
1999
+ 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
2000
+ ' break abcdefghijklmnopqrstuvwxyz;' +
2001
+ '}'
2002
+ },
2003
+ // 12.9 The {@code return} Statement.
2004
+ {
2005
+ sTitle:
2006
+ 'Exclusion of a return statement in function code that contains ' +
2007
+ 'a with statement.',
2008
+ sInput:
2009
+ '(function() {' +
2010
+ ' with ({});' +
2011
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
2012
+ ' if (0 === Math.random()) {' +
2013
+ ' return;' +
2014
+ ' } else {' +
2015
+ ' }' +
2016
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
2017
+ '}());'
2018
+ },
2019
+ {
2020
+ sTitle:
2021
+ 'Exclusion of a return statement in function code that contains ' +
2022
+ 'a direct call to the eval function.',
2023
+ sInput:
2024
+ '/*jshint evil:true */' +
2025
+ '(function() {' +
2026
+ ' eval("");' +
2027
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
2028
+ ' if (0 === Math.random()) {' +
2029
+ ' return;' +
2030
+ ' } else {' +
2031
+ ' }' +
2032
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
2033
+ '}());'
2034
+ },
2035
+ {
2036
+ sTitle:
2037
+ 'Consolidation within a return statement excluded in function ' +
2038
+ 'code due to the presence of a with statement.',
2039
+ sInput:
2040
+ '(function() {' +
2041
+ ' with ({});' +
2042
+ ' return function() {' +
2043
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2044
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2045
+ ' };' +
2046
+ '}());',
2047
+ sOutput:
2048
+ '(function() {' +
2049
+ ' with ({});' +
2050
+ ' return function() {' +
2051
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2052
+ ' void [a, a];' +
2053
+ ' };' +
2054
+ '}());'
2055
+ },
2056
+ {
2057
+ sTitle:
2058
+ 'Consolidation within a return statement excluded in function ' +
2059
+ 'code due to the presence of a direct call to the eval function.',
2060
+ sInput:
2061
+ '/*jshint evil:true */' +
2062
+ '(function() {' +
2063
+ ' eval("");' +
2064
+ ' return function() {' +
2065
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2066
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2067
+ ' };' +
2068
+ '}());',
2069
+ sOutput:
2070
+ '/*jshint evil:true */' +
2071
+ '(function() {' +
2072
+ ' eval("");' +
2073
+ ' return function() {' +
2074
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2075
+ ' void [a, a];' +
2076
+ ' };' +
2077
+ '}());'
2078
+ },
2079
+ {
2080
+ sTitle:
2081
+ 'Inclusion of a return statement in function code that contains ' +
2082
+ 'no with statement and no direct call to the eval function.',
2083
+ sInput:
2084
+ '(function() {' +
2085
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
2086
+ ' if (0 === Math.random()) {' +
2087
+ ' return;' +
2088
+ ' } else {' +
2089
+ ' }' +
2090
+ ' void ["abcdefghijklmnopqrstuvwxyz"];' +
2091
+ '}());',
2092
+ sOutput:
2093
+ '(function() {' +
2094
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2095
+ ' void [a];' +
2096
+ ' if (0 === Math.random()) {' +
2097
+ ' return;' +
2098
+ ' } else {' +
2099
+ ' }' +
2100
+ ' void [a];' +
2101
+ '}());'
2102
+ },
2103
+ // 12.10 The {@code with} Statement.
2104
+ {
2105
+ sTitle:
2106
+ 'Preservation of the statement in a with statement.',
2107
+ sInput:
2108
+ 'with ({}) {' +
2109
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2110
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2111
+ '}'
2112
+ },
2113
+ {
2114
+ sTitle:
2115
+ 'Exclusion of a with statement in the same syntactic code unit.',
2116
+ sInput:
2117
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
2118
+ 'with ({' +
2119
+ ' foo: "abcdefghijklmnopqrstuvwxyz",' +
2120
+ ' bar: "abcdefghijklmnopqrstuvwxyz"' +
2121
+ '}) {' +
2122
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2123
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2124
+ '}' +
2125
+ 'void ["abcdefghijklmnopqrstuvwxyz"];'
2126
+ },
2127
+ {
2128
+ sTitle:
2129
+ 'Exclusion of a with statement in nested function code.',
2130
+ sInput:
2131
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
2132
+ '(function() {' +
2133
+ ' with ({' +
2134
+ ' foo: "abcdefghijklmnopqrstuvwxyz",' +
2135
+ ' bar: "abcdefghijklmnopqrstuvwxyz"' +
2136
+ ' }) {' +
2137
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2138
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2139
+ ' }' +
2140
+ '}());' +
2141
+ 'void ["abcdefghijklmnopqrstuvwxyz"];'
2142
+ },
2143
+ // 12.12 Labelled Statements.
2144
+ {
2145
+ sTitle:
2146
+ 'Preservation of the label of a labelled statement.',
2147
+ sInput:
2148
+ 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random(););' +
2149
+ 'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random(););'
2150
+ },
2151
+ // 12.14 The {@code try} Statement.
2152
+ {
2153
+ sTitle:
2154
+ 'Preservation of the identifier in the catch clause of a try' +
2155
+ 'statement.',
2156
+ sInput:
2157
+ 'try {' +
2158
+ '} catch (abcdefghijklmnopqrstuvwxyz) {' +
2159
+ '} finally {' +
2160
+ '}' +
2161
+ 'try {' +
2162
+ '} catch (abcdefghijklmnopqrstuvwxyz) {' +
2163
+ '} finally {' +
2164
+ '}'
2165
+ },
2166
+ // 13 Function Definition.
2167
+ {
2168
+ sTitle:
2169
+ 'Preservation of the identifier of a function declaration.',
2170
+ sInput:
2171
+ 'function abcdefghijklmnopqrstuvwxyz() {' +
2172
+ '}' +
2173
+ 'void [abcdefghijklmnopqrstuvwxyz];'
2174
+ },
2175
+ {
2176
+ sTitle:
2177
+ 'Preservation of the identifier of a function expression.',
2178
+ sInput:
2179
+ 'void [' +
2180
+ ' function abcdefghijklmnopqrstuvwxyz() {' +
2181
+ ' },' +
2182
+ ' function abcdefghijklmnopqrstuvwxyz() {' +
2183
+ ' }' +
2184
+ '];'
2185
+ },
2186
+ {
2187
+ sTitle:
2188
+ 'Preservation of a formal parameter of a function declaration.',
2189
+ sInput:
2190
+ 'function foo(abcdefghijklmnopqrstuvwxyz) {' +
2191
+ '}' +
2192
+ 'function bar(abcdefghijklmnopqrstuvwxyz) {' +
2193
+ '}'
2194
+ },
2195
+ {
2196
+ sTitle:
2197
+ 'Preservation of a formal parameter in a function expression.',
2198
+ sInput:
2199
+ 'void [' +
2200
+ ' function(abcdefghijklmnopqrstuvwxyz) {' +
2201
+ ' },' +
2202
+ ' function(abcdefghijklmnopqrstuvwxyz) {' +
2203
+ ' }' +
2204
+ '];'
2205
+ },
2206
+ {
2207
+ sTitle:
2208
+ 'Exclusion of a function declaration.',
2209
+ sInput:
2210
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
2211
+ 'function foo() {' +
2212
+ '}' +
2213
+ 'void ["abcdefghijklmnopqrstuvwxyz"];'
2214
+ },
2215
+ {
2216
+ sTitle:
2217
+ 'Consolidation within a function declaration.',
2218
+ sInput:
2219
+ 'function foo() {' +
2220
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2221
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2222
+ '}',
2223
+ sOutput:
2224
+ 'function foo() {' +
2225
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2226
+ ' void [a, a];' +
2227
+ '}'
2228
+ },
2229
+ // 14 Program.
2230
+ {
2231
+ sTitle:
2232
+ 'Preservation of a program without source elements.',
2233
+ sInput:
2234
+ ''
2235
+ },
2236
+ // 14.1 Directive Prologues and the Use Strict Directive.
2237
+ {
2238
+ sTitle:
2239
+ 'Preservation of a Directive Prologue in global code.',
2240
+ sInput:
2241
+ '"abcdefghijklmnopqrstuvwxyz";' +
2242
+ '\'zyxwvutsrqponmlkjihgfedcba\';'
2243
+ },
2244
+ {
2245
+ sTitle:
2246
+ 'Preservation of a Directive Prologue in a function declaration.',
2247
+ sInput:
2248
+ 'function foo() {' +
2249
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2250
+ ' \'zyxwvutsrqponmlkjihgfedcba\';' +
2251
+ '}'
2252
+ },
2253
+ {
2254
+ sTitle:
2255
+ 'Preservation of a Directive Prologue in a function expression.',
2256
+ sInput:
2257
+ 'void [' +
2258
+ ' function() {' +
2259
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2260
+ ' \'zyxwvutsrqponmlkjihgfedcba\';' +
2261
+ ' }' +
2262
+ '];'
2263
+ },
2264
+ {
2265
+ sTitle:
2266
+ 'Ignorance with regard to a Directive Prologue in global code.',
2267
+ sInput:
2268
+ '"abcdefghijklmnopqrstuvwxyz";' +
2269
+ 'void ["abcdefghijklmnopqrstuvwxyz",' +
2270
+ ' "abcdefghijklmnopqrstuvwxyz"];',
2271
+ sOutput:
2272
+ '"abcdefghijklmnopqrstuvwxyz";' +
2273
+ '(function() {' +
2274
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2275
+ ' void [a, a];' +
2276
+ '}());'
2277
+ },
2278
+ {
2279
+ sTitle:
2280
+ 'Ignorance with regard to a Directive Prologue in a function' +
2281
+ 'declaration.',
2282
+ sInput:
2283
+ 'function foo() {' +
2284
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2285
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2286
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2287
+ '}',
2288
+ sOutput:
2289
+ 'function foo() {' +
2290
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2291
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2292
+ ' void [a, a];' +
2293
+ '}'
2294
+ },
2295
+ {
2296
+ sTitle:
2297
+ 'Ignorance with regard to a Directive Prologue in a function' +
2298
+ 'expression.',
2299
+ sInput:
2300
+ '(function() {' +
2301
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2302
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2303
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2304
+ '}());',
2305
+ sOutput:
2306
+ '(function() {' +
2307
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2308
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2309
+ ' void [a, a];' +
2310
+ '}());'
2311
+ },
2312
+ // 15.1 The Global Object.
2313
+ {
2314
+ sTitle:
2315
+ 'Preservation of a property of the global object.',
2316
+ sInput:
2317
+ 'void [undefined, undefined, undefined, undefined, undefined];'
2318
+ },
2319
+ // 15.1.2.1.1 Direct Call to Eval.
2320
+ {
2321
+ sTitle:
2322
+ 'Exclusion of a direct call to the eval function in the same ' +
2323
+ 'syntactic code unit.',
2324
+ sInput:
2325
+ '/*jshint evil:true */' +
2326
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
2327
+ 'eval("");' +
2328
+ 'void ["abcdefghijklmnopqrstuvwxyz"];'
2329
+ },
2330
+ {
2331
+ sTitle:
2332
+ 'Exclusion of a direct call to the eval function in nested ' +
2333
+ 'function code.',
2334
+ sInput:
2335
+ '/*jshint evil:true */' +
2336
+ 'void ["abcdefghijklmnopqrstuvwxyz"];' +
2337
+ '(function() {' +
2338
+ ' eval("");' +
2339
+ '}());' +
2340
+ 'void ["abcdefghijklmnopqrstuvwxyz"];'
2341
+ },
2342
+ {
2343
+ sTitle:
2344
+ 'Consolidation within a direct call to the eval function.',
2345
+ sInput:
2346
+ '/*jshint evil:true */' +
2347
+ 'eval(function() {' +
2348
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2349
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2350
+ '}());',
2351
+ sOutput:
2352
+ '/*jshint evil:true */' +
2353
+ 'eval(function() {' +
2354
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2355
+ ' void [a, a];' +
2356
+ '}());'
2357
+ },
2358
+ // Consolidation proper.
2359
+ {
2360
+ sTitle:
2361
+ 'No consolidation if it does not result in a reduction of the ' +
2362
+ 'number of source characters.',
2363
+ sInput:
2364
+ '(function() {' +
2365
+ ' var foo;' +
2366
+ ' void ["ab", "ab", "abc", "abc"];' +
2367
+ '}());'
2368
+ },
2369
+ {
2370
+ sTitle:
2371
+ 'Identification of a range of source elements at the beginning ' +
2372
+ 'of global code.',
2373
+ sInput:
2374
+ '/*jshint evil:true */' +
2375
+ '"abcdefghijklmnopqrstuvwxyz";' +
2376
+ 'void ["abcdefghijklmnopqrstuvwxyz",' +
2377
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2378
+ 'eval("");',
2379
+ sOutput:
2380
+ '/*jshint evil:true */' +
2381
+ '"abcdefghijklmnopqrstuvwxyz";' +
2382
+ '(function() {' +
2383
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2384
+ ' void [a, a];' +
2385
+ '}());' +
2386
+ 'eval("");'
2387
+ },
2388
+ {
2389
+ sTitle:
2390
+ 'Identification of a range of source elements in the middle of ' +
2391
+ 'global code.',
2392
+ sInput:
2393
+ '/*jshint evil:true */' +
2394
+ '"abcdefghijklmnopqrstuvwxyz";' +
2395
+ 'eval("");' +
2396
+ 'void ["abcdefghijklmnopqrstuvwxyz",' +
2397
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2398
+ 'eval("");',
2399
+ sOutput:
2400
+ '/*jshint evil:true */' +
2401
+ '"abcdefghijklmnopqrstuvwxyz";' +
2402
+ 'eval("");' +
2403
+ '(function() {' +
2404
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2405
+ ' void [a, a];' +
2406
+ '}());' +
2407
+ 'eval("");'
2408
+ },
2409
+ {
2410
+ sTitle:
2411
+ 'Identification of a range of source elements at the end of ' +
2412
+ 'global code.',
2413
+ sInput:
2414
+ '/*jshint evil:true */' +
2415
+ '"abcdefghijklmnopqrstuvwxyz";' +
2416
+ 'eval("");' +
2417
+ 'void ["abcdefghijklmnopqrstuvwxyz",' +
2418
+ ' "abcdefghijklmnopqrstuvwxyz"];',
2419
+ sOutput:
2420
+ '/*jshint evil:true */' +
2421
+ '"abcdefghijklmnopqrstuvwxyz";' +
2422
+ 'eval("");' +
2423
+ '(function() {' +
2424
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2425
+ ' void [a, a];' +
2426
+ '}());'
2427
+ },
2428
+ {
2429
+ sTitle:
2430
+ 'Identification of a range of source elements at the beginning ' +
2431
+ 'of function code.',
2432
+ sInput:
2433
+ '/*jshint evil:true */' +
2434
+ '(function() {' +
2435
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2436
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2437
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2438
+ ' eval("");' +
2439
+ '}());',
2440
+ sOutput:
2441
+ '/*jshint evil:true */' +
2442
+ '(function() {' +
2443
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2444
+ ' (function() {' +
2445
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2446
+ ' void [a, a];' +
2447
+ ' }());' +
2448
+ ' eval("");' +
2449
+ '}());'
2450
+ },
2451
+ {
2452
+ sTitle:
2453
+ 'Identification of a range of source elements in the middle of ' +
2454
+ 'function code.',
2455
+ sInput:
2456
+ '/*jshint evil:true */' +
2457
+ '(function() {' +
2458
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2459
+ ' eval("");' +
2460
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2461
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2462
+ ' eval("");' +
2463
+ '}());',
2464
+ sOutput:
2465
+ '/*jshint evil:true */' +
2466
+ '(function() {' +
2467
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2468
+ ' eval("");' +
2469
+ ' (function() {' +
2470
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2471
+ ' void [a, a];' +
2472
+ ' }());' +
2473
+ ' eval("");' +
2474
+ '}());'
2475
+ },
2476
+ {
2477
+ sTitle:
2478
+ 'Identification of a range of source elements at the end of ' +
2479
+ 'function code.',
2480
+ sInput:
2481
+ '/*jshint evil:true */' +
2482
+ '(function() {' +
2483
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2484
+ ' eval("");' +
2485
+ ' void ["abcdefghijklmnopqrstuvwxyz",' +
2486
+ ' "abcdefghijklmnopqrstuvwxyz"];' +
2487
+ '}());',
2488
+ sOutput:
2489
+ '/*jshint evil:true */' +
2490
+ '(function() {' +
2491
+ ' "abcdefghijklmnopqrstuvwxyz";' +
2492
+ ' eval("");' +
2493
+ ' (function() {' +
2494
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2495
+ ' void [a, a];' +
2496
+ ' }());' +
2497
+ '}());'
2498
+ },
2499
+ {
2500
+ sTitle:
2501
+ 'Evaluation with regard to String values of String literals and ' +
2502
+ 'String values derived from identifier names used as property' +
2503
+ 'accessors.',
2504
+ sInput:
2505
+ '(function() {' +
2506
+ ' var foo;' +
2507
+ ' void ["abcdefg", Math.abcdefg, "abcdef", Math.abcdef];' +
2508
+ '}());',
2509
+ sOutput:
2510
+ '(function() {' +
2511
+ ' var a = "abcdefg", foo;' +
2512
+ ' void [a, Math[a], "abcdef", Math.abcdef];' +
2513
+ '}());'
2514
+ },
2515
+ {
2516
+ sTitle:
2517
+ 'Evaluation with regard to the necessity of adding a variable ' +
2518
+ 'statement.',
2519
+ sInput:
2520
+ '/*jshint evil:true */' +
2521
+ '(function() {' +
2522
+ ' void ["abcdefgh", "abcdefgh"];' +
2523
+ '}());' +
2524
+ 'eval("");' +
2525
+ '(function() {' +
2526
+ ' void ["abcdefg", "abcdefg"];' +
2527
+ '}());' +
2528
+ 'eval("");' +
2529
+ '(function() {' +
2530
+ ' var foo;' +
2531
+ ' void ["abcd", "abcd"];' +
2532
+ '}());',
2533
+ sOutput:
2534
+ '/*jshint evil:true */' +
2535
+ '(function() {' +
2536
+ ' var a = "abcdefgh";' +
2537
+ ' void [a, a];' +
2538
+ '}());' +
2539
+ 'eval("");' +
2540
+ '(function() {' +
2541
+ ' void ["abcdefg", "abcdefg"];' +
2542
+ '}());' +
2543
+ 'eval("");' +
2544
+ '(function() {' +
2545
+ ' var a = "abcd", foo;' +
2546
+ ' void [a, a];' +
2547
+ '}());'
2548
+ },
2549
+ {
2550
+ sTitle:
2551
+ 'Evaluation with regard to the necessity of enclosing source ' +
2552
+ 'elements.',
2553
+ sInput:
2554
+ '/*jshint evil:true */' +
2555
+ 'void ["abcdefghijklmnopqrstuvwxy", "abcdefghijklmnopqrstuvwxy"];' +
2556
+ 'eval("");' +
2557
+ 'void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' +
2558
+ 'eval("");' +
2559
+ '(function() {' +
2560
+ ' void ["abcdefgh", "abcdefgh"];' +
2561
+ '}());' +
2562
+ '(function() {' +
2563
+ ' void ["abcdefghijklmnopqrstuvwxy",' +
2564
+ ' "abcdefghijklmnopqrstuvwxy"];' +
2565
+ ' eval("");' +
2566
+ ' void ["abcdefghijklmnopqrstuvwx",' +
2567
+ ' "abcdefghijklmnopqrstuvwx"];' +
2568
+ ' eval("");' +
2569
+ ' (function() {' +
2570
+ ' void ["abcdefgh", "abcdefgh"];' +
2571
+ ' }());' +
2572
+ '}());',
2573
+ sOutput:
2574
+ '/*jshint evil:true */' +
2575
+ '(function() {' +
2576
+ ' var a = "abcdefghijklmnopqrstuvwxy";' +
2577
+ ' void [a, a];' +
2578
+ '}());' +
2579
+ 'eval("");' +
2580
+ 'void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' +
2581
+ 'eval("");' +
2582
+ '(function() {' +
2583
+ ' var a = "abcdefgh";' +
2584
+ ' void [a, a];' +
2585
+ '}());' +
2586
+ '(function() {' +
2587
+ ' (function() {' +
2588
+ ' var a = "abcdefghijklmnopqrstuvwxy";' +
2589
+ ' void [a, a];' +
2590
+ ' }());' +
2591
+ ' eval("");' +
2592
+ ' void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' +
2593
+ ' eval("");' +
2594
+ ' (function() {' +
2595
+ ' var a = "abcdefgh";' +
2596
+ ' void [a, a];' +
2597
+ ' }());' +
2598
+ '}());'
2599
+ },
2600
+ {
2601
+ sTitle:
2602
+ 'Employment of a closure while consolidating in global code.',
2603
+ sInput:
2604
+ 'void ["abcdefghijklmnopqrstuvwxyz",' +
2605
+ ' "abcdefghijklmnopqrstuvwxyz"];',
2606
+ sOutput:
2607
+ '(function() {' +
2608
+ ' var a = "abcdefghijklmnopqrstuvwxyz";' +
2609
+ ' void [a, a];' +
2610
+ '}());'
2611
+ },
2612
+ {
2613
+ sTitle:
2614
+ 'Assignment of a shorter identifier to a value whose ' +
2615
+ 'consolidation results in a greater reduction of the number of ' +
2616
+ 'source characters.',
2617
+ sInput:
2618
+ '(function() {' +
2619
+ ' var b, c, d, e, f, g, h, i, j, k, l, m,' +
2620
+ ' n, o, p, q, r, s, t, u, v, w, x, y, z,' +
2621
+ ' A, B, C, D, E, F, G, H, I, J, K, L, M,' +
2622
+ ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
2623
+ ' $, _;' +
2624
+ ' void ["abcde", "abcde", "edcba", "edcba", "edcba"];' +
2625
+ '}());',
2626
+ sOutput:
2627
+ '(function() {' +
2628
+ ' var a = "edcba",' +
2629
+ ' b, c, d, e, f, g, h, i, j, k, l, m,' +
2630
+ ' n, o, p, q, r, s, t, u, v, w, x, y, z,' +
2631
+ ' A, B, C, D, E, F, G, H, I, J, K, L, M,' +
2632
+ ' N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
2633
+ ' $, _;' +
2634
+ ' void ["abcde", "abcde", a, a, a];' +
2635
+ '}());'
2636
+ }
2637
+ ].forEach(cAssert);
2638
+ }());
2639
+ }
2640
+
2641
+ /* Local Variables: */
2642
+ /* mode: js */
2643
+ /* coding: utf-8 */
2644
+ /* indent-tabs-mode: nil */
2645
+ /* tab-width: 2 */
2646
+ /* End: */
2647
+ /* vim: set ft=javascript fenc=utf-8 et ts=2 sts=2 sw=2: */
2648
+ /* :mode=javascript:noTabs=true:tabSize=2:indentSize=2:deepIndent=true: */
2649
+
2650
+ }, "parse-js": function(exports, require, module) {/***********************************************************************
52
2651
 
53
2652
  A JavaScript tokenizer / parser / beautifier / compressor.
54
2653
 
@@ -241,7 +2840,7 @@ var OPERATORS = array_to_hash([
241
2840
 
242
2841
  var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
243
2842
 
244
- var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
2843
+ var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{(,.;:"));
245
2844
 
246
2845
  var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
247
2846
 
@@ -536,10 +3135,10 @@ function tokenizer($TEXT) {
536
3135
  };
537
3136
 
538
3137
  function read_name() {
539
- var backslash = false, name = "", ch;
3138
+ var backslash = false, name = "", ch, escaped = false, hex;
540
3139
  while ((ch = peek()) != null) {
541
3140
  if (!backslash) {
542
- if (ch == "\\") backslash = true, next();
3141
+ if (ch == "\\") escaped = backslash = true, next();
543
3142
  else if (is_identifier_char(ch)) name += next();
544
3143
  else break;
545
3144
  }
@@ -551,6 +3150,10 @@ function tokenizer($TEXT) {
551
3150
  backslash = false;
552
3151
  }
553
3152
  }
3153
+ if (HOP(KEYWORDS, name) && escaped) {
3154
+ hex = name.charCodeAt(0).toString(16).toUpperCase();
3155
+ name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
3156
+ }
554
3157
  return name;
555
3158
  };
556
3159
 
@@ -1677,12 +4280,13 @@ function Scope(parent) {
1677
4280
  };
1678
4281
 
1679
4282
  var base54 = (function(){
1680
- var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
4283
+ var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
1681
4284
  return function(num) {
1682
- var ret = "";
4285
+ var ret = "", base = 54;
1683
4286
  do {
1684
- ret = DIGITS.charAt(num % 54) + ret;
1685
- num = Math.floor(num / 54);
4287
+ ret += DIGITS.charAt(num % base);
4288
+ num = Math.floor(num / base);
4289
+ base = 64;
1686
4290
  } while (num > 0);
1687
4291
  return ret;
1688
4292
  };
@@ -2778,7 +5382,6 @@ function make_string(str, ascii_only) {
2778
5382
  case "\f": return "\\f";
2779
5383
  case "\n": return "\\n";
2780
5384
  case "\r": return "\\r";
2781
- case "\t": return "\\t";
2782
5385
  case "\u2028": return "\\u2028";
2783
5386
  case "\u2029": return "\\u2029";
2784
5387
  case '"': ++dq; return '"';
@@ -3023,7 +5626,7 @@ function gen_code(ast, options) {
3023
5626
  if (expr[0] == "num") {
3024
5627
  if (!/\./.test(expr[1]))
3025
5628
  out += ".";
3026
- } else if (needs_parens(expr))
5629
+ } else if (expr[0] != "function" && needs_parens(expr))
3027
5630
  out = "(" + out + ")";
3028
5631
  while (i < arguments.length)
3029
5632
  out += "." + make_name(arguments[i++]);
@@ -3121,7 +5724,7 @@ function gen_code(ast, options) {
3121
5724
  if (p.length == 3) {
3122
5725
  // getter/setter. The name is in p[0], the arg.list in p[1][2], the
3123
5726
  // body in p[1][3] and type ("get" / "set") in p[2].
3124
- return indent(make_function(p[0], p[1][2], p[1][3], p[2]));
5727
+ return indent(make_function(p[0], p[1][2], p[1][3], p[2], true));
3125
5728
  }
3126
5729
  var key = p[0], val = parenthesize(p[1], "seq");
3127
5730
  if (options.quote_keys) {
@@ -3198,14 +5801,14 @@ function gen_code(ast, options) {
3198
5801
  return make(th);
3199
5802
  };
3200
5803
 
3201
- function make_function(name, args, body, keyword) {
5804
+ function make_function(name, args, body, keyword, no_parens) {
3202
5805
  var out = keyword || "function";
3203
5806
  if (name) {
3204
5807
  out += " " + make_name(name);
3205
5808
  }
3206
5809
  out += "(" + add_commas(MAP(args, make_name)) + ")";
3207
5810
  out = add_spaces([ out, make_block(body) ]);
3208
- return needs_parens(this) ? "(" + out + ")" : out;
5811
+ return (!no_parens && needs_parens(this)) ? "(" + out + ")" : out;
3209
5812
  };
3210
5813
 
3211
5814
  function must_has_semicolon(node) {
@@ -3448,6 +6051,10 @@ function ast_squeeze_more(ast) {
3448
6051
  }
3449
6052
  },
3450
6053
  "call": function(expr, args) {
6054
+ if (expr[0] == "dot" && expr[1][0] == "string" && args.length == 1
6055
+ && (args[0][1] > 0 && expr[2] == "substring" || expr[2] == "substr")) {
6056
+ return [ "call", [ "dot", expr[1], "slice"], args];
6057
+ }
3451
6058
  if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
3452
6059
  // foo.toString() ==> foo+""
3453
6060
  return [ "binary", "+", expr[1], [ "string", "" ]];