@@ -394,6 +394,13 @@ impl<'a> PeepholeOptimizations {
394
394
ctx. state . changed = true ;
395
395
}
396
396
}
397
+ if Self :: substitute_single_use_symbol_within_declaration (
398
+ var_decl. kind ,
399
+ & mut var_decl. declarations ,
400
+ ctx,
401
+ ) {
402
+ ctx. state . changed = true ;
403
+ }
397
404
398
405
// If `join_vars` is off, but there are unused declarators ... just join them to make our code simpler.
399
406
if !ctx. options ( ) . join_vars
@@ -858,6 +865,13 @@ impl<'a> PeepholeOptimizations {
858
865
ctx. state . changed = true ;
859
866
}
860
867
}
868
+ if Self :: substitute_single_use_symbol_within_declaration (
869
+ var_decl. kind ,
870
+ & mut var_decl. declarations ,
871
+ ctx,
872
+ ) {
873
+ ctx. state . changed = true ;
874
+ }
861
875
}
862
876
match_expression ! ( ForStatementInit ) => {
863
877
let init = init. to_expression_mut ( ) ;
@@ -1140,67 +1154,109 @@ impl<'a> PeepholeOptimizations {
1140
1154
if prev_var_decl. kind . is_using ( ) {
1141
1155
break ;
1142
1156
}
1157
+ let old_len = prev_var_decl. declarations . len ( ) ;
1158
+ let new_len = Self :: substitute_single_use_symbol_in_expression_from_declarators (
1159
+ expr_in_stmt,
1160
+ & mut prev_var_decl. declarations ,
1161
+ ctx,
1162
+ non_scoped_literal_only,
1163
+ ) ;
1164
+ if new_len == 0 {
1165
+ inlined = true ;
1166
+ stmts. pop ( ) ;
1167
+ } else if old_len != new_len {
1168
+ inlined = true ;
1169
+ prev_var_decl. declarations . truncate ( new_len) ;
1170
+ break ;
1171
+ } else {
1172
+ break ;
1173
+ }
1174
+ }
1175
+ inlined
1176
+ }
1143
1177
1144
- let last_non_inlined_index =
1145
- prev_var_decl. declarations . iter_mut ( ) . rposition ( |prev_decl| {
1146
- let Some ( prev_decl_init) = & mut prev_decl. init else {
1147
- return true ;
1148
- } ;
1149
- let BindingPatternKind :: BindingIdentifier ( prev_decl_id) = & prev_decl. id . kind
1150
- else {
1151
- return true ;
1152
- } ;
1153
- if ctx. is_expression_whose_name_needs_to_be_kept ( prev_decl_init) {
1154
- return true ;
1155
- }
1156
- let Some ( symbol_value) =
1157
- ctx. state . symbol_values . get_symbol_value ( prev_decl_id. symbol_id ( ) )
1158
- else {
1159
- return true ;
1160
- } ;
1161
- // we should check whether it's exported by `symbol_value.exported`
1162
- // because the variable might be exported with `export { foo }` rather than `export var foo`
1163
- if symbol_value. exported
1164
- || symbol_value. read_references_count > 1
1165
- || symbol_value. write_references_count > 0
1166
- {
1167
- return true ;
1168
- }
1169
- if non_scoped_literal_only && !prev_decl_init. is_literal_value ( false , ctx) {
1170
- return true ;
1171
- }
1172
- let replaced = Self :: substitute_single_use_symbol_in_expression (
1173
- expr_in_stmt,
1174
- & prev_decl_id. name ,
1175
- prev_decl_init,
1176
- prev_decl_init. may_have_side_effects ( ctx) ,
1177
- ctx,
1178
- ) ;
1179
- if replaced != Some ( true ) {
1180
- return true ;
1181
- }
1182
- false
1183
- } ) ;
1184
- match last_non_inlined_index {
1185
- None => {
1186
- // all inlined
1187
- stmts. pop ( ) ;
1188
- inlined = true ;
1189
- }
1190
- Some ( last_non_inlined_index)
1191
- if last_non_inlined_index + 1 == prev_var_decl. declarations . len ( ) =>
1192
- {
1193
- // no change
1194
- break ;
1195
- }
1196
- Some ( last_non_inlined_index) => {
1197
- prev_var_decl. declarations . truncate ( last_non_inlined_index + 1 ) ;
1198
- inlined = true ;
1199
- break ;
1178
+ fn substitute_single_use_symbol_within_declaration (
1179
+ kind : VariableDeclarationKind ,
1180
+ declarations : & mut Vec < ' a , VariableDeclarator < ' a > > ,
1181
+ ctx : & Ctx < ' a , ' _ > ,
1182
+ ) -> bool {
1183
+ // TODO: we should skip this compression when direct eval exists
1184
+ // because the code inside eval may reference the variable
1185
+
1186
+ let mut changed = false ;
1187
+ if !Self :: keep_top_level_var_in_script_mode ( ctx) && !kind. is_using ( ) {
1188
+ let mut i = 1 ;
1189
+ while i < declarations. len ( ) {
1190
+ let ( prev_decls, [ decl, ..] ) = declarations. split_at_mut ( i) else { unreachable ! ( ) } ;
1191
+ let Some ( decl_init) = & mut decl. init else {
1192
+ i += 1 ;
1193
+ continue ;
1194
+ } ;
1195
+ let old_len = prev_decls. len ( ) ;
1196
+ let new_len = Self :: substitute_single_use_symbol_in_expression_from_declarators (
1197
+ decl_init, prev_decls, ctx, false ,
1198
+ ) ;
1199
+ if old_len != new_len {
1200
+ changed = true ;
1201
+ let drop_count = old_len - new_len;
1202
+ declarations. drain ( i - drop_count..i) ;
1203
+ i -= drop_count;
1200
1204
}
1205
+ i += 1 ;
1201
1206
}
1202
1207
}
1203
- inlined
1208
+ changed
1209
+ }
1210
+
1211
+ /// Returns new length
1212
+ fn substitute_single_use_symbol_in_expression_from_declarators (
1213
+ target_expr : & mut Expression < ' a > ,
1214
+ declarators : & mut [ VariableDeclarator < ' a > ] ,
1215
+ ctx : & Ctx < ' a , ' _ > ,
1216
+ non_scoped_literal_only : bool ,
1217
+ ) -> usize {
1218
+ let last_non_inlined_index = declarators. iter_mut ( ) . rposition ( |prev_decl| {
1219
+ let Some ( prev_decl_init) = & mut prev_decl. init else {
1220
+ return true ;
1221
+ } ;
1222
+ let BindingPatternKind :: BindingIdentifier ( prev_decl_id) = & prev_decl. id . kind else {
1223
+ return true ;
1224
+ } ;
1225
+ if ctx. is_expression_whose_name_needs_to_be_kept ( prev_decl_init) {
1226
+ return true ;
1227
+ }
1228
+ let Some ( symbol_value) =
1229
+ ctx. state . symbol_values . get_symbol_value ( prev_decl_id. symbol_id ( ) )
1230
+ else {
1231
+ return true ;
1232
+ } ;
1233
+ // we should check whether it's exported by `symbol_value.exported`
1234
+ // because the variable might be exported with `export { foo }` rather than `export var foo`
1235
+ if symbol_value. exported
1236
+ || symbol_value. read_references_count > 1
1237
+ || symbol_value. write_references_count > 0
1238
+ {
1239
+ return true ;
1240
+ }
1241
+ if non_scoped_literal_only && !prev_decl_init. is_literal_value ( false , ctx) {
1242
+ return true ;
1243
+ }
1244
+ let replaced = Self :: substitute_single_use_symbol_in_expression (
1245
+ target_expr,
1246
+ & prev_decl_id. name ,
1247
+ prev_decl_init,
1248
+ prev_decl_init. may_have_side_effects ( ctx) ,
1249
+ ctx,
1250
+ ) ;
1251
+ if replaced != Some ( true ) {
1252
+ return true ;
1253
+ }
1254
+ false
1255
+ } ) ;
1256
+ match last_non_inlined_index {
1257
+ None => 0 ,
1258
+ Some ( last_non_inlined_index) => last_non_inlined_index + 1 ,
1259
+ }
1204
1260
}
1205
1261
1206
1262
/// Returns Some(true) when the expression is successfully replaced.
0 commit comments