@@ -528,6 +528,7 @@ AST_Scope.DEFMETHOD("process_expression", function(insert, compressor) {
528528function read_property ( obj , key ) {
529529 key = get_value ( key ) ;
530530 if ( key instanceof AST_Node ) return ;
531+
531532 var value ;
532533 if ( obj instanceof AST_Array ) {
533534 var elements = obj . elements ;
@@ -542,6 +543,7 @@ function read_property(obj, key) {
542543 if ( ! value && props [ i ] . key === key ) value = props [ i ] . value ;
543544 }
544545 }
546+
545547 return value instanceof AST_SymbolRef && value . fixed_value ( ) || value ;
546548}
547549
@@ -674,38 +676,56 @@ function is_modified(compressor, tw, node, value, level, immutable) {
674676 || value instanceof AST_This ;
675677 }
676678
677- function mark_escaped ( tw , d , scope , node , value , level , depth ) {
679+ // A definition "escapes" when its value can leave the point of use.
680+ // Example: `a = b || c`
681+ // In this example, "b" and "c" are escaping, because they're going into "a"
682+ //
683+ // def.escaped is != 0 when it escapes.
684+ //
685+ // When greater than 1, it means that N chained properties will be read off
686+ // of that def before an escape occurs. This is useful for evaluating
687+ // property accesses, where you need to know when to stop.
688+ function mark_escaped ( tw , d , scope , node , value , level = 0 , depth = 1 ) {
678689 var parent = tw . parent ( level ) ;
679690 if ( value ) {
680691 if ( value . is_constant ( ) ) return ;
681692 if ( value instanceof AST_ClassExpression ) return ;
682693 }
683- if ( parent instanceof AST_Assign && parent . operator == "=" && node === parent . right
694+
695+ if (
696+ parent instanceof AST_Assign && ( parent . operator === "=" || parent . logical ) && node === parent . right
684697 || parent instanceof AST_Call && ( node !== parent . expression || parent instanceof AST_New )
685698 || parent instanceof AST_Exit && node === parent . value && node . scope !== d . scope
686699 || parent instanceof AST_VarDef && node === parent . value
687- || parent instanceof AST_Yield && node === parent . value && node . scope !== d . scope ) {
700+ || parent instanceof AST_Yield && node === parent . value && node . scope !== d . scope
701+ ) {
688702 if ( depth > 1 && ! ( value && value . is_constant_expression ( scope ) ) ) depth = 1 ;
689703 if ( ! d . escaped || d . escaped > depth ) d . escaped = depth ;
690704 return ;
691- } else if ( parent instanceof AST_Array
705+ } else if (
706+ parent instanceof AST_Array
692707 || parent instanceof AST_Await
693708 || parent instanceof AST_Binary && lazy_op . has ( parent . operator )
694709 || parent instanceof AST_Conditional && node !== parent . condition
695710 || parent instanceof AST_Expansion
696- || parent instanceof AST_Sequence && node === parent . tail_node ( ) ) {
711+ || parent instanceof AST_Sequence && node === parent . tail_node ( )
712+ ) {
697713 mark_escaped ( tw , d , scope , parent , parent , level + 1 , depth ) ;
698714 } else if ( parent instanceof AST_ObjectKeyVal && node === parent . value ) {
699715 var obj = tw . parent ( level + 1 ) ;
716+
700717 mark_escaped ( tw , d , scope , obj , obj , level + 2 , depth ) ;
701718 } else if ( parent instanceof AST_PropAccess && node === parent . expression ) {
702719 value = read_property ( value , parent . property ) ;
720+
703721 mark_escaped ( tw , d , scope , parent , value , level + 1 , depth + 1 ) ;
704722 if ( value ) return ;
705723 }
724+
706725 if ( level > 0 ) return ;
707726 if ( parent instanceof AST_Sequence && node !== parent . tail_node ( ) ) return ;
708727 if ( parent instanceof AST_SimpleStatement ) return ;
728+
709729 d . direct_access = true ;
710730 }
711731
@@ -729,32 +749,64 @@ function is_modified(compressor, tw, node, value, level, immutable) {
729749 suppress ( node . left ) ;
730750 return ;
731751 }
752+
753+ const finish_walk = ( ) => {
754+ if ( node . logical ) {
755+ node . left . walk ( tw ) ;
756+
757+ push ( tw ) ;
758+ node . right . walk ( tw ) ;
759+ pop ( tw ) ;
760+
761+ return true ;
762+ }
763+ } ;
764+
732765 var sym = node . left ;
733- if ( ! ( sym instanceof AST_SymbolRef ) ) return ;
766+ if ( ! ( sym instanceof AST_SymbolRef ) ) return finish_walk ( ) ;
767+
734768 var def = sym . definition ( ) ;
735769 var safe = safe_to_assign ( tw , def , sym . scope , node . right ) ;
736770 def . assignments ++ ;
737- if ( ! safe ) return ;
771+ if ( ! safe ) return finish_walk ( ) ;
772+
738773 var fixed = def . fixed ;
739- if ( ! fixed && node . operator != "=" ) return ;
774+ if ( ! fixed && node . operator != "=" && ! node . logical ) return finish_walk ( ) ;
775+
740776 var eq = node . operator == "=" ;
741777 var value = eq ? node . right : node ;
742- if ( is_modified ( compressor , tw , node , value , 0 ) ) return ;
778+ if ( is_modified ( compressor , tw , node , value , 0 ) ) return finish_walk ( ) ;
779+
743780 def . references . push ( sym ) ;
744- if ( ! eq ) def . chained = true ;
745- def . fixed = eq ? function ( ) {
746- return node . right ;
747- } : function ( ) {
748- return make_node ( AST_Binary , node , {
749- operator : node . operator . slice ( 0 , - 1 ) ,
750- left : fixed instanceof AST_Node ? fixed : fixed ( ) ,
751- right : node . right
752- } ) ;
753- } ;
781+
782+ if ( ! node . logical ) {
783+ if ( ! eq ) def . chained = true ;
784+
785+ def . fixed = eq ? function ( ) {
786+ return node . right ;
787+ } : function ( ) {
788+ return make_node ( AST_Binary , node , {
789+ operator : node . operator . slice ( 0 , - 1 ) ,
790+ left : fixed instanceof AST_Node ? fixed : fixed ( ) ,
791+ right : node . right
792+ } ) ;
793+ } ;
794+ }
795+
796+ if ( node . logical ) {
797+ mark ( tw , def , false ) ;
798+ push ( tw ) ;
799+ node . right . walk ( tw ) ;
800+ pop ( tw ) ;
801+ return true ;
802+ }
803+
754804 mark ( tw , def , false ) ;
755805 node . right . walk ( tw ) ;
756806 mark ( tw , def , true ) ;
807+
757808 mark_escaped ( tw , def , sym . scope , node , value , 0 , 1 ) ;
809+
758810 return true ;
759811 } ) ;
760812 def_reduce_vars ( AST_Binary , function ( tw ) {
@@ -1357,7 +1409,8 @@ function tighten_body(statements, compressor) {
13571409 }
13581410 // Stop immediately if these node types are encountered
13591411 var parent = scanner . parent ( ) ;
1360- if ( node instanceof AST_Assign && node . operator != "=" && lhs . equivalent_to ( node . left )
1412+ if ( node instanceof AST_Assign
1413+ && ( node . logical || node . operator != "=" && lhs . equivalent_to ( node . left ) )
13611414 || node instanceof AST_Await
13621415 || node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs . equivalent_to ( node . expression )
13631416 || node instanceof AST_Debugger
@@ -1425,6 +1478,7 @@ function tighten_body(statements, compressor) {
14251478 }
14261479 return make_node ( AST_Assign , candidate , {
14271480 operator : "=" ,
1481+ logical : false ,
14281482 left : make_node ( AST_SymbolRef , candidate . name , candidate . name ) ,
14291483 right : value
14301484 } ) ;
@@ -1729,6 +1783,7 @@ function tighten_body(statements, compressor) {
17291783 var parent = scanner . parent ( level ) ;
17301784 if ( parent instanceof AST_Assign ) {
17311785 if ( write_only
1786+ && ! parent . logical
17321787 && ! ( parent . left instanceof AST_PropAccess
17331788 || lvalues . has ( parent . left . name ) ) ) {
17341789 return find_stop ( parent , level + 1 , write_only ) ;
@@ -1783,7 +1838,9 @@ function tighten_body(statements, compressor) {
17831838 }
17841839
17851840 function get_lhs ( expr ) {
1786- if ( expr instanceof AST_VarDef && expr . name instanceof AST_SymbolDeclaration ) {
1841+ if ( expr instanceof AST_Assign && expr . logical ) {
1842+ return false ;
1843+ } else if ( expr instanceof AST_VarDef && expr . name instanceof AST_SymbolDeclaration ) {
17871844 var def = expr . name . definition ( ) ;
17881845 if ( ! member ( expr . name , def . orig ) ) return ;
17891846 var referenced = def . references . length - def . replaced ;
@@ -1794,14 +1851,20 @@ function tighten_body(statements, compressor) {
17941851 return make_node ( AST_SymbolRef , expr . name , expr . name ) ;
17951852 }
17961853 } else {
1797- const lhs = expr [ expr instanceof AST_Assign ? "left" : "expression" ] ;
1854+ const lhs = expr instanceof AST_Assign
1855+ ? expr . left
1856+ : expr . expression ;
17981857 return ! is_ref_of ( lhs , AST_SymbolConst )
17991858 && ! is_ref_of ( lhs , AST_SymbolLet ) && lhs ;
18001859 }
18011860 }
18021861
18031862 function get_rvalue ( expr ) {
1804- return expr [ expr instanceof AST_Assign ? "right" : "value" ] ;
1863+ if ( expr instanceof AST_Assign ) {
1864+ return expr . right ;
1865+ } else {
1866+ return expr . value ;
1867+ }
18051868 }
18061869
18071870 function get_lvalues ( expr ) {
@@ -1860,7 +1923,9 @@ function tighten_body(statements, compressor) {
18601923 && ! ( in_loop
18611924 && ( lvalues . has ( lhs . name )
18621925 || candidate instanceof AST_Unary
1863- || candidate instanceof AST_Assign && candidate . operator != "=" ) ) ;
1926+ || ( candidate instanceof AST_Assign
1927+ && ! candidate . logical
1928+ && candidate . operator != "=" ) ) ) ;
18641929 }
18651930
18661931 function value_has_side_effects ( expr ) {
@@ -2287,7 +2352,7 @@ function tighten_body(statements, compressor) {
22872352 var def = defn . definitions [ defn . definitions . length - 1 ] ;
22882353 if ( ! ( def . value instanceof AST_Object ) ) return ;
22892354 var exprs ;
2290- if ( body instanceof AST_Assign ) {
2355+ if ( body instanceof AST_Assign && ! body . logical ) {
22912356 exprs = [ body ] ;
22922357 } else if ( body instanceof AST_Sequence ) {
22932358 exprs = body . expressions . slice ( ) ;
@@ -2510,6 +2575,8 @@ function is_undefined(node, compressor) {
25102575 && ( this . left . _dot_throw ( compressor ) || this . right . _dot_throw ( compressor ) ) ;
25112576 } ) ;
25122577 def_may_throw_on_access ( AST_Assign , function ( compressor ) {
2578+ if ( this . logical ) return true ;
2579+
25132580 return this . operator == "="
25142581 && this . right . _dot_throw ( compressor ) ;
25152582 } ) ;
@@ -3734,6 +3801,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
37343801 var drop_vars = ! ( self instanceof AST_Toplevel ) || compressor . toplevel . vars ;
37353802 const assign_as_unused = r_keep_assign . test ( compressor . option ( "unused" ) ) ? return_false : function ( node ) {
37363803 if ( node instanceof AST_Assign
3804+ && ! node . logical
37373805 && ( has_flag ( node , WRITE_ONLY ) || node . operator == "=" )
37383806 ) {
37393807 return node . left ;
@@ -3952,6 +4020,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
39524020 sym . references . push ( ref ) ;
39534021 var assign = make_node ( AST_Assign , def , {
39544022 operator : "=" ,
4023+ logical : false ,
39554024 left : ref ,
39564025 right : def . value
39574026 } ) ;
@@ -4387,6 +4456,8 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
43874456 }
43884457 } ) ;
43894458 def_drop_side_effect_free ( AST_Assign , function ( compressor ) {
4459+ if ( this . logical ) return this ;
4460+
43904461 var left = this . left ;
43914462 if ( left . has_side_effects ( compressor )
43924463 || compressor . has_directive ( "use strict" )
@@ -4983,6 +5054,7 @@ AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
49835054 var name = make_node ( AST_SymbolRef , def . name , def . name ) ;
49845055 assignments . push ( make_node ( AST_Assign , def , {
49855056 operator : "=" ,
5057+ logical : false ,
49865058 left : name ,
49875059 right : def . value
49885060 } ) ) ;
@@ -5597,6 +5669,7 @@ def_optimize(AST_Call, function(self, compressor) {
55975669 def . references . push ( sym ) ;
55985670 if ( value ) expressions . push ( make_node ( AST_Assign , self , {
55995671 operator : "=" ,
5672+ logical : false ,
56005673 left : sym ,
56015674 right : value . clone ( )
56025675 } ) ) ;
@@ -5640,6 +5713,7 @@ def_optimize(AST_Call, function(self, compressor) {
56405713 def . references . push ( sym ) ;
56415714 expressions . splice ( pos ++ , 0 , make_node ( AST_Assign , var_def , {
56425715 operator : "=" ,
5716+ logical : false ,
56435717 left : sym ,
56445718 right : make_node ( AST_Undefined , name )
56455719 } ) ) ;
@@ -6597,6 +6671,10 @@ function is_reachable(self, defs) {
65976671const ASSIGN_OPS = makePredicate ( "+ - / * % >> << >>> | ^ &" ) ;
65986672const ASSIGN_OPS_COMMUTATIVE = makePredicate ( "* | ^ &" ) ;
65996673def_optimize ( AST_Assign , function ( self , compressor ) {
6674+ if ( self . logical ) {
6675+ return self . lift_sequences ( compressor ) ;
6676+ }
6677+
66006678 var def ;
66016679 if ( compressor . option ( "dead_code" )
66026680 && self . left instanceof AST_SymbolRef
@@ -6814,16 +6892,20 @@ def_optimize(AST_Conditional, function(self, compressor) {
68146892 // |
68156893 // v
68166894 // exp = foo ? something : something_else;
6817- if ( consequent instanceof AST_Assign
6895+ if (
6896+ consequent instanceof AST_Assign
68186897 && alternative instanceof AST_Assign
6819- && consequent . operator == alternative . operator
6898+ && consequent . operator === alternative . operator
6899+ && consequent . logical === alternative . logical
68206900 && consequent . left . equivalent_to ( alternative . left )
68216901 && ( ! self . condition . has_side_effects ( compressor )
68226902 || consequent . operator == "="
6823- && ! consequent . left . has_side_effects ( compressor ) ) ) {
6903+ && ! consequent . left . has_side_effects ( compressor ) )
6904+ ) {
68246905 return make_node ( AST_Assign , self , {
68256906 operator : consequent . operator ,
68266907 left : consequent . left ,
6908+ logical : consequent . logical ,
68276909 right : make_node ( AST_Conditional , self , {
68286910 condition : self . condition ,
68296911 consequent : consequent . right ,
0 commit comments