11use oxc_allocator:: { TakeIn , Vec } ;
22use oxc_ast:: ast:: * ;
33use oxc_ast_visit:: Visit ;
4- use oxc_ecmascript:: { constant_evaluation:: ConstantEvaluation , side_effects:: MayHaveSideEffects } ;
4+ use oxc_ecmascript:: {
5+ constant_evaluation:: { ConstantEvaluation , ConstantValue } ,
6+ side_effects:: MayHaveSideEffects ,
7+ } ;
58use oxc_span:: GetSpan ;
69use oxc_traverse:: Ancestor ;
710
@@ -340,11 +343,11 @@ impl<'a> PeepholeOptimizations {
340343 }
341344 }
342345
343- pub fn keep_track_of_empty_functions ( stmt : & mut Statement < ' a > , ctx : & mut Ctx < ' a , ' _ > ) {
346+ pub fn keep_track_of_pure_functions ( stmt : & mut Statement < ' a > , ctx : & mut Ctx < ' a , ' _ > ) {
344347 match stmt {
345348 Statement :: FunctionDeclaration ( f) => {
346349 if let Some ( body) = & f. body {
347- Self :: try_save_empty_function (
350+ Self :: try_save_pure_function (
348351 f. id . as_ref ( ) ,
349352 & f. params ,
350353 body,
@@ -359,7 +362,7 @@ impl<'a> PeepholeOptimizations {
359362 if let BindingPatternKind :: BindingIdentifier ( id) = & d. id . kind {
360363 match & d. init {
361364 Some ( Expression :: ArrowFunctionExpression ( a) ) => {
362- Self :: try_save_empty_function (
365+ Self :: try_save_pure_function (
363366 Some ( id) ,
364367 & a. params ,
365368 & a. body ,
@@ -370,7 +373,7 @@ impl<'a> PeepholeOptimizations {
370373 }
371374 Some ( Expression :: FunctionExpression ( f) ) => {
372375 if let Some ( body) = & f. body {
373- Self :: try_save_empty_function (
376+ Self :: try_save_pure_function (
374377 Some ( id) ,
375378 & f. params ,
376379 body,
@@ -389,24 +392,30 @@ impl<'a> PeepholeOptimizations {
389392 }
390393 }
391394
392- fn try_save_empty_function (
395+ fn try_save_pure_function (
393396 id : Option < & BindingIdentifier < ' a > > ,
394397 params : & FormalParameters < ' a > ,
395398 body : & FunctionBody < ' a > ,
396399 r#async : bool ,
397400 generator : bool ,
398401 ctx : & mut Ctx < ' a , ' _ > ,
399402 ) {
400- if !body . is_empty ( ) || r#async || generator {
403+ if r#async || generator {
401404 return ;
402405 }
403406 // `function foo({}) {} foo(null)` is runtime type error.
404407 if !params. items . iter ( ) . all ( |pat| pat. pattern . kind . is_binding_identifier ( ) ) {
405408 return ;
406409 }
410+ if body. statements . iter ( ) . any ( |stmt| stmt. may_have_side_effects ( ctx) ) {
411+ return ;
412+ }
407413 let Some ( symbol_id) = id. and_then ( |id| id. symbol_id . get ( ) ) else { return } ;
408414 if ctx. scoping ( ) . get_resolved_references ( symbol_id) . all ( |r| r. flags ( ) . is_read_only ( ) ) {
409- ctx. state . empty_functions . insert ( symbol_id) ;
415+ ctx. state . pure_functions . insert (
416+ symbol_id,
417+ if body. is_empty ( ) { Some ( ConstantValue :: Undefined ) } else { None } ,
418+ ) ;
410419 }
411420 }
412421
@@ -415,7 +424,10 @@ impl<'a> PeepholeOptimizations {
415424 if let Expression :: Identifier ( ident) = & e. callee {
416425 if let Some ( reference_id) = ident. reference_id . get ( ) {
417426 if let Some ( symbol_id) = ctx. scoping ( ) . get_reference ( reference_id) . symbol_id ( ) {
418- if ctx. state . empty_functions . contains ( & symbol_id) {
427+ if matches ! (
428+ ctx. state. pure_functions. get( & symbol_id) ,
429+ Some ( Some ( ConstantValue :: Undefined ) )
430+ ) {
419431 let mut exprs =
420432 Self :: fold_arguments_into_needed_expressions ( & mut e. arguments , ctx) ;
421433 if exprs. is_empty ( ) {
0 commit comments