2012-08-22 18:28:59 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
A JavaScript tokenizer / parser / beautifier / compressor .
https : //github.com/mishoo/UglifyJS2
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ( C ) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
Author : Mihai Bazon
< mihai . bazon @ gmail . com >
http : //mihai.bazon.net/blog
Distributed under the BSD license :
2012-08-27 08:01:27 +00:00
Copyright 2012 ( c ) Mihai Bazon < mihai . bazon @ gmail . com >
2012-08-22 18:28:59 +00:00
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
* Redistributions of source code must retain the above
copyright notice , this list of conditions and the following
disclaimer .
* Redistributions in binary form must reproduce the above
copyright notice , this list of conditions and the following
disclaimer in the documentation and / or other materials
provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “ AS IS ” AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY ,
OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR
TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2012-10-02 09:45:31 +00:00
"use strict" ;
2012-08-22 12:21:58 +00:00
function Compressor ( options , false _by _default ) {
2012-09-26 09:16:16 +00:00
if ( ! ( this instanceof Compressor ) )
return new Compressor ( options , false _by _default ) ;
TreeTransformer . call ( this , this . before , this . after ) ;
this . options = defaults ( options , {
2017-03-31 08:41:04 +00:00
booleans : ! false _by _default ,
cascade : ! false _by _default ,
collapse _vars : ! false _by _default ,
comparisons : ! false _by _default ,
conditionals : ! false _by _default ,
2012-08-22 12:21:58 +00:00
dead _code : ! false _by _default ,
2017-03-31 08:41:04 +00:00
drop _console : false ,
2012-08-22 12:21:58 +00:00
drop _debugger : ! false _by _default ,
2012-09-03 12:47:15 +00:00
evaluate : ! false _by _default ,
2017-03-31 08:41:04 +00:00
expression : false ,
global _defs : { } ,
2012-09-05 10:43:34 +00:00
hoist _funs : ! false _by _default ,
2012-10-03 08:27:05 +00:00
hoist _vars : false ,
2017-04-15 15:50:50 +00:00
ie8 : false ,
2012-09-13 12:20:57 +00:00
if _return : ! false _by _default ,
2017-06-05 21:49:53 +00:00
inline : ! false _by _default ,
2012-09-14 12:36:38 +00:00
join _vars : ! false _by _default ,
2017-03-31 08:41:04 +00:00
keep _fargs : true ,
keep _fnames : false ,
2017-03-31 19:02:14 +00:00
keep _infinity : false ,
2017-03-31 08:41:04 +00:00
loops : ! false _by _default ,
negate _iife : ! false _by _default ,
passes : 1 ,
properties : ! false _by _default ,
2017-04-07 05:31:58 +00:00
pure _getters : ! false _by _default && "strict" ,
2013-10-04 10:17:25 +00:00
pure _funcs : null ,
2017-03-31 08:41:04 +00:00
reduce _vars : ! false _by _default ,
sequences : ! false _by _default ,
side _effects : ! false _by _default ,
2017-04-02 06:52:25 +00:00
switches : ! false _by _default ,
2017-03-31 08:41:04 +00:00
top _retain : null ,
toplevel : ! ! ( options && options [ "top_retain" ] ) ,
2017-07-05 11:20:33 +00:00
typeofs : ! false _by _default ,
2017-03-31 08:41:04 +00:00
unsafe : false ,
unsafe _comps : false ,
2017-05-30 19:38:00 +00:00
unsafe _Func : false ,
2017-03-31 08:41:04 +00:00
unsafe _math : false ,
unsafe _proto : false ,
2017-05-19 01:06:29 +00:00
unsafe _regexp : false ,
2017-03-31 08:41:04 +00:00
unused : ! false _by _default ,
2017-04-15 15:50:50 +00:00
warnings : false ,
2012-10-02 08:00:47 +00:00
} , true ) ;
2017-05-21 17:38:43 +00:00
var global _defs = this . options [ "global_defs" ] ;
if ( typeof global _defs == "object" ) for ( var key in global _defs ) {
if ( /^@/ . test ( key ) && HOP ( global _defs , key ) ) {
2017-06-27 02:31:19 +00:00
global _defs [ key . slice ( 1 ) ] = parse ( global _defs [ key ] , {
expression : true
} ) ;
2017-05-21 17:38:43 +00:00
}
}
2017-02-19 17:46:59 +00:00
var pure _funcs = this . options [ "pure_funcs" ] ;
if ( typeof pure _funcs == "function" ) {
this . pure _funcs = pure _funcs ;
} else {
this . pure _funcs = pure _funcs ? function ( node ) {
return pure _funcs . indexOf ( node . expression . print _to _string ( ) ) < 0 ;
} : return _true ;
}
2017-02-18 11:03:53 +00:00
var top _retain = this . options [ "top_retain" ] ;
if ( top _retain instanceof RegExp ) {
this . top _retain = function ( def ) {
return top _retain . test ( def . name ) ;
} ;
2017-03-15 17:02:59 +00:00
} else if ( typeof top _retain == "function" ) {
2017-02-18 11:03:53 +00:00
this . top _retain = top _retain ;
} else if ( top _retain ) {
2017-03-15 17:02:59 +00:00
if ( typeof top _retain == "string" ) {
2017-02-18 11:03:53 +00:00
top _retain = top _retain . split ( /,/ ) ;
}
this . top _retain = function ( def ) {
return top _retain . indexOf ( def . name ) >= 0 ;
} ;
}
2017-04-30 14:52:36 +00:00
var toplevel = this . options [ "toplevel" ] ;
2017-06-23 05:11:40 +00:00
this . toplevel = typeof toplevel == "string" ? {
funcs : /funcs/ . test ( toplevel ) ,
vars : /vars/ . test ( toplevel )
} : {
funcs : toplevel ,
vars : toplevel
} ;
2016-06-26 21:16:02 +00:00
var sequences = this . options [ "sequences" ] ;
2017-04-12 13:56:27 +00:00
this . sequences _limit = sequences == 1 ? 800 : sequences | 0 ;
2016-04-12 13:19:38 +00:00
this . warnings _produced = { } ;
2012-08-21 12:45:05 +00:00
} ;
2012-09-26 09:16:16 +00:00
Compressor . prototype = new TreeTransformer ;
2012-09-26 10:04:54 +00:00
merge ( Compressor . prototype , {
2012-09-26 09:16:16 +00:00
option : function ( key ) { return this . options [ key ] } ,
2017-06-23 05:11:40 +00:00
exposed : function ( def ) {
if ( def . global ) for ( var i = 0 , len = def . orig . length ; i < len ; i ++ )
2017-04-30 14:52:36 +00:00
if ( ! this . toplevel [ def . orig [ i ] instanceof AST _SymbolDefun ? "funcs" : "vars" ] )
2017-06-23 05:11:40 +00:00
return true ;
return false ;
2017-04-30 14:52:36 +00:00
} ,
2016-04-12 13:19:38 +00:00
compress : function ( node ) {
2017-03-03 10:13:07 +00:00
if ( this . option ( "expression" ) ) {
2017-04-16 17:36:50 +00:00
node . process _expression ( true ) ;
2017-03-03 10:13:07 +00:00
}
2016-04-12 13:19:38 +00:00
var passes = + this . options . passes || 1 ;
2017-07-12 18:18:59 +00:00
var last _count = 1 / 0 ;
for ( var pass = 0 ; pass < passes ; pass ++ ) {
2017-02-18 11:19:55 +00:00
if ( pass > 0 || this . option ( "reduce_vars" ) )
2017-02-25 16:40:33 +00:00
node . reset _opt _flags ( this , true ) ;
2016-04-12 13:19:38 +00:00
node = node . transform ( this ) ;
2017-07-12 18:18:59 +00:00
if ( passes > 1 ) {
var count = 0 ;
node . walk ( new TreeWalker ( function ( ) {
count ++ ;
} ) ) ;
this . info ( "pass " + pass + ": last_count: " + last _count + ", count: " + count ) ;
if ( count >= last _count ) break ;
last _count = count ;
}
2016-04-12 13:19:38 +00:00
}
2017-03-03 10:13:07 +00:00
if ( this . option ( "expression" ) ) {
2017-04-16 17:36:50 +00:00
node . process _expression ( false ) ;
2017-03-03 10:13:07 +00:00
}
2016-04-12 13:19:38 +00:00
return node ;
} ,
2017-03-22 22:49:49 +00:00
info : function ( ) {
if ( this . options . warnings == "verbose" ) {
AST _Node . warn . apply ( AST _Node , arguments ) ;
}
} ,
2016-04-12 13:19:38 +00:00
warn : function ( text , props ) {
if ( this . options . warnings ) {
// only emit unique warnings
var message = string _template ( text , props ) ;
if ( ! ( message in this . warnings _produced ) ) {
this . warnings _produced [ message ] = true ;
AST _Node . warn . apply ( AST _Node , arguments ) ;
}
}
} ,
clear _warnings : function ( ) {
this . warnings _produced = { } ;
2012-09-26 10:04:54 +00:00
} ,
before : function ( node , descend , in _list ) {
2012-09-26 13:43:14 +00:00
if ( node . _squeezed ) return node ;
2013-11-08 09:57:17 +00:00
var was _scope = false ;
2012-09-26 10:04:54 +00:00
if ( node instanceof AST _Scope ) {
node = node . hoist _declarations ( this ) ;
2013-11-08 09:57:17 +00:00
was _scope = true ;
2012-09-26 10:04:54 +00:00
}
2017-03-15 17:02:59 +00:00
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
2012-09-26 10:04:54 +00:00
descend ( node , this ) ;
2017-03-15 17:02:59 +00:00
// Existing code relies on how AST_Node.optimize() worked, and omitting the
// following replacement call would result in degraded efficiency of both
// output and performance.
2017-03-15 10:44:13 +00:00
descend ( node , this ) ;
var opt = node . optimize ( this ) ;
if ( was _scope && opt instanceof AST _Scope ) {
opt . drop _unused ( this ) ;
descend ( opt , this ) ;
2012-09-26 13:43:14 +00:00
}
2017-03-15 10:44:13 +00:00
if ( opt === node ) opt . _squeezed = true ;
return opt ;
2012-09-26 09:16:16 +00:00
}
} ) ;
2012-10-10 08:27:06 +00:00
( function ( ) {
2012-08-21 12:45:05 +00:00
2012-09-26 10:04:54 +00:00
function OPT ( node , optimizer ) {
node . DEFMETHOD ( "optimize" , function ( compressor ) {
2012-09-26 13:43:14 +00:00
var self = this ;
if ( self . _optimized ) return self ;
2015-10-05 23:51:09 +00:00
if ( compressor . has _directive ( "use asm" ) ) return self ;
2012-09-26 13:43:14 +00:00
var opt = optimizer ( self , compressor ) ;
opt . _optimized = true ;
2017-03-15 10:44:13 +00:00
return opt ;
2012-09-26 10:04:54 +00:00
} ) ;
} ;
OPT ( AST _Node , function ( self , compressor ) {
return self ;
2012-09-03 16:38:45 +00:00
} ) ;
2012-09-14 12:36:38 +00:00
AST _Node . DEFMETHOD ( "equivalent_to" , function ( node ) {
2017-03-29 14:08:26 +00:00
return this . TYPE == node . TYPE && this . print _to _string ( ) == node . print _to _string ( ) ;
2012-09-14 12:36:38 +00:00
} ) ;
2017-04-16 17:36:50 +00:00
AST _Scope . DEFMETHOD ( "process_expression" , function ( insert , compressor ) {
2017-03-03 10:13:07 +00:00
var self = this ;
var tt = new TreeTransformer ( function ( node ) {
if ( insert && node instanceof AST _SimpleStatement ) {
return make _node ( AST _Return , node , {
value : node . body
} ) ;
}
if ( ! insert && node instanceof AST _Return ) {
2017-03-29 15:27:35 +00:00
if ( compressor ) {
var value = node . value && node . value . drop _side _effect _free ( compressor , true ) ;
return value ? make _node ( AST _SimpleStatement , node , {
body : value
} ) : make _node ( AST _EmptyStatement , node ) ;
}
2017-03-03 10:13:07 +00:00
return make _node ( AST _SimpleStatement , node , {
2017-03-31 19:02:14 +00:00
body : node . value || make _node ( AST _UnaryPrefix , node , {
operator : "void" ,
expression : make _node ( AST _Number , node , {
value : 0
} )
} )
2017-03-03 10:13:07 +00:00
} ) ;
}
if ( node instanceof AST _Lambda && node !== self ) {
return node ;
}
if ( node instanceof AST _Block ) {
var index = node . body . length - 1 ;
if ( index >= 0 ) {
node . body [ index ] = node . body [ index ] . transform ( tt ) ;
}
}
if ( node instanceof AST _If ) {
node . body = node . body . transform ( tt ) ;
if ( node . alternative ) {
node . alternative = node . alternative . transform ( tt ) ;
}
}
if ( node instanceof AST _With ) {
node . body = node . body . transform ( tt ) ;
}
return node ;
} ) ;
2017-04-16 17:36:50 +00:00
self . transform ( tt ) ;
2017-03-03 10:13:07 +00:00
} ) ;
2017-04-16 17:36:50 +00:00
AST _Node . DEFMETHOD ( "reset_opt_flags" , function ( compressor , rescan ) {
2017-02-25 16:40:33 +00:00
var reduce _vars = rescan && compressor . option ( "reduce_vars" ) ;
2017-04-07 04:32:56 +00:00
var safe _ids = Object . create ( null ) ;
2017-03-02 23:12:24 +00:00
var suppressor = new TreeWalker ( function ( node ) {
2017-06-22 20:44:57 +00:00
if ( ! ( node instanceof AST _Symbol ) ) return ;
var d = node . definition ( ) ;
if ( ! d ) return ;
if ( node instanceof AST _SymbolRef ) d . references . push ( node ) ;
d . fixed = false ;
2017-03-02 23:12:24 +00:00
} ) ;
2017-04-16 17:36:50 +00:00
var tw = new TreeWalker ( function ( node , descend ) {
2017-04-08 08:46:25 +00:00
node . _squeezed = false ;
node . _optimized = false ;
2017-02-25 16:40:33 +00:00
if ( reduce _vars ) {
if ( node instanceof AST _Toplevel ) node . globals . each ( reset _def ) ;
if ( node instanceof AST _Scope ) node . variables . each ( reset _def ) ;
if ( node instanceof AST _SymbolRef ) {
var d = node . definition ( ) ;
d . references . push ( node ) ;
2017-04-18 05:38:42 +00:00
if ( d . fixed === undefined || ! safe _to _read ( d )
2017-04-16 17:36:50 +00:00
|| is _modified ( node , 0 , is _immutable ( node . fixed _value ( ) ) ) ) {
2017-03-01 16:20:53 +00:00
d . fixed = false ;
2017-05-06 15:18:55 +00:00
} else {
var parent = tw . parent ( ) ;
if ( parent instanceof AST _Assign && parent . operator == "=" && node === parent . right
|| parent instanceof AST _Call && node !== parent . expression
|| parent instanceof AST _Return && node === parent . value && node . scope !== d . scope
|| parent instanceof AST _VarDef && node === parent . value ) {
d . escaped = true ;
}
2017-03-01 16:20:53 +00:00
}
}
2017-04-07 04:32:56 +00:00
if ( node instanceof AST _SymbolCatch ) {
2017-03-05 05:13:44 +00:00
node . definition ( ) . fixed = false ;
}
2017-03-01 16:20:53 +00:00
if ( node instanceof AST _VarDef ) {
var d = node . name . definition ( ) ;
2017-04-18 05:38:42 +00:00
if ( d . fixed === undefined || safe _to _assign ( d , node . value ) ) {
2017-04-06 19:42:17 +00:00
if ( node . value ) {
d . fixed = function ( ) {
return node . value ;
} ;
2017-04-07 04:32:56 +00:00
mark ( d , false ) ;
2017-04-06 19:42:17 +00:00
descend ( ) ;
} else {
d . fixed = null ;
}
2017-04-07 04:32:56 +00:00
mark ( d , true ) ;
2017-04-06 19:42:17 +00:00
return true ;
2017-03-26 17:30:21 +00:00
} else if ( node . value ) {
2017-02-28 20:12:10 +00:00
d . fixed = false ;
2017-02-25 16:40:33 +00:00
}
2017-01-26 11:14:18 +00:00
}
2017-04-16 17:36:50 +00:00
if ( node instanceof AST _Assign
&& node . operator == "="
&& node . left instanceof AST _SymbolRef ) {
var d = node . left . definition ( ) ;
2017-04-18 05:38:42 +00:00
if ( safe _to _assign ( d , node . right ) ) {
d . references . push ( node . left ) ;
2017-04-16 17:36:50 +00:00
d . fixed = function ( ) {
return node . right ;
} ;
mark ( d , false ) ;
node . right . walk ( tw ) ;
mark ( d , true ) ;
return true ;
}
}
2017-03-07 07:37:52 +00:00
if ( node instanceof AST _Defun ) {
var d = node . name . definition ( ) ;
2017-06-23 05:11:40 +00:00
if ( compressor . exposed ( d ) || safe _to _read ( d ) ) {
2017-03-07 07:37:52 +00:00
d . fixed = false ;
} else {
d . fixed = node ;
2017-04-07 04:32:56 +00:00
mark ( d , true ) ;
2017-03-07 07:37:52 +00:00
}
var save _ids = safe _ids ;
2017-04-07 04:32:56 +00:00
safe _ids = Object . create ( null ) ;
2017-03-07 07:37:52 +00:00
descend ( ) ;
safe _ids = save _ids ;
return true ;
}
2017-04-18 05:38:42 +00:00
if ( node instanceof AST _Function ) {
push ( ) ;
var iife ;
if ( ! node . name
&& ( iife = tw . parent ( ) ) instanceof AST _Call
&& iife . expression === node ) {
2017-04-16 17:36:50 +00:00
// Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
node . argnames . forEach ( function ( arg , i ) {
var d = arg . definition ( ) ;
2017-05-12 04:34:55 +00:00
if ( ! node . uses _arguments && d . fixed === undefined ) {
d . fixed = function ( ) {
return iife . args [ i ] || make _node ( AST _Undefined , iife ) ;
} ;
mark ( d , true ) ;
} else {
d . fixed = false ;
}
2017-04-16 17:36:50 +00:00
} ) ;
}
2017-04-18 05:38:42 +00:00
descend ( ) ;
pop ( ) ;
return true ;
2017-02-18 11:19:55 +00:00
}
2017-05-13 18:10:34 +00:00
if ( node instanceof AST _Accessor ) {
var save _ids = safe _ids ;
safe _ids = Object . create ( null ) ;
descend ( ) ;
safe _ids = save _ids ;
return true ;
}
2017-04-17 09:24:29 +00:00
if ( node instanceof AST _Binary
&& ( node . operator == "&&" || node . operator == "||" ) ) {
node . left . walk ( tw ) ;
push ( ) ;
node . right . walk ( tw ) ;
pop ( ) ;
return true ;
}
2017-04-17 17:44:23 +00:00
if ( node instanceof AST _Conditional ) {
node . condition . walk ( tw ) ;
push ( ) ;
node . consequent . walk ( tw ) ;
pop ( ) ;
push ( ) ;
node . alternative . walk ( tw ) ;
pop ( ) ;
return true ;
}
2017-04-16 17:36:50 +00:00
if ( node instanceof AST _If ) {
2017-03-01 16:20:53 +00:00
node . condition . walk ( tw ) ;
push ( ) ;
node . body . walk ( tw ) ;
pop ( ) ;
if ( node . alternative ) {
push ( ) ;
node . alternative . walk ( tw ) ;
pop ( ) ;
}
return true ;
}
2017-04-16 17:36:50 +00:00
if ( node instanceof AST _DWLoop ) {
push ( ) ;
node . condition . walk ( tw ) ;
node . body . walk ( tw ) ;
pop ( ) ;
return true ;
}
2017-03-01 16:20:53 +00:00
if ( node instanceof AST _LabeledStatement ) {
push ( ) ;
node . body . walk ( tw ) ;
pop ( ) ;
return true ;
}
if ( node instanceof AST _For ) {
if ( node . init ) node . init . walk ( tw ) ;
2017-04-18 05:38:42 +00:00
if ( node . condition ) {
push ( ) ;
node . condition . walk ( tw ) ;
pop ( ) ;
}
2017-03-01 16:20:53 +00:00
push ( ) ;
node . body . walk ( tw ) ;
pop ( ) ;
2017-04-18 05:38:42 +00:00
if ( node . step ) {
push ( ) ;
node . step . walk ( tw ) ;
pop ( ) ;
}
2017-03-01 16:20:53 +00:00
return true ;
}
if ( node instanceof AST _ForIn ) {
2017-03-02 23:12:24 +00:00
node . init . walk ( suppressor ) ;
2017-03-01 16:20:53 +00:00
node . object . walk ( tw ) ;
push ( ) ;
node . body . walk ( tw ) ;
pop ( ) ;
return true ;
}
2017-04-17 06:03:29 +00:00
if ( node instanceof AST _Try ) {
push ( ) ;
walk _body ( node , tw ) ;
pop ( ) ;
if ( node . bcatch ) {
push ( ) ;
node . bcatch . walk ( tw ) ;
pop ( ) ;
}
if ( node . bfinally ) node . bfinally . walk ( tw ) ;
return true ;
}
if ( node instanceof AST _SwitchBranch ) {
2017-03-05 05:13:44 +00:00
push ( ) ;
descend ( ) ;
pop ( ) ;
return true ;
}
2016-04-12 13:19:38 +00:00
}
2017-02-18 11:19:55 +00:00
} ) ;
this . walk ( tw ) ;
2017-04-07 04:32:56 +00:00
function mark ( def , safe ) {
safe _ids [ def . id ] = safe ;
2017-03-01 16:20:53 +00:00
}
2017-04-18 05:38:42 +00:00
function safe _to _read ( def ) {
2017-04-07 04:32:56 +00:00
if ( safe _ids [ def . id ] ) {
if ( def . fixed == null ) {
var orig = def . orig [ 0 ] ;
if ( orig instanceof AST _SymbolFunarg || orig . name == "arguments" ) return false ;
def . fixed = make _node ( AST _Undefined , orig ) ;
2017-03-26 17:30:21 +00:00
}
2017-04-07 04:32:56 +00:00
return true ;
2017-03-01 16:20:53 +00:00
}
}
2017-04-18 05:38:42 +00:00
function safe _to _assign ( def , value ) {
if ( ! HOP ( safe _ids , def . id ) ) return false ;
if ( ! safe _to _read ( def ) ) return false ;
if ( def . fixed === false ) return false ;
if ( def . fixed != null && ( ! value || def . references . length > 0 ) ) return false ;
return ! def . orig . some ( function ( sym ) {
2017-05-12 06:57:41 +00:00
return sym instanceof AST _SymbolDefun
2017-04-18 05:38:42 +00:00
|| sym instanceof AST _SymbolLambda ;
} ) ;
}
2017-03-01 16:20:53 +00:00
function push ( ) {
2017-04-07 04:32:56 +00:00
safe _ids = Object . create ( safe _ids ) ;
2017-03-01 16:20:53 +00:00
}
function pop ( ) {
2017-04-07 04:32:56 +00:00
safe _ids = Object . getPrototypeOf ( safe _ids ) ;
2017-03-01 16:20:53 +00:00
}
2017-02-25 16:40:33 +00:00
function reset _def ( def ) {
2017-05-06 15:18:55 +00:00
def . escaped = false ;
2017-05-12 04:34:55 +00:00
if ( def . scope . uses _eval ) {
def . fixed = false ;
2017-06-23 05:11:40 +00:00
} else if ( ! compressor . exposed ( def ) ) {
2017-03-06 19:11:03 +00:00
def . fixed = undefined ;
} else {
def . fixed = false ;
}
2017-02-25 16:40:33 +00:00
def . references = [ ] ;
def . should _replace = undefined ;
}
2017-04-16 17:36:50 +00:00
function is _immutable ( value ) {
return value && value . is _constant ( ) || value instanceof AST _Lambda ;
}
function is _modified ( node , level , immutable ) {
2017-02-18 11:19:55 +00:00
var parent = tw . parent ( level ) ;
2017-04-08 19:18:14 +00:00
if ( is _lhs ( node , parent )
2017-04-16 17:36:50 +00:00
|| ! immutable && parent instanceof AST _Call && parent . expression === node ) {
2017-02-18 11:19:55 +00:00
return true ;
} else if ( parent instanceof AST _PropAccess && parent . expression === node ) {
2017-04-16 17:36:50 +00:00
return ! immutable && is _modified ( parent , level + 1 ) ;
2017-02-18 11:19:55 +00:00
}
}
2016-04-12 13:19:38 +00:00
} ) ;
2017-04-05 13:06:42 +00:00
AST _SymbolRef . DEFMETHOD ( "fixed_value" , function ( ) {
var fixed = this . definition ( ) . fixed ;
if ( ! fixed || fixed instanceof AST _Node ) return fixed ;
return fixed ( ) ;
} ) ;
2017-06-24 13:30:06 +00:00
AST _SymbolRef . DEFMETHOD ( "is_immutable" , function ( ) {
var orig = this . definition ( ) . orig ;
return orig . length == 1 && orig [ 0 ] instanceof AST _SymbolLambda ;
} ) ;
2017-05-06 08:15:43 +00:00
function is _lhs _read _only ( lhs ) {
2017-06-24 13:30:06 +00:00
if ( lhs instanceof AST _SymbolRef ) return lhs . definition ( ) . orig [ 0 ] instanceof AST _SymbolLambda ;
if ( lhs instanceof AST _PropAccess ) {
lhs = lhs . expression ;
if ( lhs instanceof AST _SymbolRef ) {
if ( lhs . is _immutable ( ) ) return false ;
lhs = lhs . fixed _value ( ) ;
}
if ( ! lhs ) return true ;
if ( lhs instanceof AST _RegExp ) return false ;
if ( lhs instanceof AST _Constant ) return true ;
return is _lhs _read _only ( lhs ) ;
}
return false ;
2017-05-06 08:15:43 +00:00
}
2017-04-02 08:14:09 +00:00
function find _variable ( compressor , name ) {
var scope , i = 0 ;
while ( scope = compressor . parent ( i ++ ) ) {
if ( scope instanceof AST _Scope ) break ;
if ( scope instanceof AST _Catch ) {
scope = scope . argname . definition ( ) . scope ;
break ;
}
}
return scope . find _variable ( name ) ;
}
2012-08-22 12:21:58 +00:00
function make _node ( ctor , orig , props ) {
2012-09-03 12:47:15 +00:00
if ( ! props ) props = { } ;
2012-09-26 13:43:14 +00:00
if ( orig ) {
if ( ! props . start ) props . start = orig . start ;
if ( ! props . end ) props . end = orig . end ;
}
2012-08-22 12:21:58 +00:00
return new ctor ( props ) ;
} ;
2017-04-12 13:56:27 +00:00
function make _sequence ( orig , expressions ) {
if ( expressions . length == 1 ) return expressions [ 0 ] ;
return make _node ( AST _Sequence , orig , {
expressions : expressions
} ) ;
}
2017-03-15 17:02:59 +00:00
function make _node _from _constant ( val , orig ) {
2012-10-02 10:20:07 +00:00
switch ( typeof val ) {
case "string" :
return make _node ( AST _String , orig , {
value : val
2017-02-18 11:22:24 +00:00
} ) ;
2012-10-02 10:20:07 +00:00
case "number" :
2017-03-29 10:31:55 +00:00
if ( isNaN ( val ) ) return make _node ( AST _NaN , orig ) ;
if ( isFinite ( val ) ) {
return 1 / val < 0 ? make _node ( AST _UnaryPrefix , orig , {
2016-05-18 16:49:55 +00:00
operator : "-" ,
2016-06-23 16:54:38 +00:00
expression : make _node ( AST _Number , orig , { value : - val } )
2017-03-29 10:31:55 +00:00
} ) : make _node ( AST _Number , orig , { value : val } ) ;
2016-05-18 16:49:55 +00:00
}
2017-03-29 10:31:55 +00:00
return val < 0 ? make _node ( AST _UnaryPrefix , orig , {
operator : "-" ,
expression : make _node ( AST _Infinity , orig )
} ) : make _node ( AST _Infinity , orig ) ;
2012-10-02 10:20:07 +00:00
case "boolean" :
2017-03-15 17:02:59 +00:00
return make _node ( val ? AST _True : AST _False , orig ) ;
2012-10-02 10:20:07 +00:00
case "undefined" :
2017-03-15 17:02:59 +00:00
return make _node ( AST _Undefined , orig ) ;
2012-10-02 10:20:07 +00:00
default :
if ( val === null ) {
2017-02-18 11:22:24 +00:00
return make _node ( AST _Null , orig , { value : null } ) ;
2012-10-02 10:20:07 +00:00
}
2012-10-17 18:57:08 +00:00
if ( val instanceof RegExp ) {
2017-02-18 11:22:24 +00:00
return make _node ( AST _RegExp , orig , { value : val } ) ;
2012-10-17 18:57:08 +00:00
}
2012-10-02 10:20:07 +00:00
throw new Error ( string _template ( "Can't handle constant of type: {type}" , {
type : typeof val
} ) ) ;
}
} ;
2016-02-16 10:15:59 +00:00
// we shouldn't compress (1,func)(something) to
// func(something) because that changes the meaning of
// the func (becomes lexical instead of global).
2016-02-16 18:52:28 +00:00
function maintain _this _binding ( parent , orig , val ) {
2017-04-08 06:25:28 +00:00
if ( parent instanceof AST _UnaryPrefix && parent . operator == "delete"
|| parent instanceof AST _Call && parent . expression === orig
&& ( val instanceof AST _PropAccess || val instanceof AST _SymbolRef && val . name == "eval" ) ) {
2017-04-12 13:56:27 +00:00
return make _sequence ( orig , [ make _node ( AST _Number , orig , { value : 0 } ) , val ] ) ;
2016-02-16 10:15:59 +00:00
}
return val ;
}
2017-04-12 13:56:27 +00:00
function merge _sequence ( array , node ) {
if ( node instanceof AST _Sequence ) {
array . push . apply ( array , node . expressions ) ;
} else {
array . push ( node ) ;
}
}
2012-09-16 12:46:20 +00:00
function as _statement _array ( thing ) {
if ( thing === null ) return [ ] ;
if ( thing instanceof AST _BlockStatement ) return thing . body ;
if ( thing instanceof AST _EmptyStatement ) return [ ] ;
2012-10-03 12:41:11 +00:00
if ( thing instanceof AST _Statement ) return [ thing ] ;
2012-09-16 12:46:20 +00:00
throw new Error ( "Can't convert thing to statement array" ) ;
} ;
function is _empty ( thing ) {
if ( thing === null ) return true ;
if ( thing instanceof AST _EmptyStatement ) return true ;
if ( thing instanceof AST _BlockStatement ) return thing . body . length == 0 ;
return false ;
} ;
2012-10-18 12:14:57 +00:00
function loop _body ( x ) {
if ( x instanceof AST _Switch ) return x ;
if ( x instanceof AST _For || x instanceof AST _ForIn || x instanceof AST _DWLoop ) {
return ( x . body instanceof AST _BlockStatement ? x . body : x ) ;
}
return x ;
} ;
2017-02-19 17:46:59 +00:00
function is _iife _call ( node ) {
if ( node instanceof AST _Call && ! ( node instanceof AST _New ) ) {
return node . expression instanceof AST _Function || is _iife _call ( node . expression ) ;
}
return false ;
}
2017-07-15 07:16:11 +00:00
function is _undeclared _ref ( node ) {
return node instanceof AST _SymbolRef && node . definition ( ) . undeclared ;
}
2017-09-06 17:16:21 +00:00
var global _names = makePredicate ( "Array Boolean console Date Error EvalError Function JSON Math Number parseInt RangeError ReferenceError RegExp Object String SyntaxError TypeError URIError" ) ;
2017-07-15 07:16:11 +00:00
AST _SymbolRef . DEFMETHOD ( "is_declared" , function ( compressor ) {
return ! this . definition ( ) . undeclared
|| compressor . option ( "unsafe" ) && global _names ( this . name ) ;
} ) ;
2012-08-22 12:21:58 +00:00
function tighten _body ( statements , compressor ) {
2015-11-16 10:06:12 +00:00
var CHANGED , max _iter = 10 ;
2012-09-13 12:20:57 +00:00
do {
CHANGED = false ;
2017-06-16 11:19:54 +00:00
eliminate _spurious _blocks ( statements ) ;
2012-09-13 12:20:57 +00:00
if ( compressor . option ( "dead_code" ) ) {
2017-06-16 11:19:54 +00:00
eliminate _dead _code ( statements , compressor ) ;
2012-09-13 12:20:57 +00:00
}
if ( compressor . option ( "if_return" ) ) {
2017-06-16 11:19:54 +00:00
handle _if _return ( statements , compressor ) ;
2012-09-13 12:20:57 +00:00
}
2016-06-26 21:16:02 +00:00
if ( compressor . sequences _limit > 0 ) {
2017-06-16 11:19:54 +00:00
sequencesize ( statements , compressor ) ;
2012-09-17 08:16:44 +00:00
}
2012-09-14 12:36:38 +00:00
if ( compressor . option ( "join_vars" ) ) {
2017-06-16 11:19:54 +00:00
join _consecutive _vars ( statements , compressor ) ;
2012-09-14 12:36:38 +00:00
}
2016-01-27 07:17:06 +00:00
if ( compressor . option ( "collapse_vars" ) ) {
2017-06-16 11:19:54 +00:00
collapse ( statements , compressor ) ;
2016-01-27 07:17:06 +00:00
}
2015-11-16 10:06:12 +00:00
} while ( CHANGED && max _iter -- > 0 ) ;
2013-08-20 14:45:52 +00:00
2017-05-06 08:15:43 +00:00
// Search from right to left for assignment-like expressions:
// - `var a = x;`
// - `a = x;`
// - `++a`
// For each candidate, scan from left to right for first usage, then try
// to fold assignment into the site for compression.
// Will not attempt to collapse assignments into or past code blocks
// which are not sequentially executed, e.g. loops and conditionals.
function collapse ( statements , compressor ) {
2017-04-18 13:45:34 +00:00
var scope = compressor . find _parent ( AST _Scope ) ;
2017-05-06 08:15:43 +00:00
if ( scope . uses _eval || scope . uses _with ) return statements ;
var candidates = [ ] ;
var stat _index = statements . length ;
while ( -- stat _index >= 0 ) {
2017-07-01 17:05:14 +00:00
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if ( stat _index == 0 && compressor . option ( "unused" ) ) extract _args ( ) ;
// Find collapsible assignments
2017-05-06 08:15:43 +00:00
extract _candidates ( statements [ stat _index ] ) ;
while ( candidates . length > 0 ) {
var candidate = candidates . pop ( ) ;
var lhs = get _lhs ( candidate ) ;
if ( ! lhs || is _lhs _read _only ( lhs ) ) continue ;
2017-07-01 17:05:14 +00:00
// Locate symbols which may execute code outside of scanning range
2017-05-06 08:15:43 +00:00
var lvalues = get _lvalues ( candidate ) ;
if ( lhs instanceof AST _SymbolRef ) lvalues [ lhs . name ] = false ;
var side _effects = value _has _side _effects ( candidate ) ;
2017-07-01 17:05:14 +00:00
var hit = candidate . name instanceof AST _SymbolFunarg ;
var abort = false , replaced = false ;
2017-05-06 08:15:43 +00:00
var tt = new TreeTransformer ( function ( node , descend ) {
if ( abort ) return node ;
// Skip nodes before `candidate` as quickly as possible
if ( ! hit ) {
if ( node === candidate ) {
hit = true ;
return node ;
}
return ;
}
// Stop immediately if these node types are encountered
var parent = tt . parent ( ) ;
if ( node instanceof AST _Assign && node . operator != "=" && lhs . equivalent _to ( node . left )
2017-06-16 13:18:43 +00:00
|| node instanceof AST _Call && lhs instanceof AST _PropAccess && lhs . equivalent _to ( node . expression )
2017-05-06 08:15:43 +00:00
|| node instanceof AST _Debugger
|| node instanceof AST _IterationStatement && ! ( node instanceof AST _For )
2017-07-15 07:16:11 +00:00
|| node instanceof AST _SymbolRef && ! node . is _declared ( compressor )
2017-05-06 08:15:43 +00:00
|| node instanceof AST _Try
|| node instanceof AST _With
|| parent instanceof AST _For && node !== parent . init ) {
abort = true ;
return node ;
}
// Replace variable with assignment when found
if ( ! ( node instanceof AST _SymbolDeclaration )
&& ! is _lhs ( node , parent )
&& lhs . equivalent _to ( node ) ) {
CHANGED = replaced = abort = true ;
compressor . info ( "Collapsing {name} [{file}:{line},{col}]" , {
name : node . print _to _string ( ) ,
file : node . start . file ,
line : node . start . line ,
col : node . start . col
} ) ;
if ( candidate instanceof AST _UnaryPostfix ) {
return make _node ( AST _UnaryPrefix , candidate , candidate ) ;
}
if ( candidate instanceof AST _VarDef ) {
var def = candidate . name . definition ( ) ;
2017-06-23 05:11:40 +00:00
if ( def . references . length == 1 && ! compressor . exposed ( def ) ) {
2017-05-06 08:15:43 +00:00
return maintain _this _binding ( parent , node , candidate . value ) ;
}
return make _node ( AST _Assign , candidate , {
operator : "=" ,
left : make _node ( AST _SymbolRef , candidate . name , candidate . name ) ,
right : candidate . value
} ) ;
}
2017-07-13 16:39:34 +00:00
candidate . write _only = false ;
2017-05-06 08:15:43 +00:00
return candidate ;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym ;
if ( node instanceof AST _Call
|| node instanceof AST _Exit
|| node instanceof AST _PropAccess
|| node instanceof AST _SymbolRef
&& ( lvalues [ node . name ]
|| side _effects && ! references _in _scope ( node . definition ( ) ) )
|| ( sym = lhs _or _def ( node ) ) && get _symbol ( sym ) . name in lvalues
|| parent instanceof AST _Binary
&& ( parent . operator == "&&" || parent . operator == "||" )
|| parent instanceof AST _Case
|| parent instanceof AST _Conditional
|| parent instanceof AST _For
|| parent instanceof AST _If ) {
if ( ! ( node instanceof AST _Scope ) ) descend ( node , tt ) ;
abort = true ;
return node ;
}
// Skip (non-executed) functions and (leading) default case in switch statements
if ( node instanceof AST _Default || node instanceof AST _Scope ) return node ;
} ) ;
for ( var i = stat _index ; ! abort && i < statements . length ; i ++ ) {
statements [ i ] . transform ( tt ) ;
2016-01-27 07:17:06 +00:00
}
2017-05-06 08:15:43 +00:00
if ( replaced && ! remove _candidate ( candidate ) ) statements . splice ( stat _index , 1 ) ;
2017-04-18 13:45:34 +00:00
}
}
2016-01-27 07:17:06 +00:00
2017-07-01 17:05:14 +00:00
function extract _args ( ) {
var iife , fn = compressor . self ( ) ;
if ( fn instanceof AST _Function
&& ! fn . name
&& ! fn . uses _arguments
&& ! fn . uses _eval
&& ( iife = compressor . parent ( ) ) instanceof AST _Call
&& iife . expression === fn ) {
2017-07-07 20:42:35 +00:00
var names = Object . create ( null ) ;
for ( var i = fn . argnames . length ; -- i >= 0 ; ) {
var sym = fn . argnames [ i ] ;
if ( sym . name in names ) continue ;
names [ sym . name ] = true ;
2017-07-01 17:05:14 +00:00
var arg = iife . args [ i ] ;
2017-09-03 18:32:33 +00:00
if ( ! arg ) arg = make _node ( AST _Undefined , sym ) . transform ( compressor ) ;
2017-07-05 17:03:52 +00:00
else {
var tw = new TreeWalker ( function ( node ) {
if ( ! arg ) return true ;
if ( node instanceof AST _SymbolRef && fn . variables . has ( node . name ) ) {
var s = node . definition ( ) . scope ;
if ( s !== scope ) while ( s = s . parent _scope ) {
if ( s === scope ) return true ;
}
arg = null ;
2017-07-01 17:05:14 +00:00
}
2017-07-05 17:03:52 +00:00
if ( node instanceof AST _This && ! tw . find _parent ( AST _Scope ) ) {
arg = null ;
return true ;
}
} ) ;
arg . walk ( tw ) ;
}
2017-07-07 20:42:35 +00:00
if ( arg ) candidates . unshift ( make _node ( AST _VarDef , sym , {
2017-07-01 17:05:14 +00:00
name : sym ,
value : arg
} ) ) ;
2017-07-07 20:42:35 +00:00
}
2017-07-01 17:05:14 +00:00
}
}
2017-05-06 08:15:43 +00:00
function extract _candidates ( expr ) {
if ( expr instanceof AST _Assign && ! expr . left . has _side _effects ( compressor )
|| expr instanceof AST _Unary && ( expr . operator == "++" || expr . operator == "--" ) ) {
candidates . push ( expr ) ;
} else if ( expr instanceof AST _Sequence ) {
expr . expressions . forEach ( extract _candidates ) ;
} else if ( expr instanceof AST _Definitions ) {
expr . definitions . forEach ( function ( var _def ) {
if ( var _def . value ) candidates . push ( var _def ) ;
} ) ;
} else if ( expr instanceof AST _SimpleStatement ) {
extract _candidates ( expr . body ) ;
} else if ( expr instanceof AST _For && expr . init ) {
extract _candidates ( expr . init ) ;
}
}
2017-04-18 13:45:34 +00:00
2017-05-06 08:15:43 +00:00
function get _lhs ( expr ) {
if ( expr instanceof AST _VarDef ) {
var def = expr . name . definition ( ) ;
2017-07-01 17:05:14 +00:00
if ( def . orig . length > 1 && ! ( expr . name instanceof AST _SymbolFunarg )
2017-06-23 05:11:40 +00:00
|| def . references . length == 1 && ! compressor . exposed ( def ) ) {
2017-05-06 08:15:43 +00:00
return make _node ( AST _SymbolRef , expr . name , expr . name ) ;
}
} else {
2017-05-12 06:57:41 +00:00
return expr [ expr instanceof AST _Assign ? "left" : "expression" ] ;
2017-05-06 08:15:43 +00:00
}
}
2017-04-18 13:45:34 +00:00
2017-05-06 08:15:43 +00:00
function get _symbol ( node ) {
while ( node instanceof AST _PropAccess ) node = node . expression ;
return node ;
}
2016-01-27 07:17:06 +00:00
2017-05-06 08:15:43 +00:00
function get _lvalues ( expr ) {
var lvalues = Object . create ( null ) ;
if ( expr instanceof AST _Unary ) return lvalues ;
var scope ;
var tw = new TreeWalker ( function ( node , descend ) {
if ( node instanceof AST _Scope ) {
var save _scope = scope ;
descend ( ) ;
scope = save _scope ;
return true ;
2017-04-18 13:45:34 +00:00
}
2017-05-06 08:15:43 +00:00
if ( node instanceof AST _SymbolRef || node instanceof AST _PropAccess ) {
var sym = get _symbol ( node ) ;
if ( sym instanceof AST _SymbolRef ) {
lvalues [ sym . name ] = lvalues [ sym . name ] || is _lhs ( node , tw . parent ( ) ) ;
2017-04-18 13:45:34 +00:00
}
2017-05-06 08:15:43 +00:00
}
} ) ;
expr [ expr instanceof AST _Assign ? "right" : "value" ] . walk ( tw ) ;
return lvalues ;
2016-01-27 07:17:06 +00:00
}
2017-05-06 08:15:43 +00:00
function lhs _or _def ( node ) {
if ( node instanceof AST _VarDef ) return node . value && node . name ;
return is _lhs ( node . left , node ) ;
2016-01-27 07:17:06 +00:00
}
2017-04-18 13:45:34 +00:00
2017-05-06 08:15:43 +00:00
function remove _candidate ( expr ) {
2017-07-01 20:28:11 +00:00
if ( expr . name instanceof AST _SymbolFunarg ) {
var index = compressor . self ( ) . argnames . indexOf ( expr . name ) ;
var args = compressor . parent ( ) . args ;
if ( args [ index ] ) args [ index ] = make _node ( AST _Number , args [ index ] , {
value : 0
} ) ;
return true ;
}
2017-05-06 08:15:43 +00:00
var found = false ;
return statements [ stat _index ] . transform ( new TreeTransformer ( function ( node , descend , in _list ) {
if ( found ) return node ;
if ( node === expr ) {
found = true ;
if ( node instanceof AST _VarDef ) {
remove ( node . name . definition ( ) . orig , node . name ) ;
}
return in _list ? MAP . skip : null ;
}
} , function ( node ) {
if ( node instanceof AST _Sequence ) switch ( node . expressions . length ) {
case 0 : return null ;
case 1 : return node . expressions [ 0 ] ;
}
if ( node instanceof AST _Definitions && node . definitions . length == 0
|| node instanceof AST _SimpleStatement && ! node . body ) {
return null ;
}
} ) ) ;
}
2016-01-27 07:17:06 +00:00
2017-05-06 08:15:43 +00:00
function value _has _side _effects ( expr ) {
if ( expr instanceof AST _Unary ) return false ;
return expr [ expr instanceof AST _Assign ? "right" : "value" ] . has _side _effects ( compressor ) ;
}
2016-01-27 07:17:06 +00:00
2017-05-06 08:15:43 +00:00
function references _in _scope ( def ) {
if ( def . orig . length == 1 && def . orig [ 0 ] instanceof AST _SymbolDefun ) return true ;
if ( def . scope !== scope ) return false ;
return def . references . every ( function ( ref ) {
return ref . scope === scope ;
2017-04-18 13:45:34 +00:00
} ) ;
2016-01-27 07:17:06 +00:00
}
}
2012-09-25 12:15:47 +00:00
function eliminate _spurious _blocks ( statements ) {
2012-10-08 09:53:17 +00:00
var seen _dirs = [ ] ;
2017-06-16 11:19:54 +00:00
for ( var i = 0 ; i < statements . length ; ) {
var stat = statements [ i ] ;
2012-09-25 12:15:47 +00:00
if ( stat instanceof AST _BlockStatement ) {
CHANGED = true ;
2017-06-16 11:19:54 +00:00
eliminate _spurious _blocks ( stat . body ) ;
[ ] . splice . apply ( statements , [ i , 1 ] . concat ( stat . body ) ) ;
i += stat . body . length ;
2012-09-25 12:15:47 +00:00
} else if ( stat instanceof AST _EmptyStatement ) {
CHANGED = true ;
2017-06-16 11:19:54 +00:00
statements . splice ( i , 1 ) ;
2012-10-08 09:53:17 +00:00
} else if ( stat instanceof AST _Directive ) {
if ( seen _dirs . indexOf ( stat . value ) < 0 ) {
2017-06-16 11:19:54 +00:00
i ++ ;
2012-10-08 09:53:17 +00:00
seen _dirs . push ( stat . value ) ;
} else {
CHANGED = true ;
2017-06-16 11:19:54 +00:00
statements . splice ( i , 1 ) ;
2012-10-08 09:53:17 +00:00
}
2017-06-16 11:19:54 +00:00
} else i ++ ;
}
}
2012-09-25 12:15:47 +00:00
2012-09-16 12:46:20 +00:00
function handle _if _return ( statements , compressor ) {
var self = compressor . self ( ) ;
2016-05-22 15:35:41 +00:00
var multiple _if _returns = has _multiple _if _returns ( statements ) ;
2012-09-16 12:46:20 +00:00
var in _lambda = self instanceof AST _Lambda ;
2017-06-16 11:19:54 +00:00
for ( var i = statements . length ; -- i >= 0 ; ) {
2012-09-16 12:46:20 +00:00
var stat = statements [ i ] ;
2017-06-16 11:19:54 +00:00
var next = statements [ i + 1 ] ;
if ( in _lambda && stat instanceof AST _Return && ! stat . value && ! next ) {
2012-09-16 12:46:20 +00:00
CHANGED = true ;
2017-06-16 11:19:54 +00:00
statements . length -- ;
continue ;
}
if ( stat instanceof AST _If ) {
2017-05-29 02:51:41 +00:00
var ab = aborts ( stat . body ) ;
if ( can _merge _flow ( ab ) ) {
if ( ab . label ) {
remove ( ab . label . thedef . references , ab ) ;
}
CHANGED = true ;
stat = stat . clone ( ) ;
stat . condition = stat . condition . negate ( compressor ) ;
2017-06-16 11:19:54 +00:00
var body = as _statement _array _with _return ( stat . body , ab ) ;
2017-05-29 02:51:41 +00:00
stat . body = make _node ( AST _BlockStatement , stat , {
2017-06-16 11:19:54 +00:00
body : as _statement _array ( stat . alternative ) . concat ( extract _functions ( ) )
2017-05-29 02:51:41 +00:00
} ) ;
stat . alternative = make _node ( AST _BlockStatement , stat , {
body : body
} ) ;
2017-06-16 11:19:54 +00:00
statements [ i ] = stat . transform ( compressor ) ;
continue ;
2017-05-29 02:51:41 +00:00
}
var ab = aborts ( stat . alternative ) ;
if ( can _merge _flow ( ab ) ) {
if ( ab . label ) {
remove ( ab . label . thedef . references , ab ) ;
}
CHANGED = true ;
stat = stat . clone ( ) ;
stat . body = make _node ( AST _BlockStatement , stat . body , {
2017-06-16 11:19:54 +00:00
body : as _statement _array ( stat . body ) . concat ( extract _functions ( ) )
2017-05-29 02:51:41 +00:00
} ) ;
var body = as _statement _array _with _return ( stat . alternative , ab ) ;
stat . alternative = make _node ( AST _BlockStatement , stat . alternative , {
body : body
} ) ;
2017-06-16 11:19:54 +00:00
statements [ i ] = stat . transform ( compressor ) ;
continue ;
2017-05-29 02:51:41 +00:00
}
2017-06-16 11:19:54 +00:00
}
2017-05-29 02:51:41 +00:00
2017-06-16 11:19:54 +00:00
if ( stat instanceof AST _If && stat . body instanceof AST _Return ) {
var value = stat . body . value ;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if ( ! value && ! stat . alternative
&& ( in _lambda && ! next || next instanceof AST _Return && ! next . value ) ) {
CHANGED = true ;
statements [ i ] = make _node ( AST _SimpleStatement , stat . condition , {
body : stat . condition
} ) ;
continue ;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if ( value && ! stat . alternative && next instanceof AST _Return && next . value ) {
CHANGED = true ;
stat = stat . clone ( ) ;
stat . alternative = next ;
statements . splice ( i , 2 , stat . transform ( compressor ) ) ;
continue ;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if ( multiple _if _returns && in _lambda && value && ! stat . alternative
&& ( ! next || next instanceof AST _Return ) ) {
CHANGED = true ;
stat = stat . clone ( ) ;
stat . alternative = next || make _node ( AST _Return , stat , {
value : null
} ) ;
statements . splice ( i , next ? 2 : 1 , stat . transform ( compressor ) ) ;
continue ;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements [ i - 1 ] ;
if ( compressor . option ( "sequences" ) && in _lambda && ! stat . alternative
&& prev instanceof AST _If && prev . body instanceof AST _Return
&& i + 2 == statements . length && next instanceof AST _SimpleStatement ) {
CHANGED = true ;
statements . push ( make _node ( AST _Return , next , {
value : null
} ) . transform ( compressor ) ) ;
continue ;
2012-09-16 12:46:20 +00:00
}
}
}
2016-05-22 15:35:41 +00:00
function has _multiple _if _returns ( statements ) {
var n = 0 ;
for ( var i = statements . length ; -- i >= 0 ; ) {
var stat = statements [ i ] ;
if ( stat instanceof AST _If && stat . body instanceof AST _Return ) {
if ( ++ n > 1 ) return true ;
}
}
return false ;
}
2017-05-29 02:51:41 +00:00
function is _return _void ( value ) {
return ! value || value instanceof AST _UnaryPrefix && value . operator == "void" ;
}
function can _merge _flow ( ab ) {
if ( ! ab ) return false ;
var lct = ab instanceof AST _LoopControl ? compressor . loopcontrol _target ( ab ) : null ;
return ab instanceof AST _Return && in _lambda && is _return _void ( ab . value )
|| ab instanceof AST _Continue && self === loop _body ( lct )
|| ab instanceof AST _Break && lct instanceof AST _BlockStatement && self === lct ;
}
2017-06-16 11:19:54 +00:00
function extract _functions ( ) {
var tail = statements . slice ( i + 1 ) ;
statements . length = i + 1 ;
return tail . filter ( function ( stat ) {
if ( stat instanceof AST _Defun ) {
statements . push ( stat ) ;
return false ;
}
return true ;
} ) ;
}
2017-05-29 02:51:41 +00:00
function as _statement _array _with _return ( node , ab ) {
var body = as _statement _array ( node ) . slice ( 0 , - 1 ) ;
if ( ab . value ) {
body . push ( make _node ( AST _SimpleStatement , ab . value , {
body : ab . value . expression
} ) ) ;
}
return body ;
}
2017-06-16 11:19:54 +00:00
}
2012-09-16 12:46:20 +00:00
2012-09-13 12:20:57 +00:00
function eliminate _dead _code ( statements , compressor ) {
2017-06-16 11:19:54 +00:00
var has _quit ;
2012-10-18 12:14:57 +00:00
var self = compressor . self ( ) ;
2017-06-16 11:19:54 +00:00
for ( var i = 0 , n = 0 , len = statements . length ; i < len ; i ++ ) {
var stat = statements [ i ] ;
if ( stat instanceof AST _LoopControl ) {
var lct = compressor . loopcontrol _target ( stat ) ;
if ( stat instanceof AST _Break
&& ! ( lct instanceof AST _IterationStatement )
&& loop _body ( lct ) === self
|| stat instanceof AST _Continue
&& loop _body ( lct ) === self ) {
if ( stat . label ) {
remove ( stat . label . thedef . references , stat ) ;
2012-10-18 12:14:57 +00:00
}
} else {
2017-06-16 11:19:54 +00:00
statements [ n ++ ] = stat ;
2012-10-18 12:14:57 +00:00
}
2017-06-16 11:19:54 +00:00
} else {
statements [ n ++ ] = stat ;
2012-09-13 12:20:57 +00:00
}
2017-06-16 11:19:54 +00:00
if ( aborts ( stat ) ) {
has _quit = statements . slice ( i + 1 ) ;
break ;
}
}
statements . length = n ;
CHANGED = n != len ;
if ( has _quit ) has _quit . forEach ( function ( stat ) {
extract _declarations _from _unreachable _code ( compressor , stat , statements ) ;
} ) ;
}
2012-09-13 12:20:57 +00:00
2012-09-14 12:36:38 +00:00
function sequencesize ( statements , compressor ) {
2017-06-16 11:19:54 +00:00
if ( statements . length < 2 ) return ;
var seq = [ ] , n = 0 ;
2012-09-14 12:36:38 +00:00
function push _seq ( ) {
2017-04-12 13:56:27 +00:00
if ( ! seq . length ) return ;
var body = make _sequence ( seq [ 0 ] , seq ) ;
2017-06-16 11:19:54 +00:00
statements [ n ++ ] = make _node ( AST _SimpleStatement , body , { body : body } ) ;
2012-09-14 12:36:38 +00:00
seq = [ ] ;
2017-06-16 11:19:54 +00:00
}
for ( var i = 0 , len = statements . length ; i < len ; i ++ ) {
var stat = statements [ i ] ;
2017-02-18 11:07:03 +00:00
if ( stat instanceof AST _SimpleStatement ) {
2017-04-12 13:56:27 +00:00
if ( seq . length >= compressor . sequences _limit ) push _seq ( ) ;
2017-03-26 20:37:42 +00:00
var body = stat . body ;
if ( seq . length > 0 ) body = body . drop _side _effect _free ( compressor ) ;
2017-04-12 13:56:27 +00:00
if ( body ) merge _sequence ( seq , body ) ;
2016-04-12 11:15:14 +00:00
} else {
push _seq ( ) ;
2017-06-16 11:19:54 +00:00
statements [ n ++ ] = stat ;
2016-04-12 11:15:14 +00:00
}
2017-06-16 11:19:54 +00:00
}
2012-09-14 12:36:38 +00:00
push _seq ( ) ;
2017-06-16 11:19:54 +00:00
statements . length = n ;
sequencesize _2 ( statements , compressor ) ;
CHANGED = statements . length != len ;
}
2012-09-14 12:36:38 +00:00
2012-09-16 13:29:17 +00:00
function sequencesize _2 ( statements , compressor ) {
2012-09-16 15:05:15 +00:00
function cons _seq ( right ) {
2017-06-16 11:19:54 +00:00
n -- ;
2012-09-16 15:05:15 +00:00
var left = prev . body ;
2017-04-12 13:56:27 +00:00
if ( ! ( left instanceof AST _Sequence ) ) {
left = make _node ( AST _Sequence , left , {
expressions : [ left ]
} ) ;
2012-09-16 15:05:15 +00:00
}
2017-04-12 13:56:27 +00:00
merge _sequence ( left . expressions , right ) ;
2012-09-26 13:43:14 +00:00
return left . transform ( compressor ) ;
2012-09-16 15:05:15 +00:00
} ;
2017-06-16 11:19:54 +00:00
var n = 0 , prev ;
for ( var i = 0 , len = statements . length ; i < len ; i ++ ) {
var stat = statements [ i ] ;
2012-09-16 13:29:17 +00:00
if ( prev ) {
2017-07-02 18:10:56 +00:00
if ( stat instanceof AST _For && ! ( stat . init instanceof AST _Definitions ) ) {
var abort = false ;
prev . body . walk ( new TreeWalker ( function ( node ) {
2017-07-02 20:17:37 +00:00
if ( abort || node instanceof AST _Scope ) return true ;
2017-07-02 18:10:56 +00:00
if ( node instanceof AST _Binary && node . operator == "in" ) {
abort = true ;
return true ;
2012-10-30 12:50:47 +00:00
}
2017-07-02 18:10:56 +00:00
} ) ) ;
if ( ! abort ) {
if ( stat . init ) stat . init = cons _seq ( stat . init ) ;
else {
2017-03-26 20:37:42 +00:00
stat . init = prev . body . drop _side _effect _free ( compressor ) ;
2017-06-16 11:19:54 +00:00
n -- ;
2012-10-30 12:50:47 +00:00
}
}
2012-09-16 13:29:17 +00:00
}
else if ( stat instanceof AST _If ) {
2012-09-16 15:05:15 +00:00
stat . condition = cons _seq ( stat . condition ) ;
2012-09-16 13:29:17 +00:00
}
else if ( stat instanceof AST _With ) {
2012-09-16 15:05:15 +00:00
stat . expression = cons _seq ( stat . expression ) ;
}
else if ( stat instanceof AST _Exit && stat . value ) {
stat . value = cons _seq ( stat . value ) ;
}
else if ( stat instanceof AST _Exit ) {
2017-04-02 06:52:25 +00:00
stat . value = cons _seq ( make _node ( AST _Undefined , stat ) . transform ( compressor ) ) ;
2012-09-16 15:05:15 +00:00
}
else if ( stat instanceof AST _Switch ) {
stat . expression = cons _seq ( stat . expression ) ;
2012-09-16 13:29:17 +00:00
}
}
2017-06-16 11:19:54 +00:00
statements [ n ++ ] = stat ;
2012-09-16 13:29:17 +00:00
prev = stat instanceof AST _SimpleStatement ? stat : null ;
2017-06-16 11:19:54 +00:00
}
statements . length = n ;
}
2012-09-16 13:29:17 +00:00
2012-09-14 12:36:38 +00:00
function join _consecutive _vars ( statements , compressor ) {
2017-06-16 11:19:54 +00:00
for ( var i = 0 , j = - 1 , len = statements . length ; i < len ; i ++ ) {
var stat = statements [ i ] ;
var prev = statements [ j ] ;
2012-09-14 12:36:38 +00:00
if ( stat instanceof AST _Definitions && prev && prev . TYPE == stat . TYPE ) {
prev . definitions = prev . definitions . concat ( stat . definitions ) ;
CHANGED = true ;
2012-09-15 07:54:59 +00:00
}
else if ( stat instanceof AST _For
2016-07-14 16:43:50 +00:00
&& prev instanceof AST _Var
2012-09-15 07:54:59 +00:00
&& ( ! stat . init || stat . init . TYPE == prev . TYPE ) ) {
CHANGED = true ;
if ( stat . init ) {
stat . init . definitions = prev . definitions . concat ( stat . init . definitions ) ;
} else {
stat . init = prev ;
}
2017-06-16 11:19:54 +00:00
statements [ j ] = stat ;
2012-09-15 07:54:59 +00:00
}
else {
2017-06-16 11:19:54 +00:00
statements [ ++ j ] = stat ;
2012-08-22 12:21:58 +00:00
}
2016-04-23 21:48:33 +00:00
}
2017-06-16 11:19:54 +00:00
statements . length = j + 1 ;
} ;
2016-04-23 21:48:33 +00:00
}
2012-09-13 12:20:57 +00:00
function extract _declarations _from _unreachable _code ( compressor , stat , target ) {
2016-04-10 19:41:38 +00:00
if ( ! ( stat instanceof AST _Defun ) ) {
compressor . warn ( "Dropping unreachable code [{file}:{line},{col}]" , stat . start ) ;
}
2012-09-13 12:20:57 +00:00
stat . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _Definitions ) {
2012-09-21 11:38:52 +00:00
compressor . warn ( "Declarations in unreachable code! [{file}:{line},{col}]" , node . start ) ;
2012-09-13 12:20:57 +00:00
node . remove _initializers ( ) ;
target . push ( node ) ;
return true ;
2012-08-22 12:21:58 +00:00
}
2017-05-26 08:08:51 +00:00
if ( node instanceof AST _Defun && ( node === stat || ! compressor . has _directive ( "use strict" ) ) ) {
2012-09-13 12:20:57 +00:00
target . push ( node ) ;
return true ;
2012-08-22 12:21:58 +00:00
}
2012-09-13 12:20:57 +00:00
if ( node instanceof AST _Scope ) {
return true ;
2012-08-22 12:21:58 +00:00
}
2012-09-13 12:20:57 +00:00
} ) ) ;
2012-09-04 12:36:14 +00:00
} ;
2012-08-22 12:21:58 +00:00
2017-03-31 19:02:14 +00:00
function is _undefined ( node , compressor ) {
return node . is _undefined
|| node instanceof AST _Undefined
|| node instanceof AST _UnaryPrefix
&& node . operator == "void"
&& ! node . expression . has _side _effects ( compressor ) ;
2017-03-05 05:09:27 +00:00
}
2017-05-13 18:10:34 +00:00
// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
2017-04-06 03:18:59 +00:00
( function ( def ) {
2017-05-13 18:10:34 +00:00
AST _Node . DEFMETHOD ( "may_throw_on_access" , function ( compressor ) {
2017-07-15 07:16:11 +00:00
return ! compressor . option ( "pure_getters" )
|| this . _dot _throw ( compressor ) ;
2017-04-07 09:06:01 +00:00
} ) ;
2017-07-15 07:16:11 +00:00
function is _strict ( compressor ) {
return /strict/ . test ( compressor . option ( "pure_getters" ) ) ;
2017-04-07 05:31:58 +00:00
}
2017-04-07 09:06:01 +00:00
def ( AST _Node , is _strict ) ;
2017-04-06 03:18:59 +00:00
def ( AST _Null , return _true ) ;
def ( AST _Undefined , return _true ) ;
2017-04-06 19:42:17 +00:00
def ( AST _Constant , return _false ) ;
def ( AST _Array , return _false ) ;
2017-07-15 07:16:11 +00:00
def ( AST _Object , function ( compressor ) {
if ( ! is _strict ( compressor ) ) return false ;
2017-05-13 18:10:34 +00:00
for ( var i = this . properties . length ; -- i >= 0 ; )
if ( this . properties [ i ] . value instanceof AST _Accessor ) return true ;
return false ;
} ) ;
2017-04-06 19:42:17 +00:00
def ( AST _Function , return _false ) ;
def ( AST _UnaryPostfix , return _false ) ;
2017-04-06 03:18:59 +00:00
def ( AST _UnaryPrefix , function ( ) {
return this . operator == "void" ;
} ) ;
2017-07-15 07:16:11 +00:00
def ( AST _Binary , function ( compressor ) {
2017-04-06 19:42:17 +00:00
switch ( this . operator ) {
case "&&" :
2017-07-15 07:16:11 +00:00
return this . left . _dot _throw ( compressor ) ;
2017-04-06 19:42:17 +00:00
case "||" :
2017-07-15 07:16:11 +00:00
return this . left . _dot _throw ( compressor )
&& this . right . _dot _throw ( compressor ) ;
2017-04-06 19:42:17 +00:00
default :
return false ;
}
} )
2017-07-15 07:16:11 +00:00
def ( AST _Assign , function ( compressor ) {
2017-04-06 19:42:17 +00:00
return this . operator == "="
2017-07-15 07:16:11 +00:00
&& this . right . _dot _throw ( compressor ) ;
2017-04-06 19:42:17 +00:00
} )
2017-07-15 07:16:11 +00:00
def ( AST _Conditional , function ( compressor ) {
return this . consequent . _dot _throw ( compressor )
|| this . alternative . _dot _throw ( compressor ) ;
2017-04-06 19:42:17 +00:00
} )
2017-07-15 07:16:11 +00:00
def ( AST _Sequence , function ( compressor ) {
return this . expressions [ this . expressions . length - 1 ] . _dot _throw ( compressor ) ;
2017-04-06 19:42:17 +00:00
} ) ;
2017-07-15 07:16:11 +00:00
def ( AST _SymbolRef , function ( compressor ) {
2017-04-06 03:18:59 +00:00
if ( this . is _undefined ) return true ;
2017-07-15 07:16:11 +00:00
if ( ! is _strict ( compressor ) ) return false ;
if ( is _undeclared _ref ( this ) && this . is _declared ( compressor ) ) return false ;
2017-06-24 13:30:06 +00:00
if ( this . is _immutable ( ) ) return false ;
2017-04-06 03:18:59 +00:00
var fixed = this . fixed _value ( ) ;
2017-07-15 07:16:11 +00:00
return ! fixed || fixed . _dot _throw ( compressor ) ;
2017-04-06 03:18:59 +00:00
} ) ;
} ) ( function ( node , func ) {
2017-07-15 07:16:11 +00:00
node . DEFMETHOD ( "_dot_throw" , func ) ;
2017-04-06 03:18:59 +00:00
} ) ;
2012-09-03 12:47:15 +00:00
/* -----[ boolean/negation helpers ]----- */
// methods to determine whether an expression has a boolean result type
2017-04-15 15:50:50 +00:00
( function ( def ) {
2012-09-03 12:47:15 +00:00
var unary _bool = [ "!" , "delete" ] ;
var binary _bool = [ "in" , "instanceof" , "==" , "!=" , "===" , "!==" , "<" , "<=" , ">=" , ">" ] ;
2016-10-26 15:34:30 +00:00
def ( AST _Node , return _false ) ;
2012-09-03 12:47:15 +00:00
def ( AST _UnaryPrefix , function ( ) {
return member ( this . operator , unary _bool ) ;
} ) ;
def ( AST _Binary , function ( ) {
return member ( this . operator , binary _bool ) ||
( ( this . operator == "&&" || this . operator == "||" ) &&
this . left . is _boolean ( ) && this . right . is _boolean ( ) ) ;
} ) ;
def ( AST _Conditional , function ( ) {
return this . consequent . is _boolean ( ) && this . alternative . is _boolean ( ) ;
} ) ;
def ( AST _Assign , function ( ) {
return this . operator == "=" && this . right . is _boolean ( ) ;
} ) ;
2017-04-12 13:56:27 +00:00
def ( AST _Sequence , function ( ) {
return this . expressions [ this . expressions . length - 1 ] . is _boolean ( ) ;
2012-09-03 12:47:15 +00:00
} ) ;
2016-10-26 15:34:30 +00:00
def ( AST _True , return _true ) ;
def ( AST _False , return _true ) ;
2012-09-03 12:47:15 +00:00
} ) ( function ( node , func ) {
node . DEFMETHOD ( "is_boolean" , func ) ;
} ) ;
2017-03-03 10:04:32 +00:00
// methods to determine if an expression has a numeric result type
2017-04-15 15:50:50 +00:00
( function ( def ) {
2017-03-03 10:04:32 +00:00
def ( AST _Node , return _false ) ;
def ( AST _Number , return _true ) ;
var unary = makePredicate ( "+ - ~ ++ --" ) ;
def ( AST _Unary , function ( ) {
return unary ( this . operator ) ;
} ) ;
var binary = makePredicate ( "- * / % & | ^ << >> >>>" ) ;
def ( AST _Binary , function ( compressor ) {
return binary ( this . operator ) || this . operator == "+"
&& this . left . is _number ( compressor )
&& this . right . is _number ( compressor ) ;
} ) ;
def ( AST _Assign , function ( compressor ) {
2017-03-28 09:08:16 +00:00
return binary ( this . operator . slice ( 0 , - 1 ) )
|| this . operator == "=" && this . right . is _number ( compressor ) ;
2017-03-03 10:04:32 +00:00
} ) ;
2017-04-12 13:56:27 +00:00
def ( AST _Sequence , function ( compressor ) {
return this . expressions [ this . expressions . length - 1 ] . is _number ( compressor ) ;
2017-03-03 10:04:32 +00:00
} ) ;
def ( AST _Conditional , function ( compressor ) {
return this . consequent . is _number ( compressor ) && this . alternative . is _number ( compressor ) ;
} ) ;
} ) ( function ( node , func ) {
node . DEFMETHOD ( "is_number" , func ) ;
} ) ;
2012-09-03 12:47:15 +00:00
// methods to determine if an expression has a string result type
2017-04-15 15:50:50 +00:00
( function ( def ) {
2016-10-26 15:34:30 +00:00
def ( AST _Node , return _false ) ;
def ( AST _String , return _true ) ;
2012-09-03 12:47:15 +00:00
def ( AST _UnaryPrefix , function ( ) {
return this . operator == "typeof" ;
} ) ;
2012-11-13 12:32:07 +00:00
def ( AST _Binary , function ( compressor ) {
2012-09-03 12:47:15 +00:00
return this . operator == "+" &&
2012-11-13 12:32:07 +00:00
( this . left . is _string ( compressor ) || this . right . is _string ( compressor ) ) ;
2012-09-03 12:47:15 +00:00
} ) ;
2012-11-13 12:32:07 +00:00
def ( AST _Assign , function ( compressor ) {
return ( this . operator == "=" || this . operator == "+=" ) && this . right . is _string ( compressor ) ;
} ) ;
2017-04-12 13:56:27 +00:00
def ( AST _Sequence , function ( compressor ) {
return this . expressions [ this . expressions . length - 1 ] . is _string ( compressor ) ;
2012-11-13 12:32:07 +00:00
} ) ;
def ( AST _Conditional , function ( compressor ) {
return this . consequent . is _string ( compressor ) && this . alternative . is _string ( compressor ) ;
} ) ;
2012-09-03 12:47:15 +00:00
} ) ( function ( node , func ) {
node . DEFMETHOD ( "is_string" , func ) ;
} ) ;
2017-03-26 11:14:30 +00:00
var unary _side _effects = makePredicate ( "delete ++ --" ) ;
2017-04-08 19:18:14 +00:00
function is _lhs ( node , parent ) {
if ( parent instanceof AST _Unary && unary _side _effects ( parent . operator ) ) return parent . expression ;
if ( parent instanceof AST _Assign && parent . left === node ) return node ;
2017-01-17 09:33:40 +00:00
}
2017-04-15 15:50:50 +00:00
( function ( def ) {
2017-02-18 11:27:31 +00:00
AST _Node . DEFMETHOD ( "resolve_defines" , function ( compressor ) {
if ( ! compressor . option ( "global_defs" ) ) return ;
var def = this . _find _defs ( compressor , "" ) ;
if ( def ) {
var node , parent = this , level = 0 ;
do {
node = parent ;
parent = compressor . parent ( level ++ ) ;
} while ( parent instanceof AST _PropAccess && parent . expression === node ) ;
2017-04-08 19:18:14 +00:00
if ( is _lhs ( node , parent ) ) {
2017-02-18 11:27:31 +00:00
compressor . warn ( 'global_defs ' + this . print _to _string ( ) + ' redefined [{file}:{line},{col}]' , this . start ) ;
} else {
return def ;
}
}
} ) ;
2017-03-15 17:02:59 +00:00
function to _node ( value , orig ) {
2017-02-18 11:27:31 +00:00
if ( value instanceof AST _Node ) return make _node ( value . CTOR , orig , value ) ;
if ( Array . isArray ( value ) ) return make _node ( AST _Array , orig , {
elements : value . map ( function ( value ) {
2017-03-15 17:02:59 +00:00
return to _node ( value , orig ) ;
2017-02-18 11:27:31 +00:00
} )
} ) ;
if ( value && typeof value == "object" ) {
var props = [ ] ;
2017-06-07 19:27:03 +00:00
for ( var key in value ) if ( HOP ( value , key ) ) {
2017-02-18 11:27:31 +00:00
props . push ( make _node ( AST _ObjectKeyVal , orig , {
key : key ,
2017-03-15 17:02:59 +00:00
value : to _node ( value [ key ] , orig )
2017-02-18 11:27:31 +00:00
} ) ) ;
}
return make _node ( AST _Object , orig , {
properties : props
} ) ;
}
2017-03-15 17:02:59 +00:00
return make _node _from _constant ( value , orig ) ;
2017-02-18 11:27:31 +00:00
}
def ( AST _Node , noop ) ;
def ( AST _Dot , function ( compressor , suffix ) {
2017-04-08 08:46:25 +00:00
return this . expression . _find _defs ( compressor , "." + this . property + suffix ) ;
2017-02-18 11:27:31 +00:00
} ) ;
def ( AST _SymbolRef , function ( compressor , suffix ) {
if ( ! this . global ( ) ) return ;
var name ;
var defines = compressor . option ( "global_defs" ) ;
if ( defines && HOP ( defines , ( name = this . name + suffix ) ) ) {
2017-03-15 17:02:59 +00:00
var node = to _node ( defines [ name ] , this ) ;
2017-02-18 11:27:31 +00:00
var top = compressor . find _parent ( AST _Toplevel ) ;
node . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _SymbolRef ) {
node . scope = top ;
node . thedef = top . def _global ( node ) ;
}
} ) ) ;
return node ;
}
} ) ;
} ) ( function ( node , func ) {
node . DEFMETHOD ( "_find_defs" , func ) ;
} ) ;
2017-03-15 17:02:59 +00:00
function best _of _expression ( ast1 , ast2 ) {
2012-09-10 12:54:17 +00:00
return ast1 . print _to _string ( ) . length >
ast2 . print _to _string ( ) . length
2012-09-03 16:38:45 +00:00
? ast2 : ast1 ;
2017-02-18 11:11:57 +00:00
}
function best _of _statement ( ast1 , ast2 ) {
2017-03-15 17:02:59 +00:00
return best _of _expression ( make _node ( AST _SimpleStatement , ast1 , {
2017-02-18 11:11:57 +00:00
body : ast1
} ) , make _node ( AST _SimpleStatement , ast2 , {
body : ast2
} ) ) . body ;
}
2012-09-03 12:47:15 +00:00
2017-03-15 17:02:59 +00:00
function best _of ( compressor , ast1 , ast2 ) {
return ( first _in _statement ( compressor ) ? best _of _statement : best _of _expression ) ( ast1 , ast2 ) ;
}
2012-09-03 12:47:15 +00:00
// methods to evaluate a constant expression
2017-04-15 15:50:50 +00:00
( function ( def ) {
2017-03-15 17:02:59 +00:00
// If the node has been successfully reduced to a constant,
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
2012-09-03 16:38:45 +00:00
AST _Node . DEFMETHOD ( "evaluate" , function ( compressor ) {
2017-03-15 17:02:59 +00:00
if ( ! compressor . option ( "evaluate" ) ) return this ;
2017-07-02 18:10:56 +00:00
var val = this . _eval ( compressor ) ;
return ! val || val instanceof RegExp || typeof val != "object" ? val : this ;
2012-09-03 12:47:15 +00:00
} ) ;
2017-03-31 19:02:14 +00:00
var unaryPrefix = makePredicate ( "! ~ - + void" ) ;
2017-01-26 11:16:50 +00:00
AST _Node . DEFMETHOD ( "is_constant" , function ( ) {
2015-11-24 18:27:50 +00:00
// Accomodate when compress option evaluate=false
2017-01-26 11:16:50 +00:00
// as well as the common constant expressions !0 and -1
if ( this instanceof AST _Constant ) {
return ! ( this instanceof AST _RegExp ) ;
} else {
return this instanceof AST _UnaryPrefix
&& this . expression instanceof AST _Constant
&& unaryPrefix ( this . operator ) ;
}
2015-11-24 18:27:50 +00:00
} ) ;
// Obtain the constant value of an expression already known to be constant.
2017-01-26 11:16:50 +00:00
// Result only valid iff this.is_constant() is true.
2015-11-24 18:27:50 +00:00
AST _Node . DEFMETHOD ( "constant_value" , function ( compressor ) {
// Accomodate when option evaluate=false.
2017-01-26 11:16:50 +00:00
if ( this instanceof AST _Constant && ! ( this instanceof AST _RegExp ) ) {
return this . value ;
}
// Accomodate the common constant expressions !0 and -1 when option evaluate=false.
2015-11-24 18:27:50 +00:00
if ( this instanceof AST _UnaryPrefix
2017-01-26 11:16:50 +00:00
&& this . expression instanceof AST _Constant ) switch ( this . operator ) {
case "!" :
2015-11-24 18:27:50 +00:00
return ! this . expression . value ;
2017-01-26 11:16:50 +00:00
case "~" :
return ~ this . expression . value ;
case "-" :
return - this . expression . value ;
case "+" :
return + this . expression . value ;
default :
throw new Error ( string _template ( "Cannot evaluate unary expression {value}" , {
value : this . print _to _string ( )
} ) ) ;
2015-11-24 18:27:50 +00:00
}
2017-01-26 11:16:50 +00:00
var result = this . evaluate ( compressor ) ;
2017-03-15 17:02:59 +00:00
if ( result !== this ) {
return result ;
2015-11-24 18:27:50 +00:00
}
2017-01-26 11:16:50 +00:00
throw new Error ( string _template ( "Cannot evaluate constant [{file}:{line},{col}]" , this . start ) ) ;
2015-11-24 18:27:50 +00:00
} ) ;
2012-09-03 16:43:46 +00:00
def ( AST _Statement , function ( ) {
2012-10-13 09:56:56 +00:00
throw new Error ( string _template ( "Cannot evaluate a statement [{file}:{line},{col}]" , this . start ) ) ;
} ) ;
2017-07-02 18:10:56 +00:00
def ( AST _Lambda , return _this ) ;
2013-09-19 08:58:50 +00:00
function ev ( node , compressor ) {
2013-09-20 04:24:25 +00:00
if ( ! compressor ) throw new Error ( "Compressor must be passed" ) ;
2013-09-19 08:58:50 +00:00
return node . _eval ( compressor ) ;
2012-09-03 12:47:15 +00:00
} ;
2017-07-02 18:10:56 +00:00
def ( AST _Node , return _this ) ;
2012-09-03 12:47:15 +00:00
def ( AST _Constant , function ( ) {
return this . getValue ( ) ;
} ) ;
2017-01-26 11:14:18 +00:00
def ( AST _Array , function ( compressor ) {
if ( compressor . option ( "unsafe" ) ) {
2017-07-02 18:10:56 +00:00
var elements = [ ] ;
for ( var i = 0 , len = this . elements . length ; i < len ; i ++ ) {
var element = this . elements [ i ] ;
var value = ev ( element , compressor ) ;
if ( element === value ) return this ;
elements . push ( value ) ;
}
return elements ;
2017-01-26 11:14:18 +00:00
}
2017-07-02 18:10:56 +00:00
return this ;
2017-01-26 11:14:18 +00:00
} ) ;
def ( AST _Object , function ( compressor ) {
if ( compressor . option ( "unsafe" ) ) {
var val = { } ;
for ( var i = 0 , len = this . properties . length ; i < len ; i ++ ) {
var prop = this . properties [ i ] ;
var key = prop . key ;
2017-02-28 18:03:47 +00:00
if ( key instanceof AST _Symbol ) {
key = key . name ;
} else if ( key instanceof AST _Node ) {
2017-01-26 11:14:18 +00:00
key = ev ( key , compressor ) ;
2017-07-02 18:10:56 +00:00
if ( key === prop . key ) return this ;
2017-01-26 11:14:18 +00:00
}
if ( typeof Object . prototype [ key ] === 'function' ) {
2017-07-02 18:10:56 +00:00
return this ;
2017-01-26 11:14:18 +00:00
}
val [ key ] = ev ( prop . value , compressor ) ;
2017-07-02 18:10:56 +00:00
if ( val [ key ] === prop . value ) return this ;
2017-01-26 11:14:18 +00:00
}
return val ;
}
2017-07-02 18:10:56 +00:00
return this ;
2017-01-26 11:14:18 +00:00
} ) ;
2013-09-19 08:58:50 +00:00
def ( AST _UnaryPrefix , function ( compressor ) {
2017-07-02 18:10:56 +00:00
// Function would be evaluated to an array and so typeof would
// incorrectly return 'object'. Hence making is a special case.
if ( this . operator == "typeof" && this . expression instanceof AST _Function ) {
return typeof function ( ) { } ;
}
var e = ev ( this . expression , compressor ) ;
if ( e === this . expression ) return this ;
2012-09-03 12:47:15 +00:00
switch ( this . operator ) {
2017-07-02 18:10:56 +00:00
case "!" : return ! e ;
2013-04-04 02:34:38 +00:00
case "typeof" :
// typeof <RegExp> returns "object" or "function" on different platforms
// so cannot evaluate reliably
2017-07-02 18:10:56 +00:00
if ( e instanceof RegExp ) return this ;
2013-04-04 02:34:38 +00:00
return typeof e ;
2017-07-02 18:10:56 +00:00
case "void" : return void e ;
case "~" : return ~ e ;
case "-" : return - e ;
case "+" : return + e ;
2012-09-03 12:47:15 +00:00
}
2017-07-02 18:10:56 +00:00
return this ;
2012-09-03 12:47:15 +00:00
} ) ;
2017-07-02 18:10:56 +00:00
def ( AST _Binary , function ( compressor ) {
var left = ev ( this . left , compressor ) ;
if ( left === this . left ) return this ;
var right = ev ( this . right , compressor ) ;
if ( right === this . right ) return this ;
var result ;
2012-09-03 12:47:15 +00:00
switch ( this . operator ) {
2017-07-02 18:10:56 +00:00
case "&&" : result = left && right ; break ;
case "||" : result = left || right ; break ;
case "|" : result = left | right ; break ;
case "&" : result = left & right ; break ;
case "^" : result = left ^ right ; break ;
case "+" : result = left + right ; break ;
case "*" : result = left * right ; break ;
case "/" : result = left / right ; break ;
case "%" : result = left % right ; break ;
case "-" : result = left - right ; break ;
case "<<" : result = left << right ; break ;
case ">>" : result = left >> right ; break ;
case ">>>" : result = left >>> right ; break ;
case "==" : result = left == right ; break ;
case "===" : result = left === right ; break ;
case "!=" : result = left != right ; break ;
case "!==" : result = left !== right ; break ;
case "<" : result = left < right ; break ;
case "<=" : result = left <= right ; break ;
case ">" : result = left > right ; break ;
case ">=" : result = left >= right ; break ;
2016-06-19 03:06:59 +00:00
default :
2017-07-02 18:10:56 +00:00
return this ;
2016-06-19 03:06:59 +00:00
}
2017-07-02 18:10:56 +00:00
if ( isNaN ( result ) && compressor . find _parent ( AST _With ) ) {
2016-06-19 03:06:59 +00:00
// leave original expression as is
2017-07-02 18:10:56 +00:00
return this ;
2016-06-19 03:06:59 +00:00
}
return result ;
2012-09-03 12:47:15 +00:00
} ) ;
2013-09-19 08:58:50 +00:00
def ( AST _Conditional , function ( compressor ) {
2017-07-02 18:10:56 +00:00
var condition = ev ( this . condition , compressor ) ;
if ( condition === this . condition ) return this ;
var node = condition ? this . consequent : this . alternative ;
var value = ev ( node , compressor ) ;
return value === node ? this : value ;
2012-09-03 12:47:15 +00:00
} ) ;
2013-09-19 08:58:50 +00:00
def ( AST _SymbolRef , function ( compressor ) {
2017-07-02 18:10:56 +00:00
if ( ! compressor . option ( "reduce_vars" ) ) return this ;
var fixed = this . fixed _value ( ) ;
2017-07-03 10:52:39 +00:00
if ( ! fixed ) return this ;
this . _eval = return _this ;
2017-07-02 18:10:56 +00:00
var value = ev ( fixed , compressor ) ;
if ( value === fixed ) {
delete this . _eval ;
return this ;
}
if ( ! HOP ( fixed , "_eval" ) ) fixed . _eval = function ( ) {
2017-05-06 15:18:55 +00:00
return value ;
2017-07-02 18:10:56 +00:00
} ;
if ( value && typeof value == "object" && this . definition ( ) . escaped ) {
delete this . _eval ;
return this ;
2016-04-12 18:30:44 +00:00
}
2017-07-02 18:10:56 +00:00
this . _eval = fixed . _eval ;
return value ;
2012-10-02 09:45:17 +00:00
} ) ;
2017-07-06 21:35:32 +00:00
var global _objs = {
Array : Array ,
Math : Math ,
Number : Number ,
String : String ,
} ;
function convert _to _predicate ( obj ) {
for ( var key in obj ) {
obj [ key ] = makePredicate ( obj [ key ] ) ;
}
}
var static _values = {
Math : [
"E" ,
"LN10" ,
"LN2" ,
"LOG2E" ,
"LOG10E" ,
"PI" ,
"SQRT1_2" ,
"SQRT2" ,
] ,
Number : [
"MAX_VALUE" ,
"MIN_VALUE" ,
"NaN" ,
"NEGATIVE_INFINITY" ,
"POSITIVE_INFINITY" ,
] ,
} ;
convert _to _predicate ( static _values ) ;
2017-01-26 11:14:18 +00:00
def ( AST _PropAccess , function ( compressor ) {
if ( compressor . option ( "unsafe" ) ) {
var key = this . property ;
if ( key instanceof AST _Node ) {
key = ev ( key , compressor ) ;
2017-07-02 18:10:56 +00:00
if ( key === this . property ) return this ;
2017-01-26 11:14:18 +00:00
}
2017-07-06 21:35:32 +00:00
var exp = this . expression ;
var val ;
2017-07-15 07:16:11 +00:00
if ( is _undeclared _ref ( exp ) ) {
2017-07-06 21:35:32 +00:00
if ( ! ( static _values [ exp . name ] || return _false ) ( key ) ) return this ;
val = global _objs [ exp . name ] ;
} else {
val = ev ( exp , compressor ) ;
if ( ! val || val === exp || ! HOP ( val , key ) ) return this ;
2017-01-26 11:14:18 +00:00
}
2017-07-06 21:35:32 +00:00
return val [ key ] ;
2014-06-30 22:51:42 +00:00
}
2017-07-02 18:10:56 +00:00
return this ;
2014-06-30 22:51:42 +00:00
} ) ;
2017-05-31 20:33:05 +00:00
var object _fns = [
2017-07-03 10:52:39 +00:00
"constructor" ,
"toString" ,
"valueOf" ,
2017-05-31 20:33:05 +00:00
] ;
var native _fns = {
2017-07-06 21:35:32 +00:00
Array : [
2017-07-03 10:52:39 +00:00
"indexOf" ,
"join" ,
"lastIndexOf" ,
"slice" ,
2017-07-06 21:35:32 +00:00
] . concat ( object _fns ) ,
Boolean : object _fns ,
Number : [
2017-07-03 10:52:39 +00:00
"toExponential" ,
"toFixed" ,
"toPrecision" ,
2017-07-06 21:35:32 +00:00
] . concat ( object _fns ) ,
RegExp : [
2017-07-03 10:52:39 +00:00
"test" ,
2017-07-06 21:35:32 +00:00
] . concat ( object _fns ) ,
String : [
2017-07-03 10:52:39 +00:00
"charAt" ,
"charCodeAt" ,
"concat" ,
"indexOf" ,
"italics" ,
"lastIndexOf" ,
"match" ,
"replace" ,
"search" ,
"slice" ,
"split" ,
"substr" ,
"substring" ,
"trim" ,
2017-07-06 21:35:32 +00:00
] . concat ( object _fns ) ,
2017-05-31 20:33:05 +00:00
} ;
2017-07-06 21:35:32 +00:00
convert _to _predicate ( native _fns ) ;
var static _fns = {
Array : [
"isArray" ,
] ,
Math : [
"abs" ,
"acos" ,
"asin" ,
"atan" ,
"ceil" ,
"cos" ,
"exp" ,
"floor" ,
"log" ,
"round" ,
"sin" ,
"sqrt" ,
"tan" ,
"atan2" ,
"pow" ,
"max" ,
"min"
] ,
Number : [
"isFinite" ,
"isNaN" ,
] ,
String : [
"fromCharCode" ,
] ,
} ;
convert _to _predicate ( static _fns ) ;
2017-05-31 16:56:28 +00:00
def ( AST _Call , function ( compressor ) {
var exp = this . expression ;
if ( compressor . option ( "unsafe" ) && exp instanceof AST _PropAccess ) {
var key = exp . property ;
if ( key instanceof AST _Node ) {
key = ev ( key , compressor ) ;
2017-07-02 18:10:56 +00:00
if ( key === exp . property ) return this ;
2017-05-31 16:56:28 +00:00
}
2017-07-06 21:35:32 +00:00
var val ;
var e = exp . expression ;
2017-07-15 07:16:11 +00:00
if ( is _undeclared _ref ( e ) ) {
2017-07-06 21:35:32 +00:00
if ( ! ( static _fns [ e . name ] || return _false ) ( key ) ) return this ;
val = global _objs [ e . name ] ;
} else {
val = ev ( e , compressor ) ;
if ( val === e || ! ( val && native _fns [ val . constructor . name ] || return _false ) ( key ) ) return this ;
}
var args = [ ] ;
for ( var i = 0 , len = this . args . length ; i < len ; i ++ ) {
var arg = this . args [ i ] ;
var value = ev ( arg , compressor ) ;
if ( arg === value ) return this ;
args . push ( value ) ;
2017-05-31 16:56:28 +00:00
}
2017-07-06 21:35:32 +00:00
return val [ key ] . apply ( val , args ) ;
2017-05-31 16:56:28 +00:00
}
2017-07-02 18:10:56 +00:00
return this ;
2017-05-31 16:56:28 +00:00
} ) ;
2017-07-02 18:10:56 +00:00
def ( AST _New , return _this ) ;
2012-09-03 12:47:15 +00:00
} ) ( function ( node , func ) {
node . DEFMETHOD ( "_eval" , func ) ;
} ) ;
// method to negate an expression
( function ( def ) {
function basic _negation ( exp ) {
return make _node ( AST _UnaryPrefix , exp , {
operator : "!" ,
expression : exp
} ) ;
2017-02-18 11:11:57 +00:00
}
function best ( orig , alt , first _in _statement ) {
var negated = basic _negation ( orig ) ;
if ( first _in _statement ) {
var stat = make _node ( AST _SimpleStatement , alt , {
body : alt
} ) ;
2017-03-15 17:02:59 +00:00
return best _of _expression ( negated , stat ) === stat ? alt : negated ;
2017-02-18 11:11:57 +00:00
}
2017-03-15 17:02:59 +00:00
return best _of _expression ( negated , alt ) ;
2017-02-18 11:11:57 +00:00
}
2012-09-03 12:47:15 +00:00
def ( AST _Node , function ( ) {
return basic _negation ( this ) ;
} ) ;
def ( AST _Statement , function ( ) {
2012-09-03 16:43:46 +00:00
throw new Error ( "Cannot negate a statement" ) ;
2012-09-03 12:47:15 +00:00
} ) ;
2012-09-17 08:16:44 +00:00
def ( AST _Function , function ( ) {
return basic _negation ( this ) ;
} ) ;
2012-09-03 12:47:15 +00:00
def ( AST _UnaryPrefix , function ( ) {
2012-09-03 16:38:45 +00:00
if ( this . operator == "!" )
return this . expression ;
2012-09-03 12:47:15 +00:00
return basic _negation ( this ) ;
} ) ;
2017-04-12 13:56:27 +00:00
def ( AST _Sequence , function ( compressor ) {
var expressions = this . expressions . slice ( ) ;
expressions . push ( expressions . pop ( ) . negate ( compressor ) ) ;
return make _sequence ( this , expressions ) ;
2012-09-03 12:47:15 +00:00
} ) ;
2017-02-18 11:11:57 +00:00
def ( AST _Conditional , function ( compressor , first _in _statement ) {
2012-09-03 12:47:15 +00:00
var self = this . clone ( ) ;
self . consequent = self . consequent . negate ( compressor ) ;
self . alternative = self . alternative . negate ( compressor ) ;
2017-02-18 11:11:57 +00:00
return best ( this , self , first _in _statement ) ;
2012-09-03 12:47:15 +00:00
} ) ;
2017-02-18 11:11:57 +00:00
def ( AST _Binary , function ( compressor , first _in _statement ) {
2012-09-03 12:47:15 +00:00
var self = this . clone ( ) , op = this . operator ;
2012-11-01 13:14:56 +00:00
if ( compressor . option ( "unsafe_comps" ) ) {
2012-09-26 10:04:54 +00:00
switch ( op ) {
case "<=" : self . operator = ">" ; return self ;
case "<" : self . operator = ">=" ; return self ;
case ">=" : self . operator = "<" ; return self ;
case ">" : self . operator = "<=" ; return self ;
}
2012-09-03 12:47:15 +00:00
}
switch ( op ) {
case "==" : self . operator = "!=" ; return self ;
case "!=" : self . operator = "==" ; return self ;
case "===" : self . operator = "!==" ; return self ;
case "!==" : self . operator = "===" ; return self ;
case "&&" :
self . operator = "||" ;
2017-02-18 11:11:57 +00:00
self . left = self . left . negate ( compressor , first _in _statement ) ;
2012-09-03 12:47:15 +00:00
self . right = self . right . negate ( compressor ) ;
2017-02-18 11:11:57 +00:00
return best ( this , self , first _in _statement ) ;
2012-09-03 12:47:15 +00:00
case "||" :
self . operator = "&&" ;
2017-02-18 11:11:57 +00:00
self . left = self . left . negate ( compressor , first _in _statement ) ;
2012-09-03 12:47:15 +00:00
self . right = self . right . negate ( compressor ) ;
2017-02-18 11:11:57 +00:00
return best ( this , self , first _in _statement ) ;
2012-09-03 12:47:15 +00:00
}
return basic _negation ( this ) ;
} ) ;
} ) ( function ( node , func ) {
2017-02-18 11:11:57 +00:00
node . DEFMETHOD ( "negate" , function ( compressor , first _in _statement ) {
return func . call ( this , compressor , first _in _statement ) ;
2012-09-12 13:10:03 +00:00
} ) ;
2012-09-03 12:47:15 +00:00
} ) ;
2017-02-21 06:24:18 +00:00
AST _Call . DEFMETHOD ( "has_pure_annotation" , function ( compressor ) {
if ( ! compressor . option ( "side_effects" ) ) return false ;
if ( this . pure !== undefined ) return this . pure ;
var pure = false ;
var comments , last _comment ;
if ( this . start
&& ( comments = this . start . comments _before )
&& comments . length
&& /[@#]__PURE__/ . test ( ( last _comment = comments [ comments . length - 1 ] ) . value ) ) {
2017-02-27 18:25:44 +00:00
pure = last _comment ;
2017-02-21 06:24:18 +00:00
}
return this . pure = pure ;
} ) ;
2012-09-11 10:15:55 +00:00
// determine if expression has side effects
( function ( def ) {
2016-10-26 15:34:30 +00:00
def ( AST _Node , return _true ) ;
2012-09-11 10:15:55 +00:00
2016-10-26 15:34:30 +00:00
def ( AST _EmptyStatement , return _false ) ;
def ( AST _Constant , return _false ) ;
def ( AST _This , return _false ) ;
2012-10-05 13:51:16 +00:00
2013-10-04 10:17:25 +00:00
def ( AST _Call , function ( compressor ) {
2017-02-21 06:24:18 +00:00
if ( ! this . has _pure _annotation ( compressor ) && compressor . pure _funcs ( this ) ) return true ;
2017-02-19 17:46:59 +00:00
for ( var i = this . args . length ; -- i >= 0 ; ) {
if ( this . args [ i ] . has _side _effects ( compressor ) )
return true ;
}
return false ;
2013-10-04 10:17:25 +00:00
} ) ;
2017-03-25 15:03:26 +00:00
function any ( list , compressor ) {
for ( var i = list . length ; -- i >= 0 ; )
if ( list [ i ] . has _side _effects ( compressor ) )
2012-09-21 08:23:44 +00:00
return true ;
return false ;
2017-03-25 15:03:26 +00:00
}
2012-09-11 10:15:55 +00:00
2017-03-25 15:03:26 +00:00
def ( AST _Block , function ( compressor ) {
return any ( this . body , compressor ) ;
} ) ;
2017-03-27 10:09:35 +00:00
def ( AST _Switch , function ( compressor ) {
return this . expression . has _side _effects ( compressor )
|| any ( this . body , compressor ) ;
} ) ;
2017-03-26 04:05:44 +00:00
def ( AST _Case , function ( compressor ) {
2017-03-27 10:09:35 +00:00
return this . expression . has _side _effects ( compressor )
|| any ( this . body , compressor ) ;
2017-03-26 04:05:44 +00:00
} ) ;
2017-03-25 15:03:26 +00:00
def ( AST _Try , function ( compressor ) {
return any ( this . body , compressor )
|| this . bcatch && this . bcatch . has _side _effects ( compressor )
|| this . bfinally && this . bfinally . has _side _effects ( compressor ) ;
} ) ;
def ( AST _If , function ( compressor ) {
return this . condition . has _side _effects ( compressor )
|| this . body && this . body . has _side _effects ( compressor )
|| this . alternative && this . alternative . has _side _effects ( compressor ) ;
} ) ;
def ( AST _LabeledStatement , function ( compressor ) {
return this . body . has _side _effects ( compressor ) ;
} ) ;
2013-10-02 16:33:45 +00:00
def ( AST _SimpleStatement , function ( compressor ) {
return this . body . has _side _effects ( compressor ) ;
2012-09-11 10:15:55 +00:00
} ) ;
2016-10-26 15:34:30 +00:00
def ( AST _Defun , return _true ) ;
def ( AST _Function , return _false ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Binary , function ( compressor ) {
return this . left . has _side _effects ( compressor )
|| this . right . has _side _effects ( compressor ) ;
2012-09-11 10:15:55 +00:00
} ) ;
2016-10-26 15:34:30 +00:00
def ( AST _Assign , return _true ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Conditional , function ( compressor ) {
return this . condition . has _side _effects ( compressor )
|| this . consequent . has _side _effects ( compressor )
|| this . alternative . has _side _effects ( compressor ) ;
2012-09-11 10:15:55 +00:00
} ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Unary , function ( compressor ) {
2017-03-26 11:14:30 +00:00
return unary _side _effects ( this . operator )
2013-10-02 16:33:45 +00:00
|| this . expression . has _side _effects ( compressor ) ;
2012-09-11 10:15:55 +00:00
} ) ;
2014-09-28 08:16:51 +00:00
def ( AST _SymbolRef , function ( compressor ) {
2017-07-15 07:16:11 +00:00
return ! this . is _declared ( compressor ) ;
2014-09-28 08:16:51 +00:00
} ) ;
2017-04-18 13:45:34 +00:00
def ( AST _SymbolDeclaration , return _false ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Object , function ( compressor ) {
2017-03-25 15:03:26 +00:00
return any ( this . properties , compressor ) ;
2012-09-14 12:36:38 +00:00
} ) ;
2013-10-02 16:33:45 +00:00
def ( AST _ObjectProperty , function ( compressor ) {
return this . value . has _side _effects ( compressor ) ;
2012-09-14 12:36:38 +00:00
} ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Array , function ( compressor ) {
2017-03-25 15:03:26 +00:00
return any ( this . elements , compressor ) ;
2012-09-14 12:36:38 +00:00
} ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Dot , function ( compressor ) {
2017-05-13 18:10:34 +00:00
return this . expression . may _throw _on _access ( compressor )
2017-04-06 03:18:59 +00:00
|| this . expression . has _side _effects ( compressor ) ;
2012-09-19 09:27:38 +00:00
} ) ;
2013-10-02 16:33:45 +00:00
def ( AST _Sub , function ( compressor ) {
2017-05-13 18:10:34 +00:00
return this . expression . may _throw _on _access ( compressor )
2017-04-06 03:18:59 +00:00
|| this . expression . has _side _effects ( compressor )
2013-10-02 16:33:45 +00:00
|| this . property . has _side _effects ( compressor ) ;
} ) ;
2017-04-12 13:56:27 +00:00
def ( AST _Sequence , function ( compressor ) {
return this . expressions . some ( function ( expression , index ) {
return expression . has _side _effects ( compressor ) ;
} ) ;
2012-09-21 08:23:44 +00:00
} ) ;
2012-09-11 10:15:55 +00:00
} ) ( function ( node , func ) {
node . DEFMETHOD ( "has_side_effects" , func ) ;
} ) ;
2017-05-01 16:10:11 +00:00
// determine if expression is constant
( function ( def ) {
function all ( list ) {
for ( var i = list . length ; -- i >= 0 ; )
if ( ! list [ i ] . is _constant _expression ( ) )
return false ;
return true ;
}
def ( AST _Node , return _false ) ;
def ( AST _Constant , return _true ) ;
def ( AST _Unary , function ( ) {
return this . expression . is _constant _expression ( ) ;
} ) ;
def ( AST _Binary , function ( ) {
return this . left . is _constant _expression ( ) && this . right . is _constant _expression ( ) ;
} ) ;
def ( AST _Array , function ( ) {
return all ( this . elements ) ;
} ) ;
def ( AST _Object , function ( ) {
return all ( this . properties ) ;
} ) ;
def ( AST _ObjectProperty , function ( ) {
return this . value . is _constant _expression ( ) ;
} ) ;
} ) ( function ( node , func ) {
node . DEFMETHOD ( "is_constant_expression" , func ) ;
} ) ;
2012-09-12 13:10:03 +00:00
// tell me if a statement aborts
2012-09-16 12:46:20 +00:00
function aborts ( thing ) {
return thing && thing . aborts ( ) ;
} ;
2012-09-12 13:10:03 +00:00
( function ( def ) {
2017-03-15 10:44:13 +00:00
def ( AST _Statement , return _null ) ;
def ( AST _Jump , return _this ) ;
2012-11-14 10:06:07 +00:00
function block _aborts ( ) {
2012-09-15 13:06:09 +00:00
var n = this . body . length ;
2012-09-16 12:46:20 +00:00
return n > 0 && aborts ( this . body [ n - 1 ] ) ;
2012-11-14 10:06:07 +00:00
} ;
def ( AST _BlockStatement , block _aborts ) ;
def ( AST _SwitchBranch , block _aborts ) ;
2012-10-17 13:17:14 +00:00
def ( AST _If , function ( ) {
2015-01-06 11:57:18 +00:00
return this . alternative && aborts ( this . body ) && aborts ( this . alternative ) && this ;
2012-10-17 13:17:14 +00:00
} ) ;
2012-09-12 13:10:03 +00:00
} ) ( function ( node , func ) {
node . DEFMETHOD ( "aborts" , func ) ;
} ) ;
2012-09-25 12:59:27 +00:00
/* -----[ optimizers ]----- */
2012-09-25 09:48:36 +00:00
2012-09-26 10:04:54 +00:00
OPT ( AST _Directive , function ( self , compressor ) {
2017-03-14 05:19:05 +00:00
if ( compressor . has _directive ( self . value ) !== self ) {
2012-09-26 10:04:54 +00:00
return make _node ( AST _EmptyStatement , self ) ;
2012-09-18 10:21:09 +00:00
}
2012-09-26 10:04:54 +00:00
return self ;
2012-09-18 10:21:09 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Debugger , function ( self , compressor ) {
2012-09-03 12:47:15 +00:00
if ( compressor . option ( "drop_debugger" ) )
2012-09-26 10:04:54 +00:00
return make _node ( AST _EmptyStatement , self ) ;
return self ;
2012-09-03 12:47:15 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _LabeledStatement , function ( self , compressor ) {
2012-10-18 12:14:57 +00:00
if ( self . body instanceof AST _Break
2017-04-04 15:48:22 +00:00
&& compressor . loopcontrol _target ( self . body ) === self . body ) {
2012-10-18 12:14:57 +00:00
return make _node ( AST _EmptyStatement , self ) ;
}
2012-09-26 10:04:54 +00:00
return self . label . references . length == 0 ? self . body : self ;
2012-09-03 12:47:15 +00:00
} ) ;
2012-10-09 10:52:32 +00:00
OPT ( AST _Block , function ( self , compressor ) {
2017-06-16 11:19:54 +00:00
tighten _body ( self . body , compressor ) ;
2012-10-09 10:52:32 +00:00
return self ;
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _BlockStatement , function ( self , compressor ) {
2017-06-16 11:19:54 +00:00
tighten _body ( self . body , compressor ) ;
2012-09-26 10:04:54 +00:00
switch ( self . body . length ) {
case 1 : return self . body [ 0 ] ;
case 0 : return make _node ( AST _EmptyStatement , self ) ;
2012-09-17 09:03:02 +00:00
}
2012-09-26 10:04:54 +00:00
return self ;
2012-09-05 08:31:02 +00:00
} ) ;
2012-09-23 09:47:34 +00:00
AST _Scope . DEFMETHOD ( "drop_unused" , function ( compressor ) {
2017-04-30 14:52:36 +00:00
if ( ! compressor . option ( "unused" ) ) return ;
if ( compressor . has _directive ( "use asm" ) ) return ;
2012-09-23 09:47:34 +00:00
var self = this ;
2017-04-30 14:52:36 +00:00
if ( self . uses _eval || self . uses _with ) return ;
var drop _funcs = ! ( self instanceof AST _Toplevel ) || compressor . toplevel . funcs ;
var drop _vars = ! ( self instanceof AST _Toplevel ) || compressor . toplevel . vars ;
if ( ! drop _funcs && ! drop _vars ) return ;
2017-07-13 16:39:34 +00:00
var assign _as _unused = /keep_assign/ . test ( compressor . option ( "unused" ) ) ? return _false : function ( node ) {
if ( node instanceof AST _Assign && ( node . write _only || node . operator == "=" ) ) {
return node . left ;
}
if ( node instanceof AST _Unary && node . write _only ) return node . expression ;
} ;
2017-04-30 14:52:36 +00:00
var in _use = [ ] ;
var in _use _ids = Object . create ( null ) ; // avoid expensive linear scans of in_use
if ( self instanceof AST _Toplevel && compressor . top _retain ) {
self . variables . each ( function ( def ) {
if ( compressor . top _retain ( def ) && ! ( def . id in in _use _ids ) ) {
in _use _ids [ def . id ] = true ;
in _use . push ( def ) ;
}
} ) ;
}
var var _defs _by _id = new Dictionary ( ) ;
var initializations = new Dictionary ( ) ;
// pass 1: find out which symbols are directly used in
// this scope (not in nested scopes).
var scope = this ;
var tw = new TreeWalker ( function ( node , descend ) {
if ( node !== self ) {
if ( node instanceof AST _Defun ) {
if ( ! drop _funcs && scope === self ) {
var node _def = node . name . definition ( ) ;
if ( ! ( node _def . id in in _use _ids ) ) {
in _use _ids [ node _def . id ] = true ;
in _use . push ( node _def ) ;
}
2017-02-18 11:03:53 +00:00
}
2017-04-30 14:52:36 +00:00
initializations . add ( node . name . name , node ) ;
return true ; // don't go in nested scopes
}
if ( node instanceof AST _Definitions && scope === self ) {
node . definitions . forEach ( function ( def ) {
var node _def = def . name . definition ( ) ;
if ( def . name instanceof AST _SymbolVar ) {
var _defs _by _id . add ( node _def . id , def ) ;
}
if ( ! drop _vars ) {
2017-02-18 11:03:53 +00:00
if ( ! ( node _def . id in in _use _ids ) ) {
in _use _ids [ node _def . id ] = true ;
in _use . push ( node _def ) ;
}
}
2017-04-30 14:52:36 +00:00
if ( def . value ) {
initializations . add ( def . name . name , def . value ) ;
if ( def . value . has _side _effects ( compressor ) ) {
def . value . walk ( tw ) ;
2012-09-19 09:27:38 +00:00
}
2016-03-28 21:58:50 +00:00
}
2017-04-30 14:52:36 +00:00
} ) ;
return true ;
}
2017-07-13 16:39:34 +00:00
if ( assign _as _unused ( node ) instanceof AST _SymbolRef && scope === self ) {
if ( node instanceof AST _Assign ) node . right . walk ( tw ) ;
2017-04-30 14:52:36 +00:00
return true ;
}
if ( node instanceof AST _SymbolRef ) {
var node _def = node . definition ( ) ;
if ( ! ( node _def . id in in _use _ids ) ) {
in _use _ids [ node _def . id ] = true ;
in _use . push ( node _def ) ;
2012-09-19 09:27:38 +00:00
}
2017-04-30 14:52:36 +00:00
return true ;
2012-09-19 09:27:38 +00:00
}
2017-04-30 14:52:36 +00:00
if ( node instanceof AST _Scope ) {
var save _scope = scope ;
scope = node ;
descend ( ) ;
scope = save _scope ;
return true ;
}
}
} ) ;
self . walk ( tw ) ;
// pass 2: for every used symbol we need to walk its
// initialization code to figure out if it uses other
// symbols (that may not be in_use).
for ( var i = 0 ; i < in _use . length ; ++ i ) {
in _use [ i ] . orig . forEach ( function ( decl ) {
// undeclared globals will be instanceof AST_SymbolRef
var init = initializations . get ( decl . name ) ;
if ( init ) init . forEach ( function ( init ) {
var tw = new TreeWalker ( function ( node ) {
if ( node instanceof AST _SymbolRef ) {
var node _def = node . definition ( ) ;
if ( ! ( node _def . id in in _use _ids ) ) {
in _use _ids [ node _def . id ] = true ;
in _use . push ( node _def ) ;
2012-12-05 10:30:25 +00:00
}
2017-04-30 14:52:36 +00:00
}
2012-12-05 10:30:25 +00:00
} ) ;
2017-04-30 14:52:36 +00:00
init . walk ( tw ) ;
2012-09-23 09:47:34 +00:00
} ) ;
2017-04-30 14:52:36 +00:00
} ) ;
}
// pass 3: we should drop declarations not in_use
var tt = new TreeTransformer (
function before ( node , descend , in _list ) {
if ( node instanceof AST _Function
&& node . name
&& ! compressor . option ( "keep_fnames" ) ) {
var def = node . name . definition ( ) ;
// any declarations with same name will overshadow
// name of this anonymous function and can therefore
// never be used anywhere
if ( ! ( def . id in in _use _ids ) || def . orig . length > 1 )
node . name = null ;
}
if ( node instanceof AST _Lambda && ! ( node instanceof AST _Accessor ) ) {
var trim = ! compressor . option ( "keep_fargs" ) ;
for ( var a = node . argnames , i = a . length ; -- i >= 0 ; ) {
var sym = a [ i ] ;
if ( ! ( sym . definition ( ) . id in in _use _ids ) ) {
sym . _ _unused = true ;
if ( trim ) {
a . pop ( ) ;
compressor [ sym . unreferenced ( ) ? "warn" : "info" ] ( "Dropping unused function argument {name} [{file}:{line},{col}]" , template ( sym ) ) ;
2012-10-13 12:04:44 +00:00
}
}
2017-04-30 14:52:36 +00:00
else {
trim = false ;
2012-09-23 09:47:34 +00:00
}
}
2017-04-30 14:52:36 +00:00
}
if ( drop _funcs && node instanceof AST _Defun && node !== self ) {
if ( ! ( node . name . definition ( ) . id in in _use _ids ) ) {
compressor [ node . name . unreferenced ( ) ? "warn" : "info" ] ( "Dropping unused function {name} [{file}:{line},{col}]" , template ( node . name ) ) ;
return make _node ( AST _EmptyStatement , node ) ;
}
return node ;
}
if ( drop _vars && node instanceof AST _Definitions && ! ( tt . parent ( ) instanceof AST _ForIn && tt . parent ( ) . init === node ) ) {
// place uninitialized names at the start
var body = [ ] , head = [ ] , tail = [ ] ;
// for unused names whose initialization has
// side effects, we can cascade the init. code
// into the next one, or next statement.
var side _effects = [ ] ;
node . definitions . forEach ( function ( def ) {
if ( def . value ) def . value = def . value . transform ( tt ) ;
var sym = def . name . definition ( ) ;
if ( sym . id in in _use _ids ) {
if ( def . name instanceof AST _SymbolVar ) {
var var _defs = var _defs _by _id . get ( sym . id ) ;
if ( var _defs . length > 1 && ! def . value ) {
compressor . warn ( "Dropping duplicated definition of variable {name} [{file}:{line},{col}]" , template ( def . name ) ) ;
2017-05-06 08:15:43 +00:00
remove ( var _defs , def ) ;
remove ( sym . orig , def . name ) ;
2017-04-30 14:52:36 +00:00
return ;
2017-04-17 09:11:29 +00:00
}
2017-04-30 14:52:36 +00:00
}
if ( def . value ) {
if ( side _effects . length > 0 ) {
if ( tail . length > 0 ) {
merge _sequence ( side _effects , def . value ) ;
def . value = make _sequence ( def . value , side _effects ) ;
} else {
body . push ( make _node ( AST _SimpleStatement , node , {
body : make _sequence ( node , side _effects )
} ) ) ;
2017-04-20 05:06:14 +00:00
}
2017-04-30 14:52:36 +00:00
side _effects = [ ] ;
2017-04-20 05:06:14 +00:00
}
2017-04-30 14:52:36 +00:00
tail . push ( def ) ;
2017-04-20 05:06:14 +00:00
} else {
2017-04-30 14:52:36 +00:00
head . push ( def ) ;
2012-09-23 09:47:34 +00:00
}
2017-04-30 14:52:36 +00:00
} else if ( sym . orig [ 0 ] instanceof AST _SymbolCatch ) {
var value = def . value && def . value . drop _side _effect _free ( compressor ) ;
if ( value ) merge _sequence ( side _effects , value ) ;
def . value = null ;
head . push ( def ) ;
} else {
var value = def . value && def . value . drop _side _effect _free ( compressor ) ;
if ( value ) {
compressor . warn ( "Side effects in initialization of unused variable {name} [{file}:{line},{col}]" , template ( def . name ) ) ;
merge _sequence ( side _effects , value ) ;
} else {
compressor [ def . name . unreferenced ( ) ? "warn" : "info" ] ( "Dropping unused variable {name} [{file}:{line},{col}]" , template ( def . name ) ) ;
2017-04-17 09:11:29 +00:00
}
2017-05-06 08:15:43 +00:00
remove ( sym . orig , def . name ) ;
2017-04-17 09:11:29 +00:00
}
2017-04-30 14:52:36 +00:00
} ) ;
if ( head . length == 0 && tail . length == 1 && tail [ 0 ] . name instanceof AST _SymbolVar ) {
var var _defs = var _defs _by _id . get ( tail [ 0 ] . name . definition ( ) . id ) ;
if ( var _defs . length > 1 ) {
var def = tail . pop ( ) ;
compressor . warn ( "Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]" , template ( def . name ) ) ;
2017-05-06 08:15:43 +00:00
remove ( var _defs , def ) ;
remove ( def . name . definition ( ) . orig , def . name ) ;
2017-04-30 14:52:36 +00:00
side _effects . unshift ( make _node ( AST _Assign , def , {
operator : "=" ,
left : make _node ( AST _SymbolRef , def . name , def . name ) ,
right : def . value
2017-04-20 05:06:14 +00:00
} ) ) ;
2012-09-26 13:43:14 +00:00
}
2017-02-18 11:03:53 +00:00
}
2017-04-30 14:52:36 +00:00
if ( head . length > 0 || tail . length > 0 ) {
node . definitions = head . concat ( tail ) ;
body . push ( node ) ;
}
if ( side _effects . length > 0 ) {
body . push ( make _node ( AST _SimpleStatement , node , {
body : make _sequence ( node , side _effects )
} ) ) ;
}
switch ( body . length ) {
case 0 :
return in _list ? MAP . skip : make _node ( AST _EmptyStatement , node ) ;
case 1 :
return body [ 0 ] ;
default :
return in _list ? MAP . splice ( body ) : make _node ( AST _BlockStatement , node , {
body : body
} ) ;
2017-04-19 20:18:38 +00:00
}
2017-04-30 14:52:36 +00:00
}
2017-07-13 16:39:34 +00:00
if ( drop _vars ) {
var def = assign _as _unused ( node ) ;
if ( def instanceof AST _SymbolRef
&& ! ( ( def = def . definition ( ) ) . id in in _use _ids )
2017-04-30 14:52:36 +00:00
&& self . variables . get ( def . name ) === def ) {
2017-07-13 16:39:34 +00:00
if ( node instanceof AST _Assign ) {
return maintain _this _binding ( tt . parent ( ) , node , node . right . transform ( tt ) ) ;
}
return make _node ( AST _Number , node , {
value : 0
} ) ;
2017-04-30 14:52:36 +00:00
}
}
// certain combination of unused name + side effect leads to:
// https://github.com/mishoo/UglifyJS2/issues/44
// https://github.com/mishoo/UglifyJS2/issues/1830
// https://github.com/mishoo/UglifyJS2/issues/1838
// that's an invalid AST.
// We fix it at this stage by moving the `var` outside the `for`.
if ( node instanceof AST _For ) {
descend ( node , this ) ;
2017-08-28 17:10:04 +00:00
var block ;
2017-04-30 14:52:36 +00:00
if ( node . init instanceof AST _BlockStatement ) {
2017-08-28 17:10:04 +00:00
block = node . init ;
2017-04-30 14:52:36 +00:00
node . init = block . body . pop ( ) ;
block . body . push ( node ) ;
2017-08-28 17:10:04 +00:00
}
if ( node . init instanceof AST _SimpleStatement ) {
2017-04-30 14:52:36 +00:00
node . init = node . init . body ;
} else if ( is _empty ( node . init ) ) {
node . init = null ;
2012-11-12 11:23:57 +00:00
}
2017-08-28 17:10:04 +00:00
return ! block ? node : in _list ? MAP . splice ( block . body ) : block ;
2017-04-30 14:52:36 +00:00
}
if ( node instanceof AST _LabeledStatement && node . body instanceof AST _For ) {
descend ( node , this ) ;
if ( node . body instanceof AST _BlockStatement ) {
var block = node . body ;
node . body = block . body . pop ( ) ;
block . body . push ( node ) ;
return in _list ? MAP . splice ( block . body ) : block ;
2017-04-20 05:06:14 +00:00
}
2017-04-30 14:52:36 +00:00
return node ;
2012-09-23 09:47:34 +00:00
}
2017-04-30 14:52:36 +00:00
if ( node instanceof AST _Scope && node !== self )
return node ;
function template ( sym ) {
return {
name : sym . name ,
file : sym . start . file ,
line : sym . start . line ,
col : sym . start . col
} ;
}
}
) ;
self . transform ( tt ) ;
2012-09-19 09:27:38 +00:00
} ) ;
2012-09-04 12:36:14 +00:00
AST _Scope . DEFMETHOD ( "hoist_declarations" , function ( compressor ) {
2015-10-05 23:51:09 +00:00
var self = this ;
if ( compressor . has _directive ( "use asm" ) ) return self ;
2012-09-05 10:43:34 +00:00
var hoist _funs = compressor . option ( "hoist_funs" ) ;
var hoist _vars = compressor . option ( "hoist_vars" ) ;
if ( hoist _funs || hoist _vars ) {
2012-09-25 12:15:47 +00:00
var dirs = [ ] ;
2012-09-05 10:43:34 +00:00
var hoisted = [ ] ;
2012-11-02 08:58:45 +00:00
var vars = new Dictionary ( ) , vars _found = 0 , var _decl = 0 ;
2012-09-25 12:15:47 +00:00
// let's count var_decl first, we seem to waste a lot of
// space if we hoist `var` when there's only one.
self . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _Scope && node !== self )
return true ;
if ( node instanceof AST _Var ) {
++ var _decl ;
return true ;
}
} ) ) ;
hoist _vars = hoist _vars && var _decl > 1 ;
var tt = new TreeTransformer (
function before ( node ) {
if ( node !== self ) {
if ( node instanceof AST _Directive ) {
dirs . push ( node ) ;
return make _node ( AST _EmptyStatement , node ) ;
}
2017-05-27 09:44:59 +00:00
if ( hoist _funs && node instanceof AST _Defun
&& ( tt . parent ( ) === self || ! compressor . has _directive ( "use strict" ) ) ) {
2012-09-25 12:15:47 +00:00
hoisted . push ( node ) ;
return make _node ( AST _EmptyStatement , node ) ;
}
2017-05-27 09:44:59 +00:00
if ( hoist _vars && node instanceof AST _Var ) {
2012-09-25 12:15:47 +00:00
node . definitions . forEach ( function ( def ) {
2012-11-02 08:58:45 +00:00
vars . set ( def . name . name , def ) ;
2012-09-25 12:15:47 +00:00
++ vars _found ;
} ) ;
2017-03-16 04:03:30 +00:00
var seq = node . to _assignments ( compressor ) ;
2012-09-25 12:15:47 +00:00
var p = tt . parent ( ) ;
if ( p instanceof AST _ForIn && p . init === node ) {
2016-01-05 11:56:52 +00:00
if ( seq == null ) {
var def = node . definitions [ 0 ] . name ;
return make _node ( AST _SymbolRef , def , def ) ;
}
2012-09-25 12:15:47 +00:00
return seq ;
}
if ( p instanceof AST _For && p . init === node ) {
return seq ;
}
if ( ! seq ) return make _node ( AST _EmptyStatement , node ) ;
return make _node ( AST _SimpleStatement , node , {
body : seq
} ) ;
}
if ( node instanceof AST _Scope )
return node ; // to avoid descending in nested scopes
2012-09-05 10:43:34 +00:00
}
}
2012-09-25 12:15:47 +00:00
) ;
self = self . transform ( tt ) ;
2012-12-05 10:30:25 +00:00
if ( vars _found > 0 ) {
// collect only vars which don't show up in self's arguments list
var defs = [ ] ;
vars . each ( function ( def , name ) {
if ( self instanceof AST _Lambda
&& find _if ( function ( x ) { return x . name == def . name . name } ,
self . argnames ) ) {
vars . del ( name ) ;
} else {
def = def . clone ( ) ;
def . value = null ;
defs . push ( def ) ;
vars . set ( name , def ) ;
}
} ) ;
if ( defs . length > 0 ) {
// try to merge in assignments
for ( var i = 0 ; i < self . body . length ; ) {
if ( self . body [ i ] instanceof AST _SimpleStatement ) {
var expr = self . body [ i ] . body , sym , assign ;
if ( expr instanceof AST _Assign
&& expr . operator == "="
&& ( sym = expr . left ) instanceof AST _Symbol
&& vars . has ( sym . name ) )
{
var def = vars . get ( sym . name ) ;
if ( def . value ) break ;
def . value = expr . right ;
remove ( defs , def ) ;
defs . push ( def ) ;
self . body . splice ( i , 1 ) ;
continue ;
}
2017-04-12 13:56:27 +00:00
if ( expr instanceof AST _Sequence
&& ( assign = expr . expressions [ 0 ] ) instanceof AST _Assign
2012-12-05 10:30:25 +00:00
&& assign . operator == "="
&& ( sym = assign . left ) instanceof AST _Symbol
&& vars . has ( sym . name ) )
{
var def = vars . get ( sym . name ) ;
if ( def . value ) break ;
def . value = assign . right ;
remove ( defs , def ) ;
defs . push ( def ) ;
2017-04-12 13:56:27 +00:00
self . body [ i ] . body = make _sequence ( expr , expr . expressions . slice ( 1 ) ) ;
2012-12-05 10:30:25 +00:00
continue ;
}
}
2012-12-05 11:14:49 +00:00
if ( self . body [ i ] instanceof AST _EmptyStatement ) {
self . body . splice ( i , 1 ) ;
continue ;
}
if ( self . body [ i ] instanceof AST _BlockStatement ) {
var tmp = [ i , 1 ] . concat ( self . body [ i ] . body ) ;
self . body . splice . apply ( self . body , tmp ) ;
continue ;
}
2012-12-05 10:30:25 +00:00
break ;
}
defs = make _node ( AST _Var , self , {
definitions : defs
} ) ;
hoisted . push ( defs ) ;
} ;
}
2012-09-25 12:15:47 +00:00
self . body = dirs . concat ( hoisted , self . body ) ;
2012-09-04 12:36:14 +00:00
}
2012-09-25 12:15:47 +00:00
return self ;
2012-09-04 12:36:14 +00:00
} ) ;
2017-02-19 17:46:59 +00:00
// drop_side_effect_free()
// remove side-effect-free parts which only affects return value
( function ( def ) {
// Drop side-effect-free elements from an array of expressions.
// Returns an array of expressions with side-effects or null
// if all elements were dropped. Note: original array may be
// returned if nothing changed.
function trim ( nodes , compressor , first _in _statement ) {
2017-04-12 13:56:27 +00:00
var len = nodes . length ;
if ( ! len ) return null ;
2017-02-19 17:46:59 +00:00
var ret = [ ] , changed = false ;
2017-04-12 13:56:27 +00:00
for ( var i = 0 ; i < len ; i ++ ) {
2017-02-19 17:46:59 +00:00
var node = nodes [ i ] . drop _side _effect _free ( compressor , first _in _statement ) ;
changed |= node !== nodes [ i ] ;
if ( node ) {
2017-04-12 13:56:27 +00:00
merge _sequence ( ret , node ) ;
2017-02-19 17:46:59 +00:00
first _in _statement = false ;
}
}
return changed ? ret . length ? ret : null : nodes ;
}
def ( AST _Node , return _this ) ;
def ( AST _Constant , return _null ) ;
def ( AST _This , return _null ) ;
def ( AST _Call , function ( compressor , first _in _statement ) {
2017-03-03 10:13:07 +00:00
if ( ! this . has _pure _annotation ( compressor ) && compressor . pure _funcs ( this ) ) {
2017-03-07 19:31:51 +00:00
if ( this . expression instanceof AST _Function
&& ( ! this . expression . name || ! this . expression . name . definition ( ) . references . length ) ) {
2017-03-03 10:13:07 +00:00
var node = this . clone ( ) ;
2017-04-16 17:36:50 +00:00
node . expression . process _expression ( false , compressor ) ;
2017-03-03 10:13:07 +00:00
return node ;
}
return this ;
}
2017-02-27 18:25:44 +00:00
if ( this . pure ) {
compressor . warn ( "Dropping __PURE__ call [{file}:{line},{col}]" , this . start ) ;
this . pure . value = this . pure . value . replace ( /[@#]__PURE__/g , ' ' ) ;
}
2017-02-19 17:46:59 +00:00
var args = trim ( this . args , compressor , first _in _statement ) ;
2017-04-12 13:56:27 +00:00
return args && make _sequence ( this , args ) ;
2017-02-19 17:46:59 +00:00
} ) ;
2017-05-13 18:10:34 +00:00
def ( AST _Accessor , return _null ) ;
2017-02-19 17:46:59 +00:00
def ( AST _Function , return _null ) ;
def ( AST _Binary , function ( compressor , first _in _statement ) {
var right = this . right . drop _side _effect _free ( compressor ) ;
if ( ! right ) return this . left . drop _side _effect _free ( compressor , first _in _statement ) ;
switch ( this . operator ) {
case "&&" :
case "||" :
2017-03-26 20:37:42 +00:00
if ( right === this . right ) return this ;
2017-02-19 17:46:59 +00:00
var node = this . clone ( ) ;
node . right = right ;
return node ;
default :
var left = this . left . drop _side _effect _free ( compressor , first _in _statement ) ;
if ( ! left ) return this . right . drop _side _effect _free ( compressor , first _in _statement ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( this , [ left , right ] ) ;
2017-02-19 17:46:59 +00:00
}
} ) ;
2017-07-13 16:39:34 +00:00
def ( AST _Assign , function ( compressor ) {
this . write _only = ! this . left . has _side _effects ( compressor ) ;
return this ;
} ) ;
2017-02-19 17:46:59 +00:00
def ( AST _Conditional , function ( compressor ) {
var consequent = this . consequent . drop _side _effect _free ( compressor ) ;
var alternative = this . alternative . drop _side _effect _free ( compressor ) ;
if ( consequent === this . consequent && alternative === this . alternative ) return this ;
if ( ! consequent ) return alternative ? make _node ( AST _Binary , this , {
operator : "||" ,
left : this . condition ,
right : alternative
} ) : this . condition . drop _side _effect _free ( compressor ) ;
if ( ! alternative ) return make _node ( AST _Binary , this , {
operator : "&&" ,
left : this . condition ,
right : consequent
} ) ;
var node = this . clone ( ) ;
node . consequent = consequent ;
node . alternative = alternative ;
return node ;
} ) ;
def ( AST _Unary , function ( compressor , first _in _statement ) {
2017-07-13 16:39:34 +00:00
if ( unary _side _effects ( this . operator ) ) {
this . write _only = ! this . expression . has _side _effects ( compressor ) ;
return this ;
}
2017-03-26 11:14:30 +00:00
if ( this . operator == "typeof" && this . expression instanceof AST _SymbolRef ) return null ;
var expression = this . expression . drop _side _effect _free ( compressor , first _in _statement ) ;
if ( first _in _statement
&& this instanceof AST _UnaryPrefix
&& is _iife _call ( expression ) ) {
if ( expression === this . expression && this . operator . length === 1 ) return this ;
return make _node ( AST _UnaryPrefix , this , {
operator : this . operator . length === 1 ? this . operator : "!" ,
expression : expression
} ) ;
2017-02-19 17:46:59 +00:00
}
2017-03-26 11:14:30 +00:00
return expression ;
2017-02-19 17:46:59 +00:00
} ) ;
2017-07-15 07:16:11 +00:00
def ( AST _SymbolRef , function ( compressor ) {
return this . is _declared ( compressor ) ? null : this ;
2017-02-19 17:46:59 +00:00
} ) ;
def ( AST _Object , function ( compressor , first _in _statement ) {
var values = trim ( this . properties , compressor , first _in _statement ) ;
2017-04-12 13:56:27 +00:00
return values && make _sequence ( this , values ) ;
2017-02-19 17:46:59 +00:00
} ) ;
def ( AST _ObjectProperty , function ( compressor , first _in _statement ) {
return this . value . drop _side _effect _free ( compressor , first _in _statement ) ;
} ) ;
def ( AST _Array , function ( compressor , first _in _statement ) {
var values = trim ( this . elements , compressor , first _in _statement ) ;
2017-04-12 13:56:27 +00:00
return values && make _sequence ( this , values ) ;
2017-02-19 17:46:59 +00:00
} ) ;
def ( AST _Dot , function ( compressor , first _in _statement ) {
2017-05-13 18:10:34 +00:00
if ( this . expression . may _throw _on _access ( compressor ) ) return this ;
2017-02-19 17:46:59 +00:00
return this . expression . drop _side _effect _free ( compressor , first _in _statement ) ;
} ) ;
def ( AST _Sub , function ( compressor , first _in _statement ) {
2017-05-13 18:10:34 +00:00
if ( this . expression . may _throw _on _access ( compressor ) ) return this ;
2017-02-19 17:46:59 +00:00
var expression = this . expression . drop _side _effect _free ( compressor , first _in _statement ) ;
if ( ! expression ) return this . property . drop _side _effect _free ( compressor , first _in _statement ) ;
var property = this . property . drop _side _effect _free ( compressor ) ;
if ( ! property ) return expression ;
2017-04-12 13:56:27 +00:00
return make _sequence ( this , [ expression , property ] ) ;
2017-02-19 17:46:59 +00:00
} ) ;
2017-04-12 13:56:27 +00:00
def ( AST _Sequence , function ( compressor ) {
var last = this . expressions [ this . expressions . length - 1 ] ;
var expr = last . drop _side _effect _free ( compressor ) ;
if ( expr === last ) return this ;
var expressions = this . expressions . slice ( 0 , - 1 ) ;
if ( expr ) merge _sequence ( expressions , expr ) ;
return make _sequence ( this , expressions ) ;
2017-02-19 17:46:59 +00:00
} ) ;
} ) ( function ( node , func ) {
node . DEFMETHOD ( "drop_side_effect_free" , func ) ;
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _SimpleStatement , function ( self , compressor ) {
2012-10-03 10:08:03 +00:00
if ( compressor . option ( "side_effects" ) ) {
2017-02-19 17:46:59 +00:00
var body = self . body ;
var node = body . drop _side _effect _free ( compressor , true ) ;
if ( ! node ) {
2012-10-03 10:08:03 +00:00
compressor . warn ( "Dropping side-effect-free statement [{file}:{line},{col}]" , self . start ) ;
return make _node ( AST _EmptyStatement , self ) ;
}
2017-02-19 17:46:59 +00:00
if ( node !== body ) {
return make _node ( AST _SimpleStatement , self , { body : node } ) ;
}
2012-09-11 10:15:55 +00:00
}
2012-09-26 10:04:54 +00:00
return self ;
2012-09-11 10:15:55 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _DWLoop , function ( self , compressor ) {
2012-09-07 12:18:32 +00:00
if ( ! compressor . option ( "loops" ) ) return self ;
2017-03-15 17:02:59 +00:00
var cond = self . condition . evaluate ( compressor ) ;
if ( cond !== self . condition ) {
if ( cond ) {
2012-09-04 10:20:28 +00:00
return make _node ( AST _For , self , {
body : self . body
} ) ;
2017-04-04 15:48:22 +00:00
}
if ( compressor . option ( "dead_code" ) && self instanceof AST _While ) {
2017-03-15 17:02:59 +00:00
var a = [ ] ;
extract _declarations _from _unreachable _code ( compressor , self . body , a ) ;
2017-04-22 14:15:04 +00:00
return make _node ( AST _BlockStatement , self , { body : a } ) . optimize ( compressor ) ;
2017-04-04 15:48:22 +00:00
}
if ( self instanceof AST _Do ) {
var has _loop _control = false ;
var tw = new TreeWalker ( function ( node ) {
if ( node instanceof AST _Scope || has _loop _control ) return true ;
if ( node instanceof AST _LoopControl && tw . loopcontrol _target ( node ) === self )
return has _loop _control = true ;
} ) ;
2017-04-22 14:15:04 +00:00
var parent = compressor . parent ( ) ;
( parent instanceof AST _LabeledStatement ? parent : self ) . walk ( tw ) ;
2017-04-04 15:48:22 +00:00
if ( ! has _loop _control ) return self . body ;
2012-09-04 10:20:28 +00:00
}
}
2017-02-18 11:05:54 +00:00
if ( self instanceof AST _While ) {
2017-03-15 17:02:59 +00:00
return make _node ( AST _For , self , self ) . optimize ( compressor ) ;
2017-02-18 11:05:54 +00:00
}
2012-08-21 12:45:05 +00:00
return self ;
} ) ;
2012-11-07 16:57:51 +00:00
function if _break _in _loop ( self , compressor ) {
function drop _it ( rest ) {
rest = as _statement _array ( rest ) ;
if ( self . body instanceof AST _BlockStatement ) {
self . body = self . body . clone ( ) ;
self . body . body = rest . concat ( self . body . body . slice ( 1 ) ) ;
self . body = self . body . transform ( compressor ) ;
} else {
self . body = make _node ( AST _BlockStatement , self . body , {
body : rest
} ) . transform ( compressor ) ;
}
if _break _in _loop ( self , compressor ) ;
}
var first = self . body instanceof AST _BlockStatement ? self . body . body [ 0 ] : self . body ;
if ( first instanceof AST _If ) {
if ( first . body instanceof AST _Break
2017-04-04 15:48:22 +00:00
&& compressor . loopcontrol _target ( first . body ) === compressor . self ( ) ) {
2012-11-07 16:57:51 +00:00
if ( self . condition ) {
self . condition = make _node ( AST _Binary , self . condition , {
left : self . condition ,
operator : "&&" ,
right : first . condition . negate ( compressor ) ,
} ) ;
} else {
self . condition = first . condition . negate ( compressor ) ;
}
drop _it ( first . alternative ) ;
}
else if ( first . alternative instanceof AST _Break
2017-04-04 15:48:22 +00:00
&& compressor . loopcontrol _target ( first . alternative ) === compressor . self ( ) ) {
2012-11-07 16:57:51 +00:00
if ( self . condition ) {
self . condition = make _node ( AST _Binary , self . condition , {
left : self . condition ,
operator : "&&" ,
right : first . condition ,
} ) ;
} else {
self . condition = first . condition ;
}
drop _it ( first . body ) ;
}
}
} ;
2012-09-26 10:04:54 +00:00
OPT ( AST _For , function ( self , compressor ) {
if ( ! compressor . option ( "loops" ) ) return self ;
2017-03-15 17:02:59 +00:00
if ( self . condition ) {
var cond = self . condition . evaluate ( compressor ) ;
if ( compressor . option ( "dead_code" ) && ! cond ) {
var a = [ ] ;
if ( self . init instanceof AST _Statement ) {
a . push ( self . init ) ;
}
else if ( self . init ) {
a . push ( make _node ( AST _SimpleStatement , self . init , {
body : self . init
} ) ) ;
2012-09-07 12:18:32 +00:00
}
2017-03-15 17:02:59 +00:00
extract _declarations _from _unreachable _code ( compressor , self . body , a ) ;
2017-04-22 14:15:04 +00:00
return make _node ( AST _BlockStatement , self , { body : a } ) . optimize ( compressor ) ;
2017-03-15 17:02:59 +00:00
}
if ( cond !== self . condition ) {
cond = make _node _from _constant ( cond , self . condition ) . transform ( compressor ) ;
self . condition = best _of _expression ( cond , self . condition ) ;
2012-09-07 12:18:32 +00:00
}
}
2012-11-07 16:57:51 +00:00
if _break _in _loop ( self , compressor ) ;
2012-09-26 10:04:54 +00:00
return self ;
2012-08-21 12:45:05 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _If , function ( self , compressor ) {
2017-02-18 11:02:59 +00:00
if ( is _empty ( self . alternative ) ) self . alternative = null ;
2012-09-04 10:20:28 +00:00
if ( ! compressor . option ( "conditionals" ) ) return self ;
2012-09-03 16:38:45 +00:00
// if condition can be statically determined, warn and drop
// one of the blocks. note, statically determined implies
// “has no side effects”; also it doesn't work for cases like
// `x && true`, though it probably should.
var cond = self . condition . evaluate ( compressor ) ;
2017-03-15 17:02:59 +00:00
if ( cond !== self . condition ) {
if ( cond ) {
2012-09-21 11:38:52 +00:00
compressor . warn ( "Condition always true [{file}:{line},{col}]" , self . condition . start ) ;
2012-09-07 12:18:32 +00:00
if ( compressor . option ( "dead_code" ) ) {
var a = [ ] ;
if ( self . alternative ) {
extract _declarations _from _unreachable _code ( compressor , self . alternative , a ) ;
}
a . push ( self . body ) ;
2017-03-15 10:44:13 +00:00
return make _node ( AST _BlockStatement , self , { body : a } ) . optimize ( compressor ) ;
2012-09-07 12:18:32 +00:00
}
2012-09-03 16:38:45 +00:00
} else {
2012-09-21 11:38:52 +00:00
compressor . warn ( "Condition always false [{file}:{line},{col}]" , self . condition . start ) ;
2012-09-07 12:18:32 +00:00
if ( compressor . option ( "dead_code" ) ) {
var a = [ ] ;
extract _declarations _from _unreachable _code ( compressor , self . body , a ) ;
if ( self . alternative ) a . push ( self . alternative ) ;
2017-03-15 10:44:13 +00:00
return make _node ( AST _BlockStatement , self , { body : a } ) . optimize ( compressor ) ;
2012-09-07 12:18:32 +00:00
}
2012-09-03 16:38:45 +00:00
}
2017-03-15 17:02:59 +00:00
cond = make _node _from _constant ( cond , self . condition ) . transform ( compressor ) ;
self . condition = best _of _expression ( cond , self . condition ) ;
2012-09-03 16:38:45 +00:00
}
2012-09-26 13:43:14 +00:00
var negated = self . condition . negate ( compressor ) ;
2016-02-21 17:05:02 +00:00
var self _condition _length = self . condition . print _to _string ( ) . length ;
var negated _length = negated . print _to _string ( ) . length ;
var negated _is _best = negated _length < self _condition _length ;
2012-09-10 12:54:17 +00:00
if ( self . alternative && negated _is _best ) {
2012-09-12 10:00:13 +00:00
negated _is _best = false ; // because we already do the switch here.
2016-02-21 17:05:02 +00:00
// no need to swap values of self_condition_length and negated_length
// here because they are only used in an equality comparison later on.
2012-09-10 12:54:17 +00:00
self . condition = negated ;
2012-09-04 12:36:14 +00:00
var tmp = self . body ;
2017-03-01 07:25:26 +00:00
self . body = self . alternative || make _node ( AST _EmptyStatement , self ) ;
2012-09-04 12:36:14 +00:00
self . alternative = tmp ;
}
2012-09-16 12:46:20 +00:00
if ( is _empty ( self . body ) && is _empty ( self . alternative ) ) {
2012-09-07 12:18:32 +00:00
return make _node ( AST _SimpleStatement , self . condition , {
2017-03-15 10:44:13 +00:00
body : self . condition . clone ( )
} ) . optimize ( compressor ) ;
2012-09-07 12:18:32 +00:00
}
2012-09-03 16:38:45 +00:00
if ( self . body instanceof AST _SimpleStatement
&& self . alternative instanceof AST _SimpleStatement ) {
return make _node ( AST _SimpleStatement , self , {
body : make _node ( AST _Conditional , self , {
condition : self . condition ,
2017-03-26 20:37:42 +00:00
consequent : self . body . body ,
alternative : self . alternative . body
2012-09-26 13:43:14 +00:00
} )
2017-03-15 10:44:13 +00:00
} ) . optimize ( compressor ) ;
2012-09-03 16:38:45 +00:00
}
2012-09-16 12:46:20 +00:00
if ( is _empty ( self . alternative ) && self . body instanceof AST _SimpleStatement ) {
2016-02-21 17:05:02 +00:00
if ( self _condition _length === negated _length && ! negated _is _best
&& self . condition instanceof AST _Binary && self . condition . operator == "||" ) {
// although the code length of self.condition and negated are the same,
// negated does not require additional surrounding parentheses.
// see https://github.com/mishoo/UglifyJS2/issues/979
negated _is _best = true ;
}
2012-09-10 12:54:17 +00:00
if ( negated _is _best ) return make _node ( AST _SimpleStatement , self , {
body : make _node ( AST _Binary , self , {
operator : "||" ,
left : negated ,
2017-03-26 20:37:42 +00:00
right : self . body . body
2012-09-26 13:43:14 +00:00
} )
2017-03-15 10:44:13 +00:00
} ) . optimize ( compressor ) ;
2012-09-12 10:00:13 +00:00
return make _node ( AST _SimpleStatement , self , {
2012-09-03 16:38:45 +00:00
body : make _node ( AST _Binary , self , {
2012-09-03 16:43:46 +00:00
operator : "&&" ,
left : self . condition ,
2017-03-26 20:37:42 +00:00
right : self . body . body
2012-09-26 13:43:14 +00:00
} )
2017-03-15 10:44:13 +00:00
} ) . optimize ( compressor ) ;
2012-09-03 16:38:45 +00:00
}
if ( self . body instanceof AST _EmptyStatement
2012-09-04 12:36:14 +00:00
&& self . alternative instanceof AST _SimpleStatement ) {
2012-09-03 16:38:45 +00:00
return make _node ( AST _SimpleStatement , self , {
body : make _node ( AST _Binary , self , {
2012-09-03 16:43:46 +00:00
operator : "||" ,
left : self . condition ,
2017-03-26 20:37:42 +00:00
right : self . alternative . body
2012-09-26 13:43:14 +00:00
} )
2017-03-15 10:44:13 +00:00
} ) . optimize ( compressor ) ;
2012-09-03 16:38:45 +00:00
}
2012-09-03 20:49:57 +00:00
if ( self . body instanceof AST _Exit
&& self . alternative instanceof AST _Exit
&& self . body . TYPE == self . alternative . TYPE ) {
return make _node ( self . body . CTOR , self , {
value : make _node ( AST _Conditional , self , {
condition : self . condition ,
2017-03-05 05:09:27 +00:00
consequent : self . body . value || make _node ( AST _Undefined , self . body ) ,
alternative : self . alternative . value || make _node ( AST _Undefined , self . alternative )
2017-03-15 10:44:13 +00:00
} ) . transform ( compressor )
} ) . optimize ( compressor ) ;
2012-09-03 20:49:57 +00:00
}
2012-09-12 13:10:03 +00:00
if ( self . body instanceof AST _If
&& ! self . body . alternative
&& ! self . alternative ) {
2017-03-15 10:44:13 +00:00
self = make _node ( AST _If , self , {
condition : make _node ( AST _Binary , self . condition , {
operator : "&&" ,
left : self . condition ,
right : self . body . condition
} ) ,
body : self . body . body ,
alternative : null
} ) ;
2012-09-12 13:10:03 +00:00
}
2012-09-16 12:46:20 +00:00
if ( aborts ( self . body ) ) {
2012-09-12 13:10:03 +00:00
if ( self . alternative ) {
var alt = self . alternative ;
self . alternative = null ;
return make _node ( AST _BlockStatement , self , {
body : [ self , alt ]
2017-03-15 10:44:13 +00:00
} ) . optimize ( compressor ) ;
2012-09-12 13:10:03 +00:00
}
}
2012-09-16 12:46:20 +00:00
if ( aborts ( self . alternative ) ) {
var body = self . body ;
self . body = self . alternative ;
2012-09-26 13:43:14 +00:00
self . condition = negated _is _best ? negated : self . condition . negate ( compressor ) ;
2012-09-16 12:46:20 +00:00
self . alternative = null ;
return make _node ( AST _BlockStatement , self , {
body : [ self , body ]
2017-03-15 10:44:13 +00:00
} ) . optimize ( compressor ) ;
2012-09-16 12:46:20 +00:00
}
2012-08-21 12:45:05 +00:00
return self ;
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Switch , function ( self , compressor ) {
2017-04-02 06:52:25 +00:00
if ( ! compressor . option ( "switches" ) ) return self ;
2017-03-25 21:15:46 +00:00
var branch ;
2017-03-15 17:02:59 +00:00
var value = self . expression . evaluate ( compressor ) ;
2017-03-25 21:15:46 +00:00
if ( value !== self . expression ) {
var expression = make _node _from _constant ( value , self . expression ) . transform ( compressor ) ;
2017-03-15 17:02:59 +00:00
self . expression = best _of _expression ( expression , self . expression ) ;
2017-03-25 21:15:46 +00:00
}
2017-03-27 19:59:13 +00:00
if ( ! compressor . option ( "dead_code" ) ) return self ;
var decl = [ ] ;
var body = [ ] ;
var default _branch ;
var exact _match ;
for ( var i = 0 , len = self . body . length ; i < len && ! exact _match ; i ++ ) {
branch = self . body [ i ] ;
if ( branch instanceof AST _Default ) {
2017-04-01 09:19:57 +00:00
if ( ! default _branch ) {
default _branch = branch ;
} else {
2017-04-02 09:07:20 +00:00
eliminate _branch ( branch , body [ body . length - 1 ] ) ;
2017-03-25 08:21:42 +00:00
}
2017-03-27 19:59:13 +00:00
} else if ( value !== self . expression ) {
var exp = branch . expression . evaluate ( compressor ) ;
if ( exp === value ) {
exact _match = branch ;
if ( default _branch ) {
2017-04-01 09:19:57 +00:00
var default _index = body . indexOf ( default _branch ) ;
body . splice ( default _index , 1 ) ;
eliminate _branch ( default _branch , body [ default _index - 1 ] ) ;
2017-03-27 19:59:13 +00:00
default _branch = null ;
}
2017-04-01 09:19:57 +00:00
} else if ( exp !== branch . expression ) {
2017-04-02 09:07:20 +00:00
eliminate _branch ( branch , body [ body . length - 1 ] ) ;
2017-03-27 19:59:13 +00:00
continue ;
2017-03-25 21:15:46 +00:00
}
}
2017-03-27 19:59:13 +00:00
if ( aborts ( branch ) ) {
2017-04-01 09:19:57 +00:00
var prev = body [ body . length - 1 ] ;
if ( aborts ( prev ) && prev . body . length == branch . body . length
&& make _node ( AST _BlockStatement , prev , prev ) . equivalent _to ( make _node ( AST _BlockStatement , branch , branch ) ) ) {
prev . body = [ ] ;
2017-03-29 14:08:26 +00:00
}
2017-03-26 18:32:46 +00:00
}
2017-04-01 09:19:57 +00:00
body . push ( branch ) ;
2017-03-25 21:15:46 +00:00
}
2017-04-02 09:07:20 +00:00
while ( i < len ) eliminate _branch ( self . body [ i ++ ] , body [ body . length - 1 ] ) ;
2017-03-27 19:59:13 +00:00
if ( body . length > 0 ) {
body [ 0 ] . body = decl . concat ( body [ 0 ] . body ) ;
}
self . body = body ;
while ( branch = body [ body . length - 1 ] ) {
2017-03-25 21:15:46 +00:00
var stat = branch . body [ branch . body . length - 1 ] ;
2017-04-04 15:48:22 +00:00
if ( stat instanceof AST _Break && compressor . loopcontrol _target ( stat ) === self )
2017-03-25 21:15:46 +00:00
branch . body . pop ( ) ;
2017-03-27 19:59:13 +00:00
if ( branch . body . length || branch instanceof AST _Case
&& ( default _branch || branch . expression . has _side _effects ( compressor ) ) ) break ;
if ( body . pop ( ) === default _branch ) default _branch = null ;
2017-03-25 21:15:46 +00:00
}
2017-03-27 19:59:13 +00:00
if ( body . length == 0 ) {
2017-03-26 18:32:46 +00:00
return make _node ( AST _BlockStatement , self , {
body : decl . concat ( make _node ( AST _SimpleStatement , self . expression , {
body : self . expression
} ) )
2017-03-25 21:15:46 +00:00
} ) . optimize ( compressor ) ;
}
2017-03-27 19:59:13 +00:00
if ( body . length == 1 && ( body [ 0 ] === exact _match || body [ 0 ] === default _branch ) ) {
2017-03-25 21:15:46 +00:00
var has _break = false ;
var tw = new TreeWalker ( function ( node ) {
if ( has _break
|| node instanceof AST _Lambda
|| node instanceof AST _SimpleStatement ) return true ;
2017-04-04 15:48:22 +00:00
if ( node instanceof AST _Break && tw . loopcontrol _target ( node ) === self )
2017-03-25 21:15:46 +00:00
has _break = true ;
2012-11-14 10:06:07 +00:00
} ) ;
2017-03-25 21:15:46 +00:00
self . walk ( tw ) ;
2017-04-02 06:52:25 +00:00
if ( ! has _break ) {
body = body [ 0 ] . body . slice ( ) ;
body . unshift ( make _node ( AST _SimpleStatement , self . expression , {
body : self . expression
} ) ) ;
return make _node ( AST _BlockStatement , self , {
body : body
} ) . optimize ( compressor ) ;
}
2012-11-14 10:06:07 +00:00
}
2012-09-26 10:04:54 +00:00
return self ;
2017-04-01 09:19:57 +00:00
function eliminate _branch ( branch , prev ) {
if ( prev && ! aborts ( prev ) ) {
prev . body = prev . body . concat ( branch . body ) ;
} else {
extract _declarations _from _unreachable _code ( compressor , branch , decl ) ;
}
}
2012-08-21 12:45:05 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Try , function ( self , compressor ) {
2017-06-16 11:19:54 +00:00
tighten _body ( self . body , compressor ) ;
2017-03-30 04:16:58 +00:00
if ( self . bcatch && self . bfinally && all ( self . bfinally . body , is _empty ) ) self . bfinally = null ;
if ( all ( self . body , is _empty ) ) {
var body = [ ] ;
if ( self . bcatch ) extract _declarations _from _unreachable _code ( compressor , self . bcatch , body ) ;
if ( self . bfinally ) body = body . concat ( self . bfinally . body ) ;
2017-04-22 14:15:04 +00:00
return make _node ( AST _BlockStatement , self , {
2017-03-30 04:16:58 +00:00
body : body
2017-04-22 14:15:04 +00:00
} ) . optimize ( compressor ) ;
2017-03-30 04:16:58 +00:00
}
2012-09-26 10:04:54 +00:00
return self ;
2012-08-21 12:45:05 +00:00
} ) ;
2012-08-22 12:21:58 +00:00
AST _Definitions . DEFMETHOD ( "remove_initializers" , function ( ) {
2012-10-12 08:07:35 +00:00
this . definitions . forEach ( function ( def ) { def . value = null } ) ;
2012-08-22 12:21:58 +00:00
} ) ;
2017-03-16 04:03:30 +00:00
AST _Definitions . DEFMETHOD ( "to_assignments" , function ( compressor ) {
var reduce _vars = compressor . option ( "reduce_vars" ) ;
2012-09-05 10:43:34 +00:00
var assignments = this . definitions . reduce ( function ( a , def ) {
if ( def . value ) {
2012-09-11 10:15:55 +00:00
var name = make _node ( AST _SymbolRef , def . name , def . name ) ;
2012-09-05 10:43:34 +00:00
a . push ( make _node ( AST _Assign , def , {
operator : "=" ,
2012-09-11 10:15:55 +00:00
left : name ,
2012-09-05 10:43:34 +00:00
right : def . value
} ) ) ;
2017-03-16 04:03:30 +00:00
if ( reduce _vars ) name . definition ( ) . fixed = false ;
2012-09-05 10:43:34 +00:00
}
2017-09-03 09:23:31 +00:00
remove ( def . name . definition ( ) . orig , def . name ) ;
2012-09-05 10:43:34 +00:00
return a ;
} , [ ] ) ;
if ( assignments . length == 0 ) return null ;
2017-04-12 13:56:27 +00:00
return make _sequence ( this , assignments ) ;
2012-09-05 10:43:34 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Definitions , function ( self , compressor ) {
if ( self . definitions . length == 0 )
return make _node ( AST _EmptyStatement , self ) ;
return self ;
2012-08-21 12:45:05 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Call , function ( self , compressor ) {
2017-03-03 10:13:07 +00:00
var exp = self . expression ;
2017-06-15 19:21:38 +00:00
var fn = exp ;
2017-03-02 03:33:59 +00:00
if ( compressor . option ( "unused" )
2017-06-15 19:21:38 +00:00
&& ( fn instanceof AST _Function
|| compressor . option ( "reduce_vars" )
&& fn instanceof AST _SymbolRef
&& ( fn = fn . fixed _value ( ) ) instanceof AST _Function )
&& ! fn . uses _arguments
&& ! fn . uses _eval ) {
2017-03-09 11:11:05 +00:00
var pos = 0 , last = 0 ;
for ( var i = 0 , len = self . args . length ; i < len ; i ++ ) {
2017-06-15 19:21:38 +00:00
var trim = i >= fn . argnames . length ;
if ( trim || fn . argnames [ i ] . _ _unused ) {
2017-03-09 11:11:05 +00:00
var node = self . args [ i ] . drop _side _effect _free ( compressor ) ;
if ( node ) {
self . args [ pos ++ ] = node ;
} else if ( ! trim ) {
self . args [ pos ++ ] = make _node ( AST _Number , self . args [ i ] , {
value : 0
} ) ;
continue ;
}
} else {
self . args [ pos ++ ] = self . args [ i ] ;
2017-03-02 03:33:59 +00:00
}
2017-03-09 11:11:05 +00:00
last = pos ;
2017-03-02 03:33:59 +00:00
}
2017-03-09 11:11:05 +00:00
self . args . length = last ;
2017-03-02 03:33:59 +00:00
}
2012-09-10 12:54:17 +00:00
if ( compressor . option ( "unsafe" ) ) {
2017-07-15 07:16:11 +00:00
if ( is _undeclared _ref ( exp ) ) {
2012-09-10 12:54:17 +00:00
switch ( exp . name ) {
case "Array" :
2012-09-26 10:04:54 +00:00
if ( self . args . length != 1 ) {
return make _node ( AST _Array , self , {
elements : self . args
2017-03-18 18:17:15 +00:00
} ) . optimize ( compressor ) ;
2012-09-10 12:54:17 +00:00
}
break ;
case "Object" :
2012-09-26 10:04:54 +00:00
if ( self . args . length == 0 ) {
return make _node ( AST _Object , self , {
2012-09-10 12:54:17 +00:00
properties : [ ]
2012-09-26 13:43:14 +00:00
} ) ;
2012-09-10 12:54:17 +00:00
}
break ;
case "String" :
2013-01-17 09:01:38 +00:00
if ( self . args . length == 0 ) return make _node ( AST _String , self , {
value : ""
} ) ;
2013-10-24 09:08:33 +00:00
if ( self . args . length <= 1 ) return make _node ( AST _Binary , self , {
2012-09-26 10:04:54 +00:00
left : self . args [ 0 ] ,
2012-09-10 12:54:17 +00:00
operator : "+" ,
2012-09-26 10:04:54 +00:00
right : make _node ( AST _String , self , { value : "" } )
2017-03-18 18:17:15 +00:00
} ) . optimize ( compressor ) ;
2013-10-24 09:08:33 +00:00
break ;
case "Number" :
if ( self . args . length == 0 ) return make _node ( AST _Number , self , {
value : 0
2012-09-10 12:54:17 +00:00
} ) ;
2013-10-24 09:08:33 +00:00
if ( self . args . length == 1 ) return make _node ( AST _UnaryPrefix , self , {
expression : self . args [ 0 ] ,
operator : "+"
2017-03-18 18:17:15 +00:00
} ) . optimize ( compressor ) ;
2013-10-24 09:08:33 +00:00
case "Boolean" :
if ( self . args . length == 0 ) return make _node ( AST _False , self ) ;
if ( self . args . length == 1 ) return make _node ( AST _UnaryPrefix , self , {
2017-03-01 07:25:26 +00:00
expression : make _node ( AST _UnaryPrefix , self , {
2013-10-24 09:08:33 +00:00
expression : self . args [ 0 ] ,
operator : "!"
} ) ,
operator : "!"
2017-03-18 18:17:15 +00:00
} ) . optimize ( compressor ) ;
2013-10-24 09:08:33 +00:00
break ;
2012-09-10 12:54:17 +00:00
}
}
2012-09-26 10:04:54 +00:00
else if ( exp instanceof AST _Dot && exp . property == "toString" && self . args . length == 0 ) {
return make _node ( AST _Binary , self , {
2012-11-11 13:53:34 +00:00
left : make _node ( AST _String , self , { value : "" } ) ,
2012-09-10 12:54:17 +00:00
operator : "+" ,
2012-11-11 13:53:34 +00:00
right : exp . expression
2017-03-18 18:17:15 +00:00
} ) . optimize ( compressor ) ;
2012-09-10 12:54:17 +00:00
}
2013-09-22 11:54:32 +00:00
else if ( exp instanceof AST _Dot && exp . expression instanceof AST _Array && exp . property == "join" ) EXIT : {
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
var separator ;
if ( self . args . length > 0 ) {
separator = self . args [ 0 ] . evaluate ( compressor ) ;
2017-03-15 17:02:59 +00:00
if ( separator === self . args [ 0 ] ) break EXIT ; // not a constant
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
}
var elements = [ ] ;
var consts = [ ] ;
exp . expression . elements . forEach ( function ( el ) {
2017-03-15 17:02:59 +00:00
var value = el . evaluate ( compressor ) ;
if ( value !== el ) {
consts . push ( value ) ;
2013-09-22 11:54:32 +00:00
} else {
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
if ( consts . length > 0 ) {
elements . push ( make _node ( AST _String , self , {
value : consts . join ( separator )
} ) ) ;
consts . length = 0 ;
2013-09-22 11:54:32 +00:00
}
2017-03-15 17:02:59 +00:00
elements . push ( el ) ;
2013-09-22 11:54:32 +00:00
}
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
} ) ;
if ( consts . length > 0 ) {
elements . push ( make _node ( AST _String , self , {
value : consts . join ( separator )
} ) ) ;
}
2013-09-22 11:54:32 +00:00
if ( elements . length == 0 ) return make _node ( AST _String , self , { value : "" } ) ;
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
if ( elements . length == 1 ) {
if ( elements [ 0 ] . is _string ( compressor ) ) {
return elements [ 0 ] ;
}
return make _node ( AST _Binary , elements [ 0 ] , {
operator : "+" ,
left : make _node ( AST _String , self , { value : "" } ) ,
right : elements [ 0 ]
} ) ;
}
2013-09-22 11:54:32 +00:00
if ( separator == "" ) {
var first ;
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
if ( elements [ 0 ] . is _string ( compressor )
|| elements [ 1 ] . is _string ( compressor ) ) {
first = elements . shift ( ) ;
2013-09-22 11:54:32 +00:00
} else {
first = make _node ( AST _String , self , { value : "" } ) ;
}
return elements . reduce ( function ( prev , el ) {
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
return make _node ( AST _Binary , el , {
2013-09-22 11:54:32 +00:00
operator : "+" ,
left : prev ,
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
right : el
2013-09-22 11:54:32 +00:00
} ) ;
2017-03-18 18:17:15 +00:00
} , first ) . optimize ( compressor ) ;
2013-09-22 11:54:32 +00:00
}
// need this awkward cloning to not affect original element
// best_of will decide which one to get through.
var node = self . clone ( ) ;
node . expression = node . expression . clone ( ) ;
node . expression . expression = node . expression . expression . clone ( ) ;
fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"
improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c
closes #1453
2017-02-18 11:05:11 +00:00
node . expression . expression . elements = elements ;
2017-03-15 17:02:59 +00:00
return best _of ( compressor , self , node ) ;
2013-09-22 11:54:32 +00:00
}
2017-03-18 18:17:15 +00:00
else if ( exp instanceof AST _Dot && exp . expression . is _string ( compressor ) && exp . property == "charAt" ) {
var arg = self . args [ 0 ] ;
var index = arg ? arg . evaluate ( compressor ) : 0 ;
if ( index !== arg ) {
return make _node ( AST _Sub , exp , {
expression : exp . expression ,
property : make _node _from _constant ( index | 0 , arg || exp )
} ) . optimize ( compressor ) ;
}
}
2012-09-10 12:54:17 +00:00
}
2017-05-30 19:38:00 +00:00
if ( compressor . option ( "unsafe_Func" )
2017-07-15 07:16:11 +00:00
&& is _undeclared _ref ( exp )
2017-05-30 19:38:00 +00:00
&& exp . name == "Function" ) {
// new Function() => function(){}
if ( self . args . length == 0 ) return make _node ( AST _Function , self , {
argnames : [ ] ,
body : [ ]
} ) ;
if ( all ( self . args , function ( x ) {
return x instanceof AST _String ;
} ) ) {
// quite a corner-case, but we can handle it:
// https://github.com/mishoo/UglifyJS2/issues/203
// if the code argument is a constant, then we can minify it.
try {
2017-06-27 15:53:42 +00:00
var code = "n(function(" + self . args . slice ( 0 , - 1 ) . map ( function ( arg ) {
2017-05-30 19:38:00 +00:00
return arg . value ;
} ) . join ( "," ) + "){" + self . args [ self . args . length - 1 ] . value + "})" ;
var ast = parse ( code ) ;
var mangle = { ie8 : compressor . option ( "ie8" ) } ;
ast . figure _out _scope ( mangle ) ;
var comp = new Compressor ( compressor . options ) ;
ast = ast . transform ( comp ) ;
ast . figure _out _scope ( mangle ) ;
2017-06-23 07:53:13 +00:00
base54 . reset ( ) ;
ast . compute _char _frequency ( mangle ) ;
ast . mangle _names ( mangle ) ;
2017-05-30 19:38:00 +00:00
var fun ;
ast . walk ( new TreeWalker ( function ( node ) {
if ( fun ) return true ;
if ( node instanceof AST _Lambda ) {
fun = node ;
return true ;
}
} ) ) ;
var code = OutputStream ( ) ;
AST _BlockStatement . prototype . _codegen . call ( fun , fun , code ) ;
2017-06-27 15:53:42 +00:00
self . args = [
make _node ( AST _String , self , {
value : fun . argnames . map ( function ( arg ) {
return arg . print _to _string ( ) ;
} ) . join ( "," )
} ) ,
make _node ( AST _String , self . args [ self . args . length - 1 ] , {
value : code . get ( ) . replace ( /^\{|\}$/g , "" )
} )
] ;
2017-05-30 19:38:00 +00:00
return self ;
} catch ( ex ) {
if ( ex instanceof JS _Parse _Error ) {
compressor . warn ( "Error parsing code passed to new Function [{file}:{line},{col}]" , self . args [ self . args . length - 1 ] . start ) ;
compressor . warn ( ex . toString ( ) ) ;
} else {
throw ex ;
}
}
}
}
2017-06-15 19:21:38 +00:00
var stat = fn instanceof AST _Function && fn . body [ 0 ] ;
if ( compressor . option ( "inline" ) && stat instanceof AST _Return ) {
var value = stat . value ;
if ( ! value || value . is _constant _expression ( ) ) {
var args = self . args . concat ( value || make _node ( AST _Undefined , self ) ) ;
2017-07-01 20:28:11 +00:00
return make _sequence ( self , args ) . optimize ( compressor ) ;
2017-03-03 10:13:07 +00:00
}
2017-06-15 19:21:38 +00:00
}
if ( exp instanceof AST _Function ) {
2017-06-05 21:49:53 +00:00
if ( compressor . option ( "inline" )
&& ! exp . name
&& ! exp . uses _arguments
&& ! exp . uses _eval
2017-07-01 17:05:14 +00:00
&& exp . body . length == 1
&& all ( exp . argnames , function ( arg ) {
return arg . _ _unused ;
} )
2017-06-05 21:49:53 +00:00
&& ! self . has _pure _annotation ( compressor ) ) {
2017-06-12 17:40:14 +00:00
var value ;
2017-06-05 21:49:53 +00:00
if ( stat instanceof AST _Return ) {
2017-07-01 17:05:14 +00:00
value = stat . value ;
2017-06-05 21:49:53 +00:00
} else if ( stat instanceof AST _SimpleStatement ) {
2017-06-12 17:40:14 +00:00
value = make _node ( AST _UnaryPrefix , stat , {
operator : "void" ,
2017-07-01 17:05:14 +00:00
expression : stat . body
2017-06-12 17:40:14 +00:00
} ) ;
2017-06-05 21:49:53 +00:00
}
2017-06-12 17:40:14 +00:00
if ( value ) {
2017-07-01 17:05:14 +00:00
var tw = new TreeWalker ( function ( node ) {
if ( ! value ) return true ;
if ( node instanceof AST _SymbolRef ) {
var ref = node . scope . find _variable ( node ) ;
if ( ref && ref . scope . parent _scope === fn . parent _scope ) {
value = null ;
2017-06-12 17:40:14 +00:00
return true ;
}
2017-07-01 17:05:14 +00:00
}
if ( node instanceof AST _This && ! tw . find _parent ( AST _Scope ) ) {
value = null ;
return true ;
}
} ) ;
value . walk ( tw ) ;
}
if ( value ) {
var args = self . args . concat ( value ) ;
2017-07-01 20:28:11 +00:00
return make _sequence ( self , args ) . optimize ( compressor ) ;
2017-06-05 21:49:53 +00:00
}
}
2017-03-29 15:27:35 +00:00
if ( compressor . option ( "side_effects" ) && all ( exp . body , is _empty ) ) {
var args = self . args . concat ( make _node ( AST _Undefined , self ) ) ;
2017-07-01 20:28:11 +00:00
return make _sequence ( self , args ) . optimize ( compressor ) ;
2013-12-10 17:44:41 +00:00
}
}
if ( compressor . option ( "drop_console" ) ) {
2017-03-03 10:13:07 +00:00
if ( exp instanceof AST _PropAccess ) {
var name = exp . expression ;
2015-01-31 12:22:07 +00:00
while ( name . expression ) {
name = name . expression ;
}
2017-07-15 07:16:11 +00:00
if ( is _undeclared _ref ( name ) && name . name == "console" ) {
2017-04-02 06:52:25 +00:00
return make _node ( AST _Undefined , self ) . optimize ( compressor ) ;
2015-01-31 12:22:07 +00:00
}
2012-10-05 13:51:16 +00:00
}
}
2017-02-18 11:11:57 +00:00
if ( compressor . option ( "negate_iife" )
&& compressor . parent ( ) instanceof AST _SimpleStatement
&& is _iife _call ( self ) ) {
return self . negate ( compressor , true ) ;
}
2017-05-31 16:56:28 +00:00
var ev = self . evaluate ( compressor ) ;
if ( ev !== self ) {
ev = make _node _from _constant ( ev , self ) . optimize ( compressor ) ;
return best _of ( compressor , ev , self ) ;
}
2017-02-18 11:11:57 +00:00
return self ;
2012-09-10 12:54:17 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _New , function ( self , compressor ) {
2012-09-10 12:54:17 +00:00
if ( compressor . option ( "unsafe" ) ) {
2012-09-26 10:04:54 +00:00
var exp = self . expression ;
2017-07-15 07:16:11 +00:00
if ( is _undeclared _ref ( exp ) ) {
2012-09-10 12:54:17 +00:00
switch ( exp . name ) {
case "Object" :
case "RegExp" :
case "Function" :
case "Error" :
case "Array" :
2012-12-21 19:04:15 +00:00
return make _node ( AST _Call , self , self ) . transform ( compressor ) ;
2012-09-10 12:54:17 +00:00
}
}
}
2012-09-26 10:04:54 +00:00
return self ;
2012-08-21 12:45:05 +00:00
} ) ;
2017-04-12 13:56:27 +00:00
OPT ( AST _Sequence , function ( self , compressor ) {
if ( ! compressor . option ( "side_effects" ) ) return self ;
var expressions = [ ] ;
filter _for _side _effects ( ) ;
var end = expressions . length - 1 ;
trim _right _for _undefined ( ) ;
if ( end > 0 && compressor . option ( "cascade" ) ) trim _left _for _assignment ( ) ;
if ( end == 0 ) {
self = maintain _this _binding ( compressor . parent ( ) , self , expressions [ 0 ] ) ;
if ( ! ( self instanceof AST _Sequence ) ) self = self . optimize ( compressor ) ;
2012-10-22 08:49:58 +00:00
return self ;
2017-04-12 13:56:27 +00:00
}
self . expressions = expressions ;
return self ;
function filter _for _side _effects ( ) {
var first = first _in _statement ( compressor ) ;
var last = self . expressions . length - 1 ;
self . expressions . forEach ( function ( expr , index ) {
if ( index < last ) expr = expr . drop _side _effect _free ( compressor , first ) ;
if ( expr ) {
merge _sequence ( expressions , expr ) ;
first = false ;
}
} ) ;
}
function trim _right _for _undefined ( ) {
while ( end > 0 && is _undefined ( expressions [ end ] , compressor ) ) end -- ;
if ( end < expressions . length - 1 ) {
expressions [ end ] = make _node ( AST _UnaryPrefix , self , {
operator : "void" ,
expression : expressions [ end ]
} ) ;
expressions . length = end + 1 ;
}
}
function trim _left _for _assignment ( ) {
for ( var i = 0 , j = 1 ; j <= end ; j ++ ) {
var left = expressions [ i ] ;
var cdr = expressions [ j ] ;
if ( left instanceof AST _Assign
&& ! left . left . has _side _effects ( compressor ) ) {
left = left . left ;
} else if ( left instanceof AST _Unary
&& ( left . operator == "++" || left . operator == "--" ) ) {
left = left . expression ;
} else left = null ;
2017-05-12 06:57:41 +00:00
if ( ! left || is _lhs _read _only ( left ) ) {
2017-04-12 13:56:27 +00:00
expressions [ ++ i ] = cdr ;
continue ;
}
var parent = null , field ;
2017-06-07 11:52:01 +00:00
expressions [ j ] = cdr = cdr . clone ( ) ;
2017-03-05 06:56:14 +00:00
while ( true ) {
if ( cdr . equivalent _to ( left ) ) {
2017-04-12 13:56:27 +00:00
var car = expressions [ i ] ;
if ( car instanceof AST _UnaryPostfix ) {
car = make _node ( AST _UnaryPrefix , car , {
operator : car . operator ,
expression : left
} ) ;
2017-07-14 11:52:01 +00:00
} else {
car . write _only = false ;
}
2017-03-05 06:56:14 +00:00
if ( parent ) {
2017-03-05 09:15:37 +00:00
parent [ field ] = car ;
2017-04-12 13:56:27 +00:00
expressions [ i ] = expressions [ j ] ;
} else {
expressions [ i ] = car ;
2017-03-05 06:56:14 +00:00
}
2017-04-12 13:56:27 +00:00
break ;
2017-03-05 06:56:14 +00:00
}
if ( cdr instanceof AST _Binary && ! ( cdr instanceof AST _Assign ) ) {
2017-03-24 06:30:31 +00:00
if ( cdr . left . is _constant ( ) ) {
2017-04-12 13:56:27 +00:00
if ( cdr . operator == "||" || cdr . operator == "&&" ) {
expressions [ ++ i ] = expressions [ j ] ;
break ;
}
2017-03-24 06:30:31 +00:00
field = "right" ;
} else {
field = "left" ;
}
2017-03-05 06:56:14 +00:00
} else if ( cdr instanceof AST _Call
2017-06-16 13:18:43 +00:00
&& ! ( left instanceof AST _PropAccess && cdr . expression . equivalent _to ( left ) )
2017-04-18 20:17:15 +00:00
|| cdr instanceof AST _PropAccess
2017-03-26 11:14:30 +00:00
|| cdr instanceof AST _Unary && ! unary _side _effects ( cdr . operator ) ) {
2017-03-05 06:56:14 +00:00
field = "expression" ;
2017-06-24 13:30:06 +00:00
} else if ( cdr instanceof AST _Conditional ) {
field = "condition" ;
2017-04-12 13:56:27 +00:00
} else {
expressions [ ++ i ] = expressions [ j ] ;
break ;
}
2017-03-05 06:56:14 +00:00
parent = cdr ;
2017-06-07 11:52:01 +00:00
cdr = cdr [ field ] = cdr [ field ] . clone ( ) ;
2013-12-10 17:39:03 +00:00
}
2012-09-14 12:36:38 +00:00
}
2017-04-12 13:56:27 +00:00
end = i ;
expressions . length = end + 1 ;
2012-09-14 12:36:38 +00:00
}
2012-08-21 12:45:05 +00:00
} ) ;
2012-10-22 08:49:58 +00:00
AST _Unary . DEFMETHOD ( "lift_sequences" , function ( compressor ) {
if ( compressor . option ( "sequences" ) ) {
2017-04-12 13:56:27 +00:00
if ( this . expression instanceof AST _Sequence ) {
var x = this . expression . expressions . slice ( ) ;
2017-04-06 19:42:17 +00:00
var e = this . clone ( ) ;
e . expression = x . pop ( ) ;
x . push ( e ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( this , x ) . optimize ( compressor ) ;
2012-10-22 08:49:58 +00:00
}
}
return this ;
} ) ;
OPT ( AST _UnaryPostfix , function ( self , compressor ) {
return self . lift _sequences ( compressor ) ;
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _UnaryPrefix , function ( self , compressor ) {
2017-04-08 06:25:28 +00:00
var e = self . expression ;
if ( self . operator == "delete"
&& ! ( e instanceof AST _SymbolRef
|| e instanceof AST _PropAccess
|| e instanceof AST _NaN
|| e instanceof AST _Infinity
|| e instanceof AST _Undefined ) ) {
2017-04-12 13:56:27 +00:00
if ( e instanceof AST _Sequence ) {
e = e . expressions . slice ( ) ;
2017-04-08 06:25:28 +00:00
e . push ( make _node ( AST _True , self ) ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( self , e ) . optimize ( compressor ) ;
2017-04-08 06:25:28 +00:00
}
2017-04-12 13:56:27 +00:00
return make _sequence ( self , [ e , make _node ( AST _True , self ) ] ) . optimize ( compressor ) ;
2017-04-08 06:25:28 +00:00
}
2017-02-27 18:25:44 +00:00
var seq = self . lift _sequences ( compressor ) ;
if ( seq !== self ) {
return seq ;
}
if ( compressor . option ( "side_effects" ) && self . operator == "void" ) {
e = e . drop _side _effect _free ( compressor ) ;
if ( e ) {
self . expression = e ;
return self ;
} else {
2017-03-31 19:02:14 +00:00
return make _node ( AST _Undefined , self ) . optimize ( compressor ) ;
2017-02-27 18:25:44 +00:00
}
}
2012-09-07 12:18:32 +00:00
if ( compressor . option ( "booleans" ) && compressor . in _boolean _context ( ) ) {
2012-09-17 08:50:35 +00:00
switch ( self . operator ) {
2012-09-04 10:20:28 +00:00
case "!" :
if ( e instanceof AST _UnaryPrefix && e . operator == "!" ) {
// !!foo ==> foo, if we're in boolean context
return e . expression ;
}
2017-02-18 11:11:57 +00:00
if ( e instanceof AST _Binary ) {
2017-03-15 17:02:59 +00:00
self = best _of ( compressor , self , e . negate ( compressor , first _in _statement ( compressor ) ) ) ;
2017-02-18 11:11:57 +00:00
}
2012-09-04 10:20:28 +00:00
break ;
case "typeof" :
// typeof always returns a non-empty string, thus it's
// always true in booleans
2012-09-21 11:38:52 +00:00
compressor . warn ( "Boolean expression always true [{file}:{line},{col}]" , self . start ) ;
2017-04-12 13:56:27 +00:00
return ( e instanceof AST _SymbolRef ? make _node ( AST _True , self ) : make _sequence ( self , [
e ,
make _node ( AST _True , self )
] ) ) . optimize ( compressor ) ;
2012-09-04 10:20:28 +00:00
}
2012-09-17 08:50:35 +00:00
}
2017-03-31 19:02:14 +00:00
if ( self . operator == "-" && e instanceof AST _Infinity ) {
e = e . transform ( compressor ) ;
}
2017-03-30 08:09:00 +00:00
if ( e instanceof AST _Binary
&& ( self . operator == "+" || self . operator == "-" )
&& ( e . operator == "*" || e . operator == "/" || e . operator == "%" ) ) {
2017-04-06 19:42:17 +00:00
return make _node ( AST _Binary , self , {
operator : e . operator ,
left : make _node ( AST _UnaryPrefix , e . left , {
operator : self . operator ,
expression : e . left
} ) ,
right : e . right
} ) ;
2017-03-30 08:09:00 +00:00
}
2017-03-15 17:02:59 +00:00
// avoids infinite recursion of numerals
2017-03-29 10:31:55 +00:00
if ( self . operator != "-"
2017-03-31 19:02:14 +00:00
|| ! ( e instanceof AST _Number || e instanceof AST _Infinity ) ) {
2017-03-15 17:02:59 +00:00
var ev = self . evaluate ( compressor ) ;
if ( ev !== self ) {
ev = make _node _from _constant ( ev , self ) . optimize ( compressor ) ;
return best _of ( compressor , ev , self ) ;
}
}
return self ;
2012-09-03 12:47:15 +00:00
} ) ;
2012-10-22 08:49:58 +00:00
AST _Binary . DEFMETHOD ( "lift_sequences" , function ( compressor ) {
if ( compressor . option ( "sequences" ) ) {
2017-04-12 13:56:27 +00:00
if ( this . left instanceof AST _Sequence ) {
var x = this . left . expressions . slice ( ) ;
2017-04-06 19:42:17 +00:00
var e = this . clone ( ) ;
e . left = x . pop ( ) ;
x . push ( e ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( this , x ) . optimize ( compressor ) ;
2017-03-18 19:04:22 +00:00
}
2017-04-12 13:56:27 +00:00
if ( this . right instanceof AST _Sequence && ! this . left . has _side _effects ( compressor ) ) {
2017-03-18 19:04:22 +00:00
var assign = this . operator == "=" && this . left instanceof AST _SymbolRef ;
2017-04-12 13:56:27 +00:00
var x = this . right . expressions ;
var last = x . length - 1 ;
for ( var i = 0 ; i < last ; i ++ ) {
if ( ! assign && x [ i ] . has _side _effects ( compressor ) ) break ;
}
if ( i == last ) {
x = x . slice ( ) ;
2017-04-06 19:42:17 +00:00
var e = this . clone ( ) ;
2017-04-12 13:56:27 +00:00
e . right = x . pop ( ) ;
x . push ( e ) ;
return make _sequence ( this , x ) . optimize ( compressor ) ;
} else if ( i > 0 ) {
var e = this . clone ( ) ;
e . right = make _sequence ( this . right , x . slice ( i ) ) ;
x = x . slice ( 0 , i ) ;
x . push ( e ) ;
return make _sequence ( this , x ) . optimize ( compressor ) ;
2017-03-18 19:04:22 +00:00
}
2013-11-06 08:47:36 +00:00
}
2012-10-22 08:49:58 +00:00
}
return this ;
} ) ;
2012-11-05 14:01:09 +00:00
var commutativeOperators = makePredicate ( "== === != !== * & | ^" ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Binary , function ( self , compressor ) {
2017-03-03 10:04:32 +00:00
function reversible ( ) {
2017-03-31 19:02:14 +00:00
return self . left . is _constant ( )
|| self . right . is _constant ( )
2017-03-03 10:04:32 +00:00
|| ! self . left . has _side _effects ( compressor )
&& ! self . right . has _side _effects ( compressor ) ;
}
function reverse ( op ) {
if ( reversible ( ) ) {
2015-10-05 23:51:09 +00:00
if ( op ) self . operator = op ;
var tmp = self . left ;
self . left = self . right ;
self . right = tmp ;
}
}
2012-11-05 14:01:09 +00:00
if ( commutativeOperators ( self . operator ) ) {
2017-03-31 19:02:14 +00:00
if ( self . right . is _constant ( )
&& ! self . left . is _constant ( ) ) {
2013-05-08 13:22:39 +00:00
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
2013-10-29 20:37:36 +00:00
if ( ! ( self . left instanceof AST _Binary
2013-10-30 08:44:50 +00:00
&& PRECEDENCE [ self . left . operator ] >= PRECEDENCE [ self . operator ] ) ) {
2017-03-03 10:04:32 +00:00
reverse ( ) ;
2013-10-29 20:37:36 +00:00
}
2012-11-05 14:01:09 +00:00
}
}
2012-10-22 08:49:58 +00:00
self = self . lift _sequences ( compressor ) ;
2012-10-03 08:27:05 +00:00
if ( compressor . option ( "comparisons" ) ) switch ( self . operator ) {
2012-09-10 12:54:17 +00:00
case "===" :
case "!==" :
2012-11-13 12:32:07 +00:00
if ( ( self . left . is _string ( compressor ) && self . right . is _string ( compressor ) ) ||
2017-03-03 10:04:32 +00:00
( self . left . is _number ( compressor ) && self . right . is _number ( compressor ) ) ||
2012-09-26 10:04:54 +00:00
( self . left . is _boolean ( ) && self . right . is _boolean ( ) ) ) {
self . operator = self . operator . substr ( 0 , 2 ) ;
2012-09-10 12:54:17 +00:00
}
2012-09-14 16:56:59 +00:00
// XXX: intentionally falling down to the next case
case "==" :
case "!=" :
2017-02-18 11:01:42 +00:00
// "undefined" == typeof x => undefined === x
2017-07-05 11:20:33 +00:00
if ( compressor . option ( "typeofs" )
&& self . left instanceof AST _String
2013-05-08 13:22:39 +00:00
&& self . left . value == "undefined"
&& self . right instanceof AST _UnaryPrefix
2017-02-18 11:01:42 +00:00
&& self . right . operator == "typeof" ) {
var expr = self . right . expression ;
2017-07-15 07:16:11 +00:00
if ( expr instanceof AST _SymbolRef ? expr . is _declared ( compressor )
2017-04-15 15:50:50 +00:00
: ! ( expr instanceof AST _PropAccess && compressor . option ( "ie8" ) ) ) {
2017-02-18 11:01:42 +00:00
self . right = expr ;
2013-05-08 13:22:39 +00:00
self . left = make _node ( AST _Undefined , self . left ) . optimize ( compressor ) ;
2012-09-26 10:04:54 +00:00
if ( self . operator . length == 2 ) self . operator += "=" ;
2012-09-17 15:49:52 +00:00
}
2012-09-14 16:56:59 +00:00
}
2012-09-10 12:54:17 +00:00
break ;
}
2017-03-31 10:47:44 +00:00
if ( compressor . option ( "booleans" ) && self . operator == "+" && compressor . in _boolean _context ( ) ) {
2012-10-04 05:49:18 +00:00
var ll = self . left . evaluate ( compressor ) ;
var rr = self . right . evaluate ( compressor ) ;
2017-03-15 17:02:59 +00:00
if ( ll && typeof ll == "string" ) {
2017-02-27 18:25:44 +00:00
compressor . warn ( "+ in boolean context always true [{file}:{line},{col}]" , self . start ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( self , [
self . right ,
make _node ( AST _True , self )
] ) . optimize ( compressor ) ;
2017-02-27 18:25:44 +00:00
}
2017-03-15 17:02:59 +00:00
if ( rr && typeof rr == "string" ) {
2012-09-26 10:04:54 +00:00
compressor . warn ( "+ in boolean context always true [{file}:{line},{col}]" , self . start ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( self , [
self . left ,
make _node ( AST _True , self )
] ) . optimize ( compressor ) ;
2012-09-04 10:20:28 +00:00
}
}
2015-07-22 13:53:25 +00:00
if ( compressor . option ( "comparisons" ) && self . is _boolean ( ) ) {
2012-10-03 08:34:05 +00:00
if ( ! ( compressor . parent ( ) instanceof AST _Binary )
|| compressor . parent ( ) instanceof AST _Assign ) {
2012-09-25 15:03:31 +00:00
var negated = make _node ( AST _UnaryPrefix , self , {
operator : "!" ,
2017-03-15 17:02:59 +00:00
expression : self . negate ( compressor , first _in _statement ( compressor ) )
2012-09-25 15:03:31 +00:00
} ) ;
2017-03-15 17:02:59 +00:00
self = best _of ( compressor , self , negated ) ;
2012-09-25 15:03:31 +00:00
}
2016-06-13 16:19:06 +00:00
if ( compressor . option ( "unsafe_comps" ) ) {
switch ( self . operator ) {
case "<" : reverse ( ">" ) ; break ;
case "<=" : reverse ( ">=" ) ; break ;
}
2012-09-12 10:23:24 +00:00
}
}
2017-03-02 03:31:39 +00:00
if ( self . operator == "+" ) {
if ( self . right instanceof AST _String
&& self . right . getValue ( ) == ""
&& self . left . is _string ( compressor ) ) {
return self . left ;
}
if ( self . left instanceof AST _String
&& self . left . getValue ( ) == ""
&& self . right . is _string ( compressor ) ) {
return self . right ;
}
if ( self . left instanceof AST _Binary
&& self . left . operator == "+"
&& self . left . left instanceof AST _String
&& self . left . left . getValue ( ) == ""
&& self . right . is _string ( compressor ) ) {
self . left = self . left . right ;
return self . transform ( compressor ) ;
}
2012-11-12 14:41:03 +00:00
}
2013-09-22 11:54:32 +00:00
if ( compressor . option ( "evaluate" ) ) {
2017-01-26 11:16:50 +00:00
switch ( self . operator ) {
case "&&" :
2017-03-31 10:47:44 +00:00
var ll = self . left . evaluate ( compressor ) ;
if ( ! ll ) {
compressor . warn ( "Condition left of && always false [{file}:{line},{col}]" , self . start ) ;
return maintain _this _binding ( compressor . parent ( ) , self , self . left ) . optimize ( compressor ) ;
} else if ( ll !== self . left ) {
compressor . warn ( "Condition left of && always true [{file}:{line},{col}]" , self . start ) ;
return maintain _this _binding ( compressor . parent ( ) , self , self . right ) . optimize ( compressor ) ;
}
if ( compressor . option ( "booleans" ) && compressor . in _boolean _context ( ) ) {
var rr = self . right . evaluate ( compressor ) ;
if ( ! rr ) {
compressor . warn ( "Boolean && always false [{file}:{line},{col}]" , self . start ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( self , [
self . left ,
make _node ( AST _False , self )
] ) . optimize ( compressor ) ;
2017-03-31 10:47:44 +00:00
} else if ( rr !== self . right ) {
compressor . warn ( "Dropping side-effect-free && in boolean context [{file}:{line},{col}]" , self . start ) ;
return self . left . optimize ( compressor ) ;
2017-01-26 11:16:50 +00:00
}
}
break ;
case "||" :
2017-03-31 10:47:44 +00:00
var ll = self . left . evaluate ( compressor ) ;
if ( ! ll ) {
compressor . warn ( "Condition left of || always false [{file}:{line},{col}]" , self . start ) ;
return maintain _this _binding ( compressor . parent ( ) , self , self . right ) . optimize ( compressor ) ;
} else if ( ll !== self . left ) {
compressor . warn ( "Condition left of || always true [{file}:{line},{col}]" , self . start ) ;
return maintain _this _binding ( compressor . parent ( ) , self , self . left ) . optimize ( compressor ) ;
}
if ( compressor . option ( "booleans" ) && compressor . in _boolean _context ( ) ) {
var rr = self . right . evaluate ( compressor ) ;
if ( ! rr ) {
compressor . warn ( "Dropping side-effect-free || in boolean context [{file}:{line},{col}]" , self . start ) ;
return self . left . optimize ( compressor ) ;
} else if ( rr !== self . right ) {
compressor . warn ( "Boolean || always true [{file}:{line},{col}]" , self . start ) ;
2017-04-12 13:56:27 +00:00
return make _sequence ( self , [
self . left ,
make _node ( AST _True , self )
] ) . optimize ( compressor ) ;
2017-01-26 11:16:50 +00:00
}
}
break ;
}
2017-03-03 10:04:32 +00:00
var associative = true ;
switch ( self . operator ) {
case "+" :
// "foo" + ("bar" + x) => "foobar" + x
2013-09-22 11:54:32 +00:00
if ( self . left instanceof AST _Constant
&& self . right instanceof AST _Binary
&& self . right . operator == "+"
&& self . right . left instanceof AST _Constant
&& self . right . is _string ( compressor ) ) {
self = make _node ( AST _Binary , self , {
operator : "+" ,
2017-03-03 10:04:32 +00:00
left : make _node ( AST _String , self . left , {
2013-09-22 12:26:10 +00:00
value : "" + self . left . getValue ( ) + self . right . left . getValue ( ) ,
2013-09-22 11:54:32 +00:00
start : self . left . start ,
end : self . right . left . end
} ) ,
right : self . right . right
} ) ;
}
2017-03-03 10:04:32 +00:00
// (x + "foo") + "bar" => x + "foobar"
2013-09-22 11:54:32 +00:00
if ( self . right instanceof AST _Constant
&& self . left instanceof AST _Binary
&& self . left . operator == "+"
&& self . left . right instanceof AST _Constant
&& self . left . is _string ( compressor ) ) {
self = make _node ( AST _Binary , self , {
operator : "+" ,
left : self . left . left ,
2017-03-03 10:04:32 +00:00
right : make _node ( AST _String , self . right , {
2013-09-22 12:26:10 +00:00
value : "" + self . left . right . getValue ( ) + self . right . getValue ( ) ,
2013-09-22 11:54:32 +00:00
start : self . left . right . start ,
end : self . right . end
} )
} ) ;
}
2017-03-03 10:04:32 +00:00
// (x + "foo") + ("bar" + y) => (x + "foobar") + y
2013-09-22 12:26:10 +00:00
if ( self . left instanceof AST _Binary
&& self . left . operator == "+"
&& self . left . is _string ( compressor )
&& self . left . right instanceof AST _Constant
&& self . right instanceof AST _Binary
&& self . right . operator == "+"
2013-10-26 16:44:52 +00:00
&& self . right . left instanceof AST _Constant
&& self . right . is _string ( compressor ) ) {
2013-09-22 12:26:10 +00:00
self = make _node ( AST _Binary , self , {
operator : "+" ,
left : make _node ( AST _Binary , self . left , {
operator : "+" ,
left : self . left . left ,
2017-03-03 10:04:32 +00:00
right : make _node ( AST _String , self . left . right , {
2013-09-22 12:26:10 +00:00
value : "" + self . left . right . getValue ( ) + self . right . left . getValue ( ) ,
start : self . left . right . start ,
end : self . right . left . end
} )
} ) ,
right : self . right . right
} ) ;
}
2017-03-03 10:04:32 +00:00
// a + -b => a - b
if ( self . right instanceof AST _UnaryPrefix
&& self . right . operator == "-"
&& self . left . is _number ( compressor ) ) {
self = make _node ( AST _Binary , self , {
operator : "-" ,
left : self . left ,
right : self . right . expression
} ) ;
2017-03-24 14:09:19 +00:00
break ;
2017-03-03 10:04:32 +00:00
}
// -a + b => b - a
if ( self . left instanceof AST _UnaryPrefix
&& self . left . operator == "-"
&& reversible ( )
&& self . right . is _number ( compressor ) ) {
self = make _node ( AST _Binary , self , {
operator : "-" ,
left : self . right ,
right : self . left . expression
} ) ;
2017-03-24 14:09:19 +00:00
break ;
2017-03-03 10:04:32 +00:00
}
case "*" :
associative = compressor . option ( "unsafe_math" ) ;
case "&" :
case "|" :
case "^" :
// a + +b => +b + a
if ( self . left . is _number ( compressor )
&& self . right . is _number ( compressor )
&& reversible ( )
&& ! ( self . left instanceof AST _Binary
&& self . left . operator != self . operator
&& PRECEDENCE [ self . left . operator ] >= PRECEDENCE [ self . operator ] ) ) {
var reversed = make _node ( AST _Binary , self , {
operator : self . operator ,
left : self . right ,
right : self . left
} ) ;
if ( self . right instanceof AST _Constant
&& ! ( self . left instanceof AST _Constant ) ) {
2017-03-15 17:02:59 +00:00
self = best _of ( compressor , reversed , self ) ;
2017-03-03 10:04:32 +00:00
} else {
2017-03-15 17:02:59 +00:00
self = best _of ( compressor , self , reversed ) ;
2017-03-03 10:04:32 +00:00
}
}
if ( associative && self . is _number ( compressor ) ) {
// a + (b + c) => (a + b) + c
if ( self . right instanceof AST _Binary
&& self . right . operator == self . operator ) {
self = make _node ( AST _Binary , self , {
operator : self . operator ,
left : make _node ( AST _Binary , self . left , {
operator : self . operator ,
left : self . left ,
right : self . right . left ,
start : self . left . start ,
end : self . right . left . end
} ) ,
right : self . right . right
} ) ;
}
// (n + 2) + 3 => 5 + n
// (2 * n) * 3 => 6 + n
if ( self . right instanceof AST _Constant
&& self . left instanceof AST _Binary
&& self . left . operator == self . operator ) {
if ( self . left . left instanceof AST _Constant ) {
self = make _node ( AST _Binary , self , {
operator : self . operator ,
left : make _node ( AST _Binary , self . left , {
operator : self . operator ,
left : self . left . left ,
right : self . right ,
start : self . left . left . start ,
end : self . right . end
} ) ,
right : self . left . right
} ) ;
} else if ( self . left . right instanceof AST _Constant ) {
self = make _node ( AST _Binary , self , {
operator : self . operator ,
left : make _node ( AST _Binary , self . left , {
operator : self . operator ,
left : self . left . right ,
right : self . right ,
start : self . left . right . start ,
end : self . right . end
} ) ,
right : self . left . left
} ) ;
}
}
// (a | 1) | (2 | d) => (3 | a) | b
if ( self . left instanceof AST _Binary
&& self . left . operator == self . operator
&& self . left . right instanceof AST _Constant
&& self . right instanceof AST _Binary
&& self . right . operator == self . operator
&& self . right . left instanceof AST _Constant ) {
self = make _node ( AST _Binary , self , {
operator : self . operator ,
left : make _node ( AST _Binary , self . left , {
operator : self . operator ,
left : make _node ( AST _Binary , self . left . left , {
operator : self . operator ,
left : self . left . right ,
right : self . right . left ,
start : self . left . right . start ,
end : self . right . left . end
} ) ,
right : self . left . left
} ) ,
right : self . right . right
} ) ;
}
}
2013-09-22 11:54:32 +00:00
}
}
2015-06-30 07:07:13 +00:00
// x && (y && z) ==> x && y && z
// x || (y || z) ==> x || y || z
2017-02-18 11:07:52 +00:00
// x + ("y" + z) ==> x + "y" + z
// "x" + (y + "z")==> "x" + y + "z"
2013-10-30 08:45:58 +00:00
if ( self . right instanceof AST _Binary
&& self . right . operator == self . operator
2017-02-18 11:07:52 +00:00
&& ( self . operator == "&&"
|| self . operator == "||"
|| ( self . operator == "+"
&& ( self . right . left . is _string ( compressor )
|| ( self . left . is _string ( compressor )
&& self . right . right . is _string ( compressor ) ) ) ) ) )
2013-10-30 08:45:58 +00:00
{
self . left = make _node ( AST _Binary , self . left , {
operator : self . operator ,
left : self . left ,
right : self . right . left
} ) ;
self . right = self . right . right ;
return self . transform ( compressor ) ;
}
2017-03-15 17:02:59 +00:00
var ev = self . evaluate ( compressor ) ;
if ( ev !== self ) {
ev = make _node _from _constant ( ev , self ) . optimize ( compressor ) ;
return best _of ( compressor , ev , self ) ;
}
return self ;
2012-09-03 12:47:15 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _SymbolRef , function ( self , compressor ) {
2017-02-18 11:27:31 +00:00
var def = self . resolve _defines ( compressor ) ;
if ( def ) {
2017-04-08 08:46:25 +00:00
return def . optimize ( compressor ) ;
2017-02-18 11:27:31 +00:00
}
// testing against !self.scope.uses_with first is an optimization
2017-04-15 15:50:50 +00:00
if ( ! compressor . option ( "ie8" )
2017-07-15 07:16:11 +00:00
&& is _undeclared _ref ( self )
2017-02-18 11:27:31 +00:00
&& ( ! self . scope . uses _with || ! compressor . find _parent ( AST _With ) ) ) {
switch ( self . name ) {
case "undefined" :
2017-03-15 17:02:59 +00:00
return make _node ( AST _Undefined , self ) . optimize ( compressor ) ;
2017-02-18 11:27:31 +00:00
case "NaN" :
2017-03-31 19:02:14 +00:00
return make _node ( AST _NaN , self ) . optimize ( compressor ) ;
2017-02-18 11:27:31 +00:00
case "Infinity" :
2017-03-31 19:02:14 +00:00
return make _node ( AST _Infinity , self ) . optimize ( compressor ) ;
2012-10-02 10:20:07 +00:00
}
2012-09-14 16:04:18 +00:00
}
2017-06-05 21:49:53 +00:00
if ( compressor . option ( "reduce_vars" )
2017-04-16 09:25:39 +00:00
&& is _lhs ( self , compressor . parent ( ) ) !== self ) {
2017-02-18 11:12:57 +00:00
var d = self . definition ( ) ;
2017-04-05 13:06:42 +00:00
var fixed = self . fixed _value ( ) ;
2017-06-05 21:49:53 +00:00
if ( fixed instanceof AST _Defun ) {
2017-06-22 22:59:53 +00:00
d . fixed = fixed = make _node ( AST _Function , fixed , fixed ) ;
2017-06-05 21:49:53 +00:00
}
if ( compressor . option ( "unused" )
&& fixed instanceof AST _Function
&& d . references . length == 1
&& ! ( d . scope . uses _arguments && d . orig [ 0 ] instanceof AST _SymbolFunarg )
&& ! d . scope . uses _eval
&& compressor . find _parent ( AST _Scope ) === fixed . parent _scope ) {
2017-06-22 22:59:53 +00:00
return fixed . clone ( true ) ;
2017-06-05 21:49:53 +00:00
}
if ( compressor . option ( "evaluate" ) && fixed ) {
2017-02-25 16:40:33 +00:00
if ( d . should _replace === undefined ) {
2017-04-05 13:06:42 +00:00
var init = fixed . evaluate ( compressor ) ;
2017-05-19 01:06:29 +00:00
if ( init !== fixed && ( compressor . option ( "unsafe_regexp" ) || ! ( init instanceof RegExp ) ) ) {
2017-04-06 19:42:17 +00:00
init = make _node _from _constant ( init , fixed ) ;
2017-05-02 12:47:10 +00:00
var value _length = init . optimize ( compressor ) . print _to _string ( ) . length ;
2017-04-16 09:25:39 +00:00
var fn ;
if ( has _symbol _ref ( fixed ) ) {
fn = function ( ) {
var result = init . optimize ( compressor ) ;
return result === init ? result . clone ( true ) : result ;
} ;
} else {
2017-05-02 12:47:10 +00:00
value _length = Math . min ( value _length , fixed . print _to _string ( ) . length ) ;
2017-04-16 09:25:39 +00:00
fn = function ( ) {
var result = best _of _expression ( init . optimize ( compressor ) , fixed ) ;
return result === init || result === fixed ? result . clone ( true ) : result ;
} ;
}
2017-05-02 12:47:10 +00:00
var name _length = d . name . length ;
2017-04-16 09:25:39 +00:00
var overhead = 0 ;
2017-06-23 05:11:40 +00:00
if ( compressor . option ( "unused" ) && ! compressor . exposed ( d ) ) {
2017-05-02 12:47:10 +00:00
overhead = ( name _length + 2 + value _length ) / d . references . length ;
2017-04-16 09:25:39 +00:00
}
2017-05-02 12:47:10 +00:00
d . should _replace = value _length <= name _length + overhead ? fn : false ;
2017-02-25 16:40:33 +00:00
} else {
d . should _replace = false ;
}
}
if ( d . should _replace ) {
2017-04-16 09:25:39 +00:00
return d . should _replace ( ) ;
2017-02-25 16:40:33 +00:00
}
2017-02-18 11:12:57 +00:00
}
}
2012-09-26 10:04:54 +00:00
return self ;
2017-04-16 09:25:39 +00:00
function has _symbol _ref ( value ) {
var found ;
value . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _SymbolRef ) found = true ;
if ( found ) return true ;
} ) ) ;
return found ;
}
2012-09-14 16:04:18 +00:00
} ) ;
2017-04-08 19:18:14 +00:00
function is _atomic ( lhs , self ) {
return lhs instanceof AST _SymbolRef || lhs . TYPE === self . TYPE ;
2017-04-08 06:25:28 +00:00
}
2012-09-26 10:04:54 +00:00
OPT ( AST _Undefined , function ( self , compressor ) {
2012-09-17 09:24:21 +00:00
if ( compressor . option ( "unsafe" ) ) {
2017-04-02 08:14:09 +00:00
var undef = find _variable ( compressor , "undefined" ) ;
2012-09-17 09:24:21 +00:00
if ( undef ) {
2017-03-05 05:09:27 +00:00
var ref = make _node ( AST _SymbolRef , self , {
2012-09-17 09:24:21 +00:00
name : "undefined" ,
2017-04-02 08:14:09 +00:00
scope : undef . scope ,
2012-09-17 09:24:21 +00:00
thedef : undef
} ) ;
2017-03-05 05:09:27 +00:00
ref . is _undefined = true ;
return ref ;
2012-09-17 09:24:21 +00:00
}
}
2017-04-08 19:18:14 +00:00
var lhs = is _lhs ( compressor . self ( ) , compressor . parent ( ) ) ;
if ( lhs && is _atomic ( lhs , self ) ) return self ;
2017-03-31 19:02:14 +00:00
return make _node ( AST _UnaryPrefix , self , {
operator : "void" ,
expression : make _node ( AST _Number , self , {
value : 0
} )
} ) ;
} ) ;
OPT ( AST _Infinity , function ( self , compressor ) {
2017-04-08 19:18:14 +00:00
var lhs = is _lhs ( compressor . self ( ) , compressor . parent ( ) ) ;
if ( lhs && is _atomic ( lhs , self ) ) return self ;
2017-04-07 07:39:59 +00:00
if ( compressor . option ( "keep_infinity" )
2017-04-08 19:18:14 +00:00
&& ! ( lhs && ! is _atomic ( lhs , self ) )
2017-04-07 07:39:59 +00:00
&& ! find _variable ( compressor , "Infinity" ) )
return self ;
return make _node ( AST _Binary , self , {
2017-03-31 19:02:14 +00:00
operator : "/" ,
left : make _node ( AST _Number , self , {
value : 1
} ) ,
right : make _node ( AST _Number , self , {
value : 0
} )
} ) ;
} ) ;
OPT ( AST _NaN , function ( self , compressor ) {
2017-04-08 19:18:14 +00:00
var lhs = is _lhs ( compressor . self ( ) , compressor . parent ( ) ) ;
if ( lhs && ! is _atomic ( lhs , self )
2017-04-07 07:39:59 +00:00
|| find _variable ( compressor , "NaN" ) ) {
return make _node ( AST _Binary , self , {
operator : "/" ,
left : make _node ( AST _Number , self , {
value : 0
} ) ,
right : make _node ( AST _Number , self , {
value : 0
} )
} ) ;
}
return self ;
2012-09-14 13:26:30 +00:00
} ) ;
2012-09-14 12:36:38 +00:00
var ASSIGN _OPS = [ '+' , '-' , '/' , '*' , '%' , '>>' , '<<' , '>>>' , '|' , '^' , '&' ] ;
2016-08-26 22:47:25 +00:00
var ASSIGN _OPS _COMMUTATIVE = [ '*' , '|' , '^' , '&' ] ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Assign , function ( self , compressor ) {
2012-10-22 08:49:58 +00:00
self = self . lift _sequences ( compressor ) ;
2016-08-26 22:47:25 +00:00
if ( self . operator == "=" && self . left instanceof AST _SymbolRef && self . right instanceof AST _Binary ) {
// x = expr1 OP expr2
if ( self . right . left instanceof AST _SymbolRef
&& self . right . left . name == self . left . name
&& member ( self . right . operator , ASSIGN _OPS ) ) {
// x = x - 2 ---> x -= 2
self . operator = self . right . operator + "=" ;
self . right = self . right . right ;
}
else if ( self . right . right instanceof AST _SymbolRef
&& self . right . right . name == self . left . name
&& member ( self . right . operator , ASSIGN _OPS _COMMUTATIVE )
&& ! self . right . left . has _side _effects ( compressor ) ) {
// x = 2 & x ---> x &= 2
self . operator = self . right . operator + "=" ;
self . right = self . right . left ;
}
2012-09-14 12:36:38 +00:00
}
2012-09-26 10:04:54 +00:00
return self ;
2012-08-21 12:45:05 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Conditional , function ( self , compressor ) {
2012-09-04 10:20:28 +00:00
if ( ! compressor . option ( "conditionals" ) ) return self ;
2017-04-12 13:56:27 +00:00
// This looks like lift_sequences(), should probably be under "sequences"
if ( self . condition instanceof AST _Sequence ) {
var expressions = self . condition . expressions . slice ( ) ;
self . condition = expressions . pop ( ) ;
expressions . push ( self ) ;
return make _sequence ( self , expressions ) ;
2012-09-17 08:50:35 +00:00
}
2012-09-04 10:20:28 +00:00
var cond = self . condition . evaluate ( compressor ) ;
2017-03-15 17:02:59 +00:00
if ( cond !== self . condition ) {
if ( cond ) {
2012-09-21 11:38:52 +00:00
compressor . warn ( "Condition always true [{file}:{line},{col}]" , self . start ) ;
2016-02-16 18:52:28 +00:00
return maintain _this _binding ( compressor . parent ( ) , self , self . consequent ) ;
2012-09-04 10:20:28 +00:00
} else {
2012-09-21 11:38:52 +00:00
compressor . warn ( "Condition always false [{file}:{line},{col}]" , self . start ) ;
2016-02-16 18:52:28 +00:00
return maintain _this _binding ( compressor . parent ( ) , self , self . alternative ) ;
2012-09-04 10:20:28 +00:00
}
}
2017-03-15 17:02:59 +00:00
var negated = cond . negate ( compressor , first _in _statement ( compressor ) ) ;
if ( best _of ( compressor , cond , negated ) === negated ) {
2012-09-10 12:54:17 +00:00
self = make _node ( AST _Conditional , self , {
condition : negated ,
consequent : self . alternative ,
alternative : self . consequent
} ) ;
}
2017-03-26 08:36:33 +00:00
var condition = self . condition ;
2012-09-10 12:54:17 +00:00
var consequent = self . consequent ;
var alternative = self . alternative ;
2017-03-26 08:36:33 +00:00
// x?x:y --> x||y
if ( condition instanceof AST _SymbolRef
&& consequent instanceof AST _SymbolRef
&& condition . definition ( ) === consequent . definition ( ) ) {
return make _node ( AST _Binary , self , {
operator : "||" ,
left : condition ,
right : alternative
} ) ;
}
2017-03-24 10:52:48 +00:00
// if (foo) exp = something; else exp = something_else;
// |
// v
// exp = foo ? something : something_else;
2012-09-10 12:54:17 +00:00
if ( consequent instanceof AST _Assign
&& alternative instanceof AST _Assign
&& consequent . operator == alternative . operator
2012-09-14 12:36:38 +00:00
&& consequent . left . equivalent _to ( alternative . left )
2017-03-24 10:52:48 +00:00
&& ( ! self . condition . has _side _effects ( compressor )
|| consequent . operator == "="
&& ! consequent . left . has _side _effects ( compressor ) ) ) {
2013-12-29 08:31:30 +00:00
return make _node ( AST _Assign , self , {
2012-09-10 12:54:17 +00:00
operator : consequent . operator ,
left : consequent . left ,
right : make _node ( AST _Conditional , self , {
condition : self . condition ,
consequent : consequent . right ,
alternative : alternative . right
} )
} ) ;
}
2017-02-27 18:25:44 +00:00
// x ? y(a) : y(b) --> y(x ? a : b)
2013-12-29 08:31:30 +00:00
if ( consequent instanceof AST _Call
&& alternative . TYPE === consequent . TYPE
2017-02-27 18:25:44 +00:00
&& consequent . args . length == 1
&& alternative . args . length == 1
&& consequent . expression . equivalent _to ( alternative . expression )
&& ! consequent . expression . has _side _effects ( compressor ) ) {
consequent . args [ 0 ] = make _node ( AST _Conditional , self , {
condition : self . condition ,
consequent : consequent . args [ 0 ] ,
alternative : alternative . args [ 0 ]
} ) ;
return consequent ;
2013-12-29 08:31:30 +00:00
}
2014-02-06 20:39:13 +00:00
// x?y?z:a:a --> x&&y?z:a
if ( consequent instanceof AST _Conditional
&& consequent . alternative . equivalent _to ( alternative ) ) {
return make _node ( AST _Conditional , self , {
condition : make _node ( AST _Binary , self , {
left : self . condition ,
operator : "&&" ,
right : consequent . condition
} ) ,
consequent : consequent . consequent ,
alternative : alternative
} ) ;
}
2017-02-27 18:25:44 +00:00
// x ? y : y --> x, y
if ( consequent . equivalent _to ( alternative ) ) {
2017-04-12 13:56:27 +00:00
return make _sequence ( self , [
self . condition ,
consequent
] ) . optimize ( compressor ) ;
2014-09-02 20:30:25 +00:00
}
2015-11-24 18:27:50 +00:00
2016-04-02 04:21:13 +00:00
if ( is _true ( self . consequent ) ) {
if ( is _false ( self . alternative ) ) {
// c ? true : false ---> !!c
return booleanize ( self . condition ) ;
}
// c ? true : x ---> !!c || x
return make _node ( AST _Binary , self , {
operator : "||" ,
left : booleanize ( self . condition ) ,
right : self . alternative
} ) ;
}
if ( is _false ( self . consequent ) ) {
if ( is _true ( self . alternative ) ) {
// c ? false : true ---> !c
return booleanize ( self . condition . negate ( compressor ) ) ;
}
// c ? false : x ---> !c && x
return make _node ( AST _Binary , self , {
operator : "&&" ,
left : booleanize ( self . condition . negate ( compressor ) ) ,
right : self . alternative
} ) ;
}
if ( is _true ( self . alternative ) ) {
// c ? x : true ---> !c || x
return make _node ( AST _Binary , self , {
operator : "||" ,
left : booleanize ( self . condition . negate ( compressor ) ) ,
right : self . consequent
2014-09-13 15:59:19 +00:00
} ) ;
}
2016-04-02 04:21:13 +00:00
if ( is _false ( self . alternative ) ) {
// c ? x : false ---> !!c && x
return make _node ( AST _Binary , self , {
operator : "&&" ,
left : booleanize ( self . condition ) ,
right : self . consequent
} ) ;
2014-09-13 15:59:19 +00:00
}
2016-04-02 04:21:13 +00:00
2012-09-10 12:54:17 +00:00
return self ;
2015-11-24 18:27:50 +00:00
2016-04-02 04:21:13 +00:00
function booleanize ( node ) {
if ( node . is _boolean ( ) ) return node ;
// !!expression
return make _node ( AST _UnaryPrefix , node , {
operator : "!" ,
expression : node . negate ( compressor )
} ) ;
}
2015-11-24 18:27:50 +00:00
// AST_True or !0
function is _true ( node ) {
return node instanceof AST _True
|| ( node instanceof AST _UnaryPrefix
&& node . operator == "!"
&& node . expression instanceof AST _Constant
&& ! node . expression . value ) ;
}
// AST_False or !1
function is _false ( node ) {
return node instanceof AST _False
|| ( node instanceof AST _UnaryPrefix
&& node . operator == "!"
&& node . expression instanceof AST _Constant
&& ! ! node . expression . value ) ;
}
2012-08-21 12:45:05 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Boolean , function ( self , compressor ) {
2012-09-25 07:31:03 +00:00
if ( compressor . option ( "booleans" ) ) {
var p = compressor . parent ( ) ;
if ( p instanceof AST _Binary && ( p . operator == "=="
|| p . operator == "!=" ) ) {
compressor . warn ( "Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]" , {
operator : p . operator ,
2012-09-26 10:04:54 +00:00
value : self . value ,
2012-09-25 07:31:03 +00:00
file : p . start . file ,
line : p . start . line ,
col : p . start . col ,
} ) ;
2012-09-26 10:04:54 +00:00
return make _node ( AST _Number , self , {
value : + self . value
2012-09-25 07:31:03 +00:00
} ) ;
}
2012-09-26 10:04:54 +00:00
return make _node ( AST _UnaryPrefix , self , {
2012-09-25 07:31:03 +00:00
operator : "!" ,
2012-09-26 10:04:54 +00:00
expression : make _node ( AST _Number , self , {
value : 1 - self . value
2012-09-25 07:31:03 +00:00
} )
} ) ;
}
2012-09-26 10:04:54 +00:00
return self ;
2012-09-03 20:49:57 +00:00
} ) ;
2012-09-26 10:04:54 +00:00
OPT ( AST _Sub , function ( self , compressor ) {
var prop = self . property ;
2012-09-25 15:03:31 +00:00
if ( prop instanceof AST _String && compressor . option ( "properties" ) ) {
prop = prop . getValue ( ) ;
2017-07-15 14:50:59 +00:00
if ( is _identifier _string ( prop ) ) {
2012-09-26 10:04:54 +00:00
return make _node ( AST _Dot , self , {
expression : self . expression ,
2012-09-25 15:03:31 +00:00
property : prop
2014-06-30 22:51:42 +00:00
} ) . optimize ( compressor ) ;
2012-09-25 15:03:31 +00:00
}
2014-01-07 10:48:21 +00:00
var v = parseFloat ( prop ) ;
if ( ! isNaN ( v ) && v . toString ( ) == prop ) {
self . property = make _node ( AST _Number , self . property , {
2014-01-07 10:54:14 +00:00
value : v
2014-01-07 10:48:21 +00:00
} ) ;
}
2012-09-25 15:03:31 +00:00
}
2017-03-15 17:02:59 +00:00
var ev = self . evaluate ( compressor ) ;
if ( ev !== self ) {
ev = make _node _from _constant ( ev , self ) . optimize ( compressor ) ;
return best _of ( compressor , ev , self ) ;
}
return self ;
2012-09-25 12:59:27 +00:00
} ) ;
2017-07-06 13:51:58 +00:00
AST _Lambda . DEFMETHOD ( "contains_this" , function ( ) {
var result ;
var self = this ;
self . walk ( new TreeWalker ( function ( node ) {
if ( result ) return true ;
if ( node instanceof AST _This ) return result = true ;
if ( node !== self && node instanceof AST _Scope ) return true ;
} ) ) ;
return result ;
} ) ;
2014-06-30 22:51:42 +00:00
OPT ( AST _Dot , function ( self , compressor ) {
2017-02-18 11:27:31 +00:00
var def = self . resolve _defines ( compressor ) ;
if ( def ) {
2017-04-08 08:46:25 +00:00
return def . optimize ( compressor ) ;
2017-02-18 11:27:31 +00:00
}
2017-07-06 13:51:58 +00:00
if ( compressor . option ( "unsafe" ) && self . expression instanceof AST _Object ) {
var values = self . expression . properties ;
for ( var i = values . length ; -- i >= 0 ; ) {
2017-07-15 14:50:59 +00:00
if ( values [ i ] . key === self . property ) {
2017-07-06 13:51:58 +00:00
var value = values [ i ] . value ;
if ( value instanceof AST _Function ? ! value . contains _this ( ) : ! value . has _side _effects ( compressor ) ) {
var obj = self . expression . clone ( ) ;
obj . properties = obj . properties . slice ( ) ;
obj . properties . splice ( i , 1 ) ;
return make _sequence ( self , [ obj , value ] ) . optimize ( compressor ) ;
}
}
}
}
2017-02-18 11:34:54 +00:00
if ( compressor . option ( "unsafe_proto" )
&& self . expression instanceof AST _Dot
&& self . expression . property == "prototype" ) {
var exp = self . expression . expression ;
2017-07-15 07:16:11 +00:00
if ( is _undeclared _ref ( exp ) ) switch ( exp . name ) {
2017-02-18 11:34:54 +00:00
case "Array" :
self . expression = make _node ( AST _Array , self . expression , {
elements : [ ]
} ) ;
break ;
case "Object" :
self . expression = make _node ( AST _Object , self . expression , {
properties : [ ]
} ) ;
break ;
case "String" :
self . expression = make _node ( AST _String , self . expression , {
value : ""
} ) ;
break ;
}
}
2017-03-15 17:02:59 +00:00
var ev = self . evaluate ( compressor ) ;
if ( ev !== self ) {
ev = make _node _from _constant ( ev , self ) . optimize ( compressor ) ;
return best _of ( compressor , ev , self ) ;
}
return self ;
2014-06-30 22:51:42 +00:00
} ) ;
2012-10-17 18:57:08 +00:00
function literals _in _boolean _context ( self , compressor ) {
2017-02-27 18:25:44 +00:00
if ( compressor . option ( "booleans" ) && compressor . in _boolean _context ( ) ) {
2017-04-12 13:56:27 +00:00
return best _of ( compressor , self , make _sequence ( self , [
self ,
make _node ( AST _True , self )
] ) . optimize ( compressor ) ) ;
2012-10-17 18:57:08 +00:00
}
return self ;
} ;
OPT ( AST _Array , literals _in _boolean _context ) ;
OPT ( AST _Object , literals _in _boolean _context ) ;
OPT ( AST _RegExp , literals _in _boolean _context ) ;
2015-10-28 17:25:27 +00:00
OPT ( AST _Return , function ( self , compressor ) {
2017-03-31 19:02:14 +00:00
if ( self . value && is _undefined ( self . value , compressor ) ) {
2015-10-28 21:51:46 +00:00
self . value = null ;
2015-10-28 17:25:27 +00:00
}
return self ;
} ) ;
2017-02-18 11:27:31 +00:00
OPT ( AST _VarDef , function ( self , compressor ) {
var defines = compressor . option ( "global_defs" ) ;
if ( defines && HOP ( defines , self . name . name ) ) {
compressor . warn ( 'global_defs ' + self . name . name + ' redefined [{file}:{line},{col}]' , self . start ) ;
}
return self ;
} ) ;
2012-08-21 12:45:05 +00:00
} ) ( ) ;