@@ -274,7 +274,7 @@ function generateStringConversion(valueType: string, cg: CodeGenerator): void {
274274function hashCode ( str : string ) : number {
275275 let hash = 0 ;
276276 for ( let i = 0 ; i < str . length ; i ++ ) {
277- hash = ( hash * 31 + str . charCodeAt ( i ) ) | 0 ; // Simulate Java's overflow behavior
277+ hash = ( ( hash * 31 ) + str . charCodeAt ( i ) ) ; // Simulate Java's overflow behavior
278278 }
279279 return hash ;
280280}
@@ -1301,7 +1301,6 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
13011301 const caseLabels : Label [ ] = cases . map ( ( ) => cg . generateNewLabel ( ) ) ;
13021302 const defaultLabel = cg . generateNewLabel ( ) ;
13031303 const endLabel = cg . generateNewLabel ( ) ;
1304- const positionOffset = cg . code . length ;
13051304
13061305 // Track the switch statement's end label
13071306 cg . switchLabels . push ( endLabel ) ;
@@ -1310,6 +1309,7 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
13101309 const caseValues : number [ ] = [ ] ;
13111310 const caseLabelMap : Map < number , Label > = new Map ( ) ;
13121311 let hasDefault = false ;
1312+ const positionOffset = cg . code . length ;
13131313
13141314 cases . forEach ( ( caseGroup , index ) => {
13151315 caseGroup . labels . forEach ( ( label ) => {
@@ -1433,33 +1433,25 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
14331433 cg . code [ caseLabelIndex [ i ] ] = caseLabels [ i - 1 ] . offset - positionOffset
14341434 }
14351435
1436- console . debug ( cg . code )
1437-
1438- console . debug ( caseLabels [ caseLabels . length - 1 ] )
1439- console . debug ( caseLabels . splice ( 0 , caseLabels . length - 1 ) )
1440-
14411436 endLabel . offset = cg . code . length ;
14421437
14431438 } else if ( resultType === "Ljava/lang/String;" ) {
1444- // **String cases **
1439+ // **String Switch Handling **
14451440 const hashCaseMap : Map < number , Label > = new Map ( ) ;
1446- const hashCodeVarIndex = cg . maxLocals ++ ;
14471441
1448- // Generate ` hashCode()` call
1442+ // Compute and store hashCode()
14491443 cg . code . push (
14501444 OPCODE . INVOKEVIRTUAL ,
14511445 0 ,
14521446 cg . constantPoolManager . indexMethodrefInfo ( "java/lang/String" , "hashCode" , "()I" )
14531447 ) ;
14541448
1455- // const switchCaseExpressionHash = hashCode();
1456- cg . code . push ( OPCODE . ISTORE , hashCodeVarIndex ) ;
1457-
1449+ // Create lookup table for hashCodes
14581450 cases . forEach ( ( caseGroup , index ) => {
14591451 caseGroup . labels . forEach ( ( label ) => {
14601452 if ( label . kind === "CaseLabel" ) {
14611453 const caseValue = ( label . expression as Literal ) . literalType . value ;
1462- const hashCodeValue = hashCode ( caseValue ) ;
1454+ const hashCodeValue = hashCode ( caseValue . slice ( 1 , caseValue . length - 1 ) ) ;
14631455 if ( ! hashCaseMap . has ( hashCodeValue ) ) {
14641456 hashCaseMap . set ( hashCodeValue , caseLabels [ index ] ) ;
14651457 }
@@ -1469,63 +1461,91 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
14691461 } ) ;
14701462 } ) ;
14711463
1472- // **Compare hashCodes**
1473- hashCaseMap . forEach ( ( label , hashCode ) => {
1474- cg . code . push ( OPCODE . ILOAD , hashCodeVarIndex ) ;
1475- cg . code . push ( OPCODE . BIPUSH , hashCode ) ;
1476- cg . addBranchInstr ( OPCODE . IF_ICMPEQ , label ) ;
1477- } ) ;
1464+ const caseLabelIndex : number [ ] = [ ]
1465+ let indexTracker = cg . code . length ;
1466+ const positionOffset = cg . code . length ;
14781467
1479- cg . addBranchInstr ( OPCODE . GOTO , defaultLabel ) ;
1468+ // **LOOKUPSWITCH Implementation**
1469+ cg . code . push ( OPCODE . LOOKUPSWITCH ) ;
1470+ indexTracker ++
14801471
1481- // **Process case bodies**
1482- let previousCase : SwitchCase | null = null ;
1472+ // Ensure 4-byte alignment
1473+ while ( cg . code . length % 4 !== 0 ) {
1474+ cg . code . push ( 0 ) ;
1475+ indexTracker ++
1476+ }
14831477
1484- const nonDefaultCases = cases . filter ( ( caseGroup ) =>
1485- caseGroup . labels . some ( ( label ) => label . kind === "CaseLabel" ) )
1478+ // Default jump target
1479+ cg . code . push ( 0 , 0 , 0 , defaultLabel . offset ) ;
1480+ caseLabelIndex . push ( indexTracker + 3 ) ;
1481+ indexTracker += 4 ;
14861482
1487- nonDefaultCases . forEach ( ( caseGroup , index ) => {
1488- caseLabels [ index ] . offset = cg . code . length ;
14891483
1490- // Ensure statements array is always defined
1491- caseGroup . statements = caseGroup . statements || [ ] ;
1484+ // Number of case-value pairs
1485+ cg . code . push ( ( hashCaseMap . size >> 24 ) & 0xff , ( hashCaseMap . size >> 16 ) & 0xff ,
1486+ ( hashCaseMap . size >> 8 ) & 0xff , hashCaseMap . size & 0xff ) ;
1487+ indexTracker += 4 ;
14921488
1493- // Handle fallthrough
1494- if ( previousCase && ( previousCase . statements ?. length ?? 0 ) === 0 ) {
1495- previousCase . labels . push ( ...caseGroup . labels ) ;
1496- }
1489+ // Populate LOOKUPSWITCH
1490+ hashCaseMap . forEach ( ( label , hashCode ) => {
1491+ cg . code . push ( hashCode >> 24 , ( hashCode >> 16 ) & 0xff , ( hashCode >> 8 ) & 0xff , hashCode & 0xff ) ;
1492+ cg . code . push ( 0 , 0 , 0 , label . offset ) ;
1493+ caseLabelIndex . push ( indexTracker + 7 ) ;
1494+ indexTracker += 8 ;
1495+ } ) ;
14971496
1498- // Generate string comparison
1499- const caseValue = caseGroup . labels . find ( ( label ) : label is CaseLabel => label . kind === "CaseLabel" ) ;
1500- if ( caseValue ) {
1501- const caseStr = ( caseValue . expression as Literal ) . literalType . value ;
1502- const caseStrIndex = cg . constantPoolManager . indexStringInfo ( caseStr ) ;
1503- cg . code . push ( OPCODE . LDC , caseStrIndex ) ;
1504- cg . code . push (
1505- OPCODE . INVOKEVIRTUAL ,
1506- 0 ,
1507- cg . constantPoolManager . indexMethodrefInfo ( "java/lang/String" , "equals" , "(Ljava/lang/Object;)Z" )
1508- ) ;
1509- const caseEndLabel = cg . generateNewLabel ( ) ;
1510- cg . addBranchInstr ( OPCODE . IFEQ , caseEndLabel ) ;
1497+ // **Case Handling**
1498+ let previousCase : SwitchCase | null = null ;
15111499
1512- // Compile case statements
1513- caseGroup . statements . forEach ( ( statement ) => {
1514- const { stackSize } = compile ( statement , cg ) ;
1515- maxStack = Math . max ( maxStack , stackSize ) ;
1516- } ) ;
1500+ cases . filter ( ( caseGroup ) =>
1501+ caseGroup . labels . some ( ( label ) => label . kind === "CaseLabel" ) )
1502+ . forEach ( ( caseGroup , index ) => {
1503+ caseLabels [ index ] . offset = cg . code . length ;
15171504
1518- caseEndLabel . offset = cg . code . length ;
1519- }
1505+ // Ensure statements exist
1506+ caseGroup . statements = caseGroup . statements || [ ] ;
15201507
1521- previousCase = caseGroup ;
1522- } ) ;
1508+ // Handle fallthrough
1509+ if ( previousCase && ( previousCase . statements ?. length ?? 0 ) === 0 ) {
1510+ previousCase . labels . push ( ...caseGroup . labels ) ;
1511+ }
15231512
1524- // **Process default case**
1513+ // **String Comparison for Collisions**
1514+ const caseValue = caseGroup . labels . find ( ( label ) : label is CaseLabel => label . kind === "CaseLabel" ) ;
1515+ if ( caseValue ) {
1516+ // TODO: check for actual String equality instead of just rely on hashCode equality
1517+ // (see the commented out code below)
1518+
1519+ // const caseStr = (caseValue.expression as Literal).literalType.value;
1520+ // const caseStrIndex = cg.constantPoolManager.indexStringInfo(caseStr);
1521+
1522+ // cg.code.push(OPCODE.LDC, caseStrIndex);
1523+ // cg.code.push(
1524+ // OPCODE.INVOKEVIRTUAL,
1525+ // 0,
1526+ // cg.constantPoolManager.indexMethodrefInfo("java/lang/String", "equals", "(Ljava/lang/Object;)Z")
1527+ // );
1528+ //
1529+ const caseEndLabel = cg . generateNewLabel ( ) ;
1530+ // cg.addBranchInstr(OPCODE.IFEQ, caseEndLabel);
1531+
1532+ // Compile case statements
1533+ caseGroup . statements . forEach ( ( statement ) => {
1534+ const { stackSize } = compile ( statement , cg ) ;
1535+ maxStack = Math . max ( maxStack , stackSize ) ;
1536+ } ) ;
1537+
1538+ caseEndLabel . offset = cg . code . length ;
1539+ }
1540+
1541+ previousCase = caseGroup ;
1542+ } ) ;
1543+
1544+ // **Default Case Handling**
15251545 defaultLabel . offset = cg . code . length ;
15261546 const defaultCase = cases . find ( ( caseGroup ) =>
1527- caseGroup . labels . some ( ( label ) => label . kind === "DefaultLabel" )
1528- ) ;
1547+ caseGroup . labels . some ( ( label ) => label . kind === "DefaultLabel" ) ) ;
1548+
15291549 if ( defaultCase ) {
15301550 defaultCase . statements = defaultCase . statements || [ ] ;
15311551 defaultCase . statements . forEach ( ( statement ) => {
@@ -1534,7 +1554,14 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
15341554 } ) ;
15351555 }
15361556
1557+ cg . code [ caseLabelIndex [ 0 ] ] = caseLabels [ caseLabels . length - 1 ] . offset - positionOffset ;
1558+
1559+ for ( let i = 1 ; i < caseLabelIndex . length ; i ++ ) {
1560+ cg . code [ caseLabelIndex [ i ] ] = caseLabels [ i - 1 ] . offset - positionOffset
1561+ }
1562+
15371563 endLabel . offset = cg . code . length ;
1564+
15381565 } else {
15391566 throw new Error ( `Switch statements only support byte, short, int, char, or String types. Found: ${ resultType } ` ) ;
15401567 }
0 commit comments