@@ -101,6 +101,8 @@ public class JavaScriptLifter: Lifter {
101101
102102        var  w  =  JavaScriptWriter ( analyzer:  analyzer,  version:  version,  stripComments:  !options. contains ( . includeComments) ,  includeLineNumbers:  options. contains ( . includeLineNumbers) ) 
103103
104+         var  loopLabelStack  =  LabelStack ( type:  . loopblock) 
105+ 
104106        var  wasmCodeStarts :  Int ? =  nil 
105107
106108        if  options. contains ( . includeComments) ,  let  header =  program. comments. at ( . header)  { 
@@ -1150,14 +1152,19 @@ public class JavaScriptLifter: Lifter {
11501152
11511153            case  . beginWhileLoopBody: 
11521154                let  COND  =  handleEndSingleExpressionContext ( result:  input ( 0 ) ,  with:  & w) 
1155+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
11531156                w. emitBlock ( " while ( \( COND) ) { " ) 
11541157                w. enterNewBlock ( ) 
11551158
1159+ 
11561160            case  . endWhileLoop: 
11571161                w. leaveCurrentBlock ( ) 
11581162                w. emit ( " } " ) 
1163+                 loopLabelStack. pop ( ) 
1164+ 
11591165
11601166            case  . beginDoWhileLoopBody: 
1167+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
11611168                w. emit ( " do { " ) 
11621169                w. enterNewBlock ( ) 
11631170
@@ -1168,6 +1175,7 @@ public class JavaScriptLifter: Lifter {
11681175            case  . endDoWhileLoop: 
11691176                let  COND  =  handleEndSingleExpressionContext ( result:  input ( 0 ) ,  with:  & w) 
11701177                w. emitBlock ( " } while ( \( COND) ) " ) 
1178+                 loopLabelStack. pop ( ) 
11711179
11721180            case  . beginForLoopInitializer: 
11731181                // While we could inline into the loop header, we probably don't want to do that as it will often lead
@@ -1243,6 +1251,7 @@ public class JavaScriptLifter: Lifter {
12431251                let  INITIALIZER  =  header. initializer
12441252                var  CONDITION  =  header. condition
12451253                var  AFTERTHOUGHT  =  handleEndSingleExpressionContext ( with:  & w) 
1254+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
12461255
12471256                if  !INITIALIZER. contains ( " \n " )  && !CONDITION. contains ( " \n " )  && !AFTERTHOUGHT. contains ( " \n " )  { 
12481257                    if  !CONDITION. isEmpty {  CONDITION =  "   "  +  CONDITION } 
@@ -1262,36 +1271,48 @@ public class JavaScriptLifter: Lifter {
12621271            case  . endForLoop: 
12631272                w. leaveCurrentBlock ( ) 
12641273                w. emit ( " } " ) 
1274+                 loopLabelStack. pop ( ) 
1275+ 
12651276
12661277            case  . beginForInLoop: 
12671278                let  LET  =  w. declarationKeyword ( for:  instr. innerOutput) 
12681279                let  V  =  w. declare ( instr. innerOutput) 
12691280                let  OBJ  =  input ( 0 ) 
1281+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
12701282                w. emit ( " for ( \( LET)   \( V)  in  \( OBJ) ) { " ) 
12711283                w. enterNewBlock ( ) 
12721284
1285+ 
12731286            case  . endForInLoop: 
12741287                w. leaveCurrentBlock ( ) 
12751288                w. emit ( " } " ) 
1289+                 loopLabelStack. pop ( ) 
1290+ 
12761291
12771292            case  . beginForOfLoop: 
12781293                let  V  =  w. declare ( instr. innerOutput) 
12791294                let  LET  =  w. declarationKeyword ( for:  instr. innerOutput) 
12801295                let  OBJ  =  input ( 0 ) 
1296+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
12811297                w. emit ( " for ( \( LET)   \( V)  of  \( OBJ) ) { " ) 
12821298                w. enterNewBlock ( ) 
12831299
1300+ 
12841301            case  . beginForOfLoopWithDestruct( let  op) : 
12851302                let  outputs  =  w. declareAll ( instr. innerOutputs) 
12861303                let  PATTERN  =  liftArrayDestructPattern ( indices:  op. indices,  outputs:  outputs,  hasRestElement:  op. hasRestElement) 
12871304                let  LET  =  w. varKeyword
12881305                let  OBJ  =  input ( 0 ) 
1306+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
12891307                w. emit ( " for ( \( LET)  [ \( PATTERN) ] of  \( OBJ) ) { " ) 
12901308                w. enterNewBlock ( ) 
12911309
1310+ 
12921311            case  . endForOfLoop: 
12931312                w. leaveCurrentBlock ( ) 
12941313                w. emit ( " } " ) 
1314+                 loopLabelStack. pop ( ) 
1315+ 
12951316
12961317            case  . beginRepeatLoop( let  op) : 
12971318                let  LET  =  w. varKeyword
@@ -1302,12 +1323,16 @@ public class JavaScriptLifter: Lifter {
13021323                    I =  " i " 
13031324                } 
13041325                let  ITERATIONS  =  op. iterations
1326+                 loopLabelStack. push ( currentCodeLength:  w. code. count) 
13051327                w. emit ( " for ( \( LET)   \( I)  = 0;  \( I)  <  \( ITERATIONS) ;  \( I) ++) { " ) 
13061328                w. enterNewBlock ( ) 
13071329
1330+ 
13081331            case  . endRepeatLoop: 
13091332                w. leaveCurrentBlock ( ) 
13101333                w. emit ( " } " ) 
1334+                 loopLabelStack. pop ( ) 
1335+ 
13111336
13121337            case  . loopBreak( _) , 
13131338                 . switchBreak: 
@@ -1361,6 +1386,7 @@ public class JavaScriptLifter: Lifter {
13611386                w. emit ( " { " ) 
13621387                w. enterNewBlock ( ) 
13631388
1389+ 
13641390            case  . endBlockStatement: 
13651391                w. leaveCurrentBlock ( ) 
13661392                w. emit ( " } " ) 
@@ -1372,6 +1398,11 @@ public class JavaScriptLifter: Lifter {
13721398                let  VALUE  =  input ( 0 ) 
13731399                w. emit ( " fuzzilli('FUZZILLI_PRINT',  \( VALUE) ); " ) 
13741400
1401+             case  . loopNestedContinue( let  op) : 
1402+                 w. withScriptWriter  {  writer in 
1403+                     loopLabelStack. translateContinueLabel ( w:  & writer,  expDepth:  op. depth) 
1404+                 } 
1405+ 
13751406            case  . createWasmGlobal( let  op) : 
13761407                let  V  =  w. declare ( instr. output) 
13771408                let  LET  =  w. varKeyword
@@ -1818,6 +1849,7 @@ public class JavaScriptLifter: Lifter {
18181849        } 
18191850    } 
18201851
1852+ 
18211853    /// A wrapper around a ScriptWriter. It's main responsibility is expression inlining.
18221854    ///
18231855    /// Expression inlining roughly works as follows:
@@ -2236,6 +2268,10 @@ public class JavaScriptLifter: Lifter {
22362268                return  analyzer. numUses ( of:  v)  <=  1 
22372269            } 
22382270        } 
2271+ 
2272+         mutating  func  withScriptWriter( _ body:  ( inout  ScriptWriter )  ->  Void )  { 
2273+             body ( & writer) 
2274+         } 
22392275    } 
22402276
22412277    // Helper class for formatting object literals.
@@ -2268,4 +2304,89 @@ public class JavaScriptLifter: Lifter {
22682304            fields [ fields. count -  1 ]  +=  body +  " } " 
22692305        } 
22702306    } 
2307+ 
2308+     // Every possible position of label
2309+     struct  LabelPin  { 
2310+         var  beginPos :  Int 
2311+         var  hasLabel :  Bool 
2312+     } 
2313+ 
2314+     enum  LabelType  { 
2315+         case  loopblock
2316+         case  ifblock
2317+         case  switchblock
2318+         case  tryblock
2319+         case  codeBlock
2320+         case  withblock
2321+     } 
2322+ 
2323+     /// A structure that manages label positions within a specific control flow block (e.g., loop, if, switch, etc.).
2324+     public  struct  LabelStack  { 
2325+         let  type :  LabelType 
2326+ 
2327+         private  var  stack :  [ LabelPin ]  =  [ ] 
2328+ 
2329+         init ( type:  LabelType )  { 
2330+             self . type =  type
2331+         } 
2332+ 
2333+         /// Records a new label pin at the current code position.
2334+         mutating  func  push( currentCodeLength:  Int )  { 
2335+             stack. append ( LabelPin ( beginPos:  currentCodeLength,  hasLabel:  false ) ) 
2336+         } 
2337+ 
2338+         /// Removes the most recently recorded label pin.
2339+         mutating  func  pop( )  { 
2340+             _ =  stack. popLast ( ) 
2341+         } 
2342+ 
2343+         /// Checks whether a label has already been inserted at the specified index.
2344+         func  labelExists( at index:  Int )  ->  Bool  { 
2345+             return  stack [ index] . hasLabel
2346+         } 
2347+ 
2348+         /// Updates the label stack when a label is inserted into the code.
2349+         ///
2350+         /// This method:
2351+         /// - Marks the label at the specified index as inserted.
2352+         /// - Inserts the label content at the given code position.
2353+         /// - Shifts the positions of subsequent labels accordingly.
2354+         mutating  func  insertLabel( writer:  inout  ScriptWriter ,  at index:  Int ,  labelContent:  String )  { 
2355+             guard  index <  stack. count else  {  return  } 
2356+ 
2357+             let  insertPos  =  stack [ index] . beginPos
2358+             writer. insert ( insertPos,  labelContent) 
2359+ 
2360+             stack [ index] . hasLabel =  true 
2361+ 
2362+             let  delta  =  labelContent. count
2363+             for  i  in  index+ 1 ..< stack. count { 
2364+                 stack [ i] . beginPos +=  delta
2365+             } 
2366+         } 
2367+ 
2368+         mutating  func  translateBreakLabel( w:  inout  ScriptWriter ,  expDepth:  Int ) { 
2369+             let  d  =  expDepth %  stack. count
2370+             let  pre  =  String ( repeating:  "   " ,  count:  4  *  d) 
2371+             let  s  =  pre +  " label "  +  String( d)  +  " : \n " 
2372+             if ( !stack[ d] . hasLabel) { 
2373+                 insertLabel ( writer:  & w,  at:  d,  labelContent:  s) 
2374+             } 
2375+             w. emit ( " break  "  +  " label "  +  String( d)  +  " ; " ) 
2376+         } 
2377+ 
2378+         mutating  func  translateContinueLabel( w:  inout  ScriptWriter ,  expDepth:  Int ) { 
2379+             let  d  =  expDepth %  stack. count
2380+             let  pre  =  String ( repeating:  "   " ,  count:  4  *  d) 
2381+             let  s  =  pre +  " label "  +  String( d)  +  " : \n " 
2382+             if ( !stack[ d] . hasLabel) { 
2383+                 insertLabel ( writer:  & w,  at:  d,  labelContent:  s) 
2384+             } 
2385+             w. emit ( " continue  "  +  " label "  +  String( d)  +  " ; " ) 
2386+         } 
2387+ 
2388+         mutating  func  depth( )  ->  Int { 
2389+             return  stack. count
2390+         } 
2391+     } 
22712392} 
0 commit comments