@@ -1384,6 +1384,38 @@ impl<'a> PeepholeOptimizations {
13841384 // ```
13851385 return Some ( false ) ;
13861386 }
1387+ if replacement_has_side_effect {
1388+ // If the assignment target may depend on side effects of the replacement,
1389+ // don't reorder it past the assignment target. The non-last part of the
1390+ // assignment target is evaluated before the assignment evaluation so that
1391+ // part may be changed by the side effect. For example, "fn()" may change
1392+ // "foo" here:
1393+ // ```js
1394+ // let a = fn();
1395+ // foo.bar = a;
1396+ // ```
1397+ let may_depend_on_side_effect = match & assign_expr. left {
1398+ AssignmentTarget :: AssignmentTargetIdentifier ( _) => false ,
1399+ AssignmentTarget :: ComputedMemberExpression ( member_expr) => {
1400+ Self :: is_expression_that_reference_may_change ( & member_expr. object , ctx)
1401+ }
1402+ AssignmentTarget :: PrivateFieldExpression ( member_expr) => {
1403+ Self :: is_expression_that_reference_may_change ( & member_expr. object , ctx)
1404+ }
1405+ AssignmentTarget :: StaticMemberExpression ( member_expr) => {
1406+ Self :: is_expression_that_reference_may_change ( & member_expr. object , ctx)
1407+ }
1408+ AssignmentTarget :: ArrayAssignmentTarget ( _)
1409+ | AssignmentTarget :: ObjectAssignmentTarget ( _)
1410+ | AssignmentTarget :: TSAsExpression ( _)
1411+ | AssignmentTarget :: TSNonNullExpression ( _)
1412+ | AssignmentTarget :: TSSatisfiesExpression ( _)
1413+ | AssignmentTarget :: TSTypeAssertion ( _) => true ,
1414+ } ;
1415+ if may_depend_on_side_effect {
1416+ return Some ( false ) ;
1417+ }
1418+ }
13871419 // If we get here then it should be safe to attempt to substitute the
13881420 // replacement past the left operand into the right operand.
13891421 if let Some ( changed) = Self :: substitute_single_use_symbol_in_expression (
@@ -1799,6 +1831,21 @@ impl<'a> PeepholeOptimizations {
17991831 // Otherwise we should stop trying to substitute past this point
18001832 Some ( false )
18011833 }
1834+
1835+ fn is_expression_that_reference_may_change ( expr : & Expression < ' a > , ctx : & Ctx < ' a , ' _ > ) -> bool {
1836+ match expr {
1837+ Expression :: Identifier ( id) => {
1838+ if let Some ( symbol_id) = ctx. scoping ( ) . get_reference ( id. reference_id ( ) ) . symbol_id ( )
1839+ {
1840+ ctx. scoping ( ) . symbol_is_mutated ( symbol_id)
1841+ } else {
1842+ true
1843+ }
1844+ }
1845+ Expression :: ThisExpression ( _) => false ,
1846+ _ => true ,
1847+ }
1848+ }
18021849}
18031850
18041851#[ cfg( test) ]
0 commit comments