@@ -521,11 +521,17 @@ class JSGenerator {
521521 this . usedMathFunctions . add ( 'abs' ) ;
522522 return new TypedInput ( `abs(${ value . asNumber ( ) } )` , TYPES . NUMBER ) ;
523523 }
524- case BLOCKS . OP . ACOS :
524+ case BLOCKS . OP . ACOS : {
525525 // Needs to be marked as NaN because Math.acos(1.0001) === NaN
526+ const value = this . descendInput ( node . value ) ;
527+ if ( value . isAlwaysConstant ( ) ) {
528+ const val = toNotNaN ( + value . constantValue ) ;
529+ return new ConstantInput ( Math . acos ( val ) * 180 / Math . PI , false ) ;
530+ }
526531 this . usedMathFunctions . add ( 'acos' ) ;
527532 this . usedMathFunctions . add ( 'PI' ) ;
528- return new TypedInput ( `((acos(${ this . descendInput ( node . value ) . asNumber ( ) } ) * 180) / PI)` , TYPES . NUMBER_NAN ) ;
533+ return new TypedInput ( `((acos(${ value . asNumber ( ) } ) * 180) / PI)` , TYPES . NUMBER_NAN ) ;
534+ }
529535 case BLOCKS . OP . ADD : {
530536 // Needs to be marked as NaN because Infinity + -Infinity === NaN
531537 const left = this . descendInput ( node . left ) ;
@@ -705,15 +711,15 @@ class JSGenerator {
705711 if ( left . isAlwaysConstant ( ) && right . isAlwaysConstant ( ) ) {
706712 return new ConstantInput ( Cast . compare ( left . constantValue , right . constantValue ) > 0 , false ) ;
707713 }
708- if ( left . isAlwaysFinite ( ) && right . isAlwaysFinite ( ) ) {
714+ if ( left . isAlwaysNumber ( ) && right . isAlwaysNumberOrNaN ( ) ) {
709715 return new TypedInput ( `(${ left . asNumber ( ) } > ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
710716 }
711- if ( left . isAlwaysNumber ( ) && right . isAlwaysNumber ( ) ) {
712- return new TypedInput ( `(${ left . asNumber ( ) } > ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
717+ if ( left . isAlwaysNumberOrNaN ( ) && right . isAlwaysNumber ( ) ) {
718+ return new TypedInput ( `! (${ left . asNumber ( ) } <= ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
713719 }
714- // When either operand is known to never be a number, avoid all number parsing.
715- if ( left . isNeverNumber ( ) || right . isNeverNumber ( ) ) {
716- return new TypedInput ( `(${ left . asLowerString ( ) } > ${ right . asLowerString ( ) } )` , TYPES . BOOLEAN ) ;
720+ if ( ( left . isAlwaysNumber ( ) && right . isAlwaysNumber ( ) ) ||
721+ ( left . isNeverNumber ( ) || right . isNeverNumber ( ) ) ) {
722+ return new TypedInput ( `(${ left . asNumber ( ) } > ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
717723 }
718724 // No compile-time optimizations possible - use fallback method.
719725 return new TypedInput ( `compareGreaterThan(${ left . asUnknown ( ) } , ${ right . asUnknown ( ) } )` , TYPES . BOOLEAN ) ;
@@ -742,16 +748,15 @@ class JSGenerator {
742748 if ( left . isAlwaysConstant ( ) && right . isAlwaysConstant ( ) ) {
743749 return new ConstantInput ( Cast . compare ( left . constantValue , right . constantValue ) < 0 , false ) ;
744750 }
745-
746- if ( left . isAlwaysFinite ( ) && right . isAlwaysFinite ( ) ) {
751+ if ( left . isAlwaysNumber ( ) && right . isAlwaysNumberOrNaN ( ) ) {
747752 return new TypedInput ( `(${ left . asNumber ( ) } < ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
748753 }
749- if ( left . isAlwaysNumber ( ) && right . isAlwaysNumber ( ) ) {
750- return new TypedInput ( `(${ left . asNumber ( ) } < ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
754+ if ( left . isAlwaysNumberOrNaN ( ) && right . isAlwaysNumber ( ) ) {
755+ return new TypedInput ( `! (${ left . asNumber ( ) } >= ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
751756 }
752- // When either operand is known to never be a number, avoid all number parsing.
753- if ( left . isNeverNumber ( ) || right . isNeverNumber ( ) ) {
754- return new TypedInput ( `(${ left . asLowerString ( ) } < ${ right . asLowerString ( ) } )` , TYPES . BOOLEAN ) ;
757+ if ( ( left . isAlwaysNumber ( ) && right . isAlwaysNumber ( ) ) ||
758+ ( left . isNeverNumber ( ) || right . isNeverNumber ( ) ) ) {
759+ return new TypedInput ( `(${ left . asNumber ( ) } < ${ right . asNumber ( ) } )` , TYPES . BOOLEAN ) ;
755760 }
756761 // No compile-time optimizations possible - use fallback method.
757762 return new TypedInput ( `compareLessThan(${ left . asUnknown ( ) } , ${ right . asUnknown ( ) } )` , TYPES . BOOLEAN ) ;
@@ -772,15 +777,27 @@ class JSGenerator {
772777 }
773778 return new TypedInput ( `((${ string . asString ( ) } )[${ l } ] || "")` , TYPES . STRING ) ;
774779 }
775- case BLOCKS . OP . LN :
780+ case BLOCKS . OP . LN : {
776781 // Needs to be marked as NaN because Math.log(-1) == NaN
782+ const value = this . descendInput ( node . value ) ;
783+ if ( value . isAlwaysConstant ( ) ) {
784+ const val = toNotNaN ( + value . constantValue ) ;
785+ return new ConstantInput ( Math . log ( val ) , false ) ;
786+ }
777787 this . usedMathFunctions . add ( 'log' ) ;
778- return new TypedInput ( `log(${ this . descendInput ( node . value ) . asNumber ( ) } )` , TYPES . NUMBER_NAN ) ;
779- case BLOCKS . OP . LOG :
788+ return new TypedInput ( `log(${ value . asNumber ( ) } )` , TYPES . NUMBER_NAN ) ;
789+ }
790+ case BLOCKS . OP . LOG : {
780791 // Needs to be marked as NaN because Math.log(-1) == NaN
792+ const value = this . descendInput ( node . value ) ;
793+ if ( value . isAlwaysConstant ( ) ) {
794+ const val = toNotNaN ( + value . constantValue ) ;
795+ return new ConstantInput ( Math . log ( val ) / Math . LN10 , false ) ;
796+ }
781797 this . usedMathFunctions . add ( 'log' ) ;
782798 this . usedMathFunctions . add ( 'LN10' ) ;
783- return new TypedInput ( `(log(${ this . descendInput ( node . value ) . asNumber ( ) } ) / LN10)` , TYPES . NUMBER_NAN ) ;
799+ return new TypedInput ( `(log(${ value . asNumber ( ) } ) / LN10)` , TYPES . NUMBER_NAN ) ;
800+ }
784801 case BLOCKS . OP . MOD : {
785802 this . descendedIntoModulo = true ;
786803 const left = this . descendInput ( node . left ) ;
@@ -877,7 +894,8 @@ class JSGenerator {
877894 const procedureCode = node . code ;
878895 const procedureVariant = node . variant ;
879896 const procedureData = this . ir . procedures [ procedureVariant ] ;
880- if ( procedureData . stack === null ) {
897+ const stack = procedureData . stack ;
898+ if ( stack === null || stack . length === 0 ) {
881899 // Procedure has no body; still evaluate arguments for side effects
882900 const args = [ ] ;
883901 for ( const input of node . arguments ) {
@@ -889,6 +907,13 @@ class JSGenerator {
889907 return new TypedInput ( '""' , TYPES . STRING ) ;
890908 }
891909
910+ if ( node . arguments . length === 0 ) {
911+ if ( stack [ 0 ] . kind === BLOCKS . PROCEDURES . RETURN ) {
912+ const input = this . descendInput ( stack [ 0 ] . value ) ;
913+ return input ;
914+ }
915+ }
916+
892917 // Recursion makes this complicated because:
893918 // - We need to yield *between* each call in the same command block
894919 // - We need to evaluate arguments *before* that yield happens
@@ -1475,23 +1500,24 @@ class JSGenerator {
14751500 const procedureCode = node . code ;
14761501 const procedureVariant = node . variant ;
14771502 const procedureData = this . ir . procedures [ procedureVariant ] ;
1478- if ( procedureData . stack === null ) {
1503+ const stack = procedureData . stack ;
1504+ if ( stack === null || stack . length === 0 ) {
14791505 // Procedure has no body; still evaluate arguments for side effects
14801506 break ;
14811507 }
14821508
1483- if ( this . _canInlineProcedureCallInStack ( node , procedureData ) ) {
1484- this . _emitInlinedProcedureCallInStack ( node , procedureData ) ;
1485- this . resetVariableInputs ( ) ;
1486- this . clearVariableTypes ( ) ;
1487- break ;
1488- }
1489-
14901509 const yieldForRecursion = ! this . isWarp && procedureCode === this . script . procedureCode ;
14911510 if ( yieldForRecursion ) {
14921511 this . yieldNotWarp ( ) ;
14931512 }
14941513
1514+ if ( node . arguments . length === 0 && stack . length === 1 ) {
1515+ if ( [ BLOCKS . VAR . SET ] . includes ( stack [ 0 ] . kind ) ) {
1516+ this . descendStack ( stack , new Frame ( false ) ) ;
1517+ break ;
1518+ }
1519+ }
1520+
14951521 if ( procedureData . yields ) {
14961522 this . source += 'yield* ' ;
14971523 }
@@ -1692,7 +1718,9 @@ class JSGenerator {
16921718 stopScript ( ) {
16931719 this . _flushMonitorUpdates ( ) ;
16941720 if ( this . isProcedure ) {
1695- this . source += 'return "";\n' ;
1721+ if ( this . script . stack [ 0 ] ?. kind !== BLOCKS . PROCEDURES . RETURN ) {
1722+ this . source += 'return "";\n' ;
1723+ }
16961724 } else {
16971725 this . retire ( ) ;
16981726 }
0 commit comments