@@ -1150,14 +1150,17 @@ public class JavaScriptLifter: Lifter {
11501150
11511151 case . beginWhileLoopBody:
11521152 let COND = handleEndSingleExpressionContext ( result: input ( 0 ) , with: & w)
1153+ w. pushLabelStack ( LabelType . loopblock)
11531154 w. emitBlock ( " while ( \( COND) ) { " )
11541155 w. enterNewBlock ( )
11551156
11561157 case . endWhileLoop:
11571158 w. leaveCurrentBlock ( )
11581159 w. emit ( " } " )
1160+ w. popLabelStack ( LabelType . loopblock)
11591161
11601162 case . beginDoWhileLoopBody:
1163+ w. pushLabelStack ( LabelType . loopblock)
11611164 w. emit ( " do { " )
11621165 w. enterNewBlock ( )
11631166
@@ -1168,6 +1171,7 @@ public class JavaScriptLifter: Lifter {
11681171 case . endDoWhileLoop:
11691172 let COND = handleEndSingleExpressionContext ( result: input ( 0 ) , with: & w)
11701173 w. emitBlock ( " } while ( \( COND) ) " )
1174+ w. popLabelStack ( LabelType . loopblock)
11711175
11721176 case . beginForLoopInitializer:
11731177 // While we could inline into the loop header, we probably don't want to do that as it will often lead
@@ -1243,6 +1247,7 @@ public class JavaScriptLifter: Lifter {
12431247 let INITIALIZER = header. initializer
12441248 var CONDITION = header. condition
12451249 var AFTERTHOUGHT = handleEndSingleExpressionContext ( with: & w)
1250+ w. pushLabelStack ( LabelType . loopblock)
12461251
12471252 if !INITIALIZER. contains ( " \n " ) && !CONDITION. contains ( " \n " ) && !AFTERTHOUGHT. contains ( " \n " ) {
12481253 if !CONDITION. isEmpty { CONDITION = " " + CONDITION }
@@ -1262,22 +1267,26 @@ public class JavaScriptLifter: Lifter {
12621267 case . endForLoop:
12631268 w. leaveCurrentBlock ( )
12641269 w. emit ( " } " )
1270+ w. popLabelStack ( LabelType . loopblock)
12651271
12661272 case . beginForInLoop:
12671273 let LET = w. declarationKeyword ( for: instr. innerOutput)
12681274 let V = w. declare ( instr. innerOutput)
12691275 let OBJ = input ( 0 )
1276+ w. pushLabelStack ( LabelType . loopblock)
12701277 w. emit ( " for ( \( LET) \( V) in \( OBJ) ) { " )
12711278 w. enterNewBlock ( )
12721279
12731280 case . endForInLoop:
12741281 w. leaveCurrentBlock ( )
12751282 w. emit ( " } " )
1283+ w. popLabelStack ( LabelType . loopblock)
12761284
12771285 case . beginForOfLoop:
12781286 let V = w. declare ( instr. innerOutput)
12791287 let LET = w. declarationKeyword ( for: instr. innerOutput)
12801288 let OBJ = input ( 0 )
1289+ w. pushLabelStack ( LabelType . loopblock)
12811290 w. emit ( " for ( \( LET) \( V) of \( OBJ) ) { " )
12821291 w. enterNewBlock ( )
12831292
@@ -1286,12 +1295,14 @@ public class JavaScriptLifter: Lifter {
12861295 let PATTERN = liftArrayDestructPattern ( indices: op. indices, outputs: outputs, hasRestElement: op. hasRestElement)
12871296 let LET = w. varKeyword
12881297 let OBJ = input ( 0 )
1298+ w. pushLabelStack ( LabelType . loopblock)
12891299 w. emit ( " for ( \( LET) [ \( PATTERN) ] of \( OBJ) ) { " )
12901300 w. enterNewBlock ( )
12911301
12921302 case . endForOfLoop:
12931303 w. leaveCurrentBlock ( )
12941304 w. emit ( " } " )
1305+ w. popLabelStack ( LabelType . loopblock)
12951306
12961307 case . beginRepeatLoop( let op) :
12971308 let LET = w. varKeyword
@@ -1302,12 +1313,14 @@ public class JavaScriptLifter: Lifter {
13021313 I = " i "
13031314 }
13041315 let ITERATIONS = op. iterations
1316+ w. pushLabelStack ( LabelType . loopblock)
13051317 w. emit ( " for ( \( LET) \( I) = 0; \( I) < \( ITERATIONS) ; \( I) ++) { " )
13061318 w. enterNewBlock ( )
13071319
13081320 case . endRepeatLoop:
13091321 w. leaveCurrentBlock ( )
13101322 w. emit ( " } " )
1323+ w. popLabelStack ( LabelType . loopblock)
13111324
13121325 case . loopBreak( _) ,
13131326 . switchBreak:
@@ -1372,6 +1385,9 @@ public class JavaScriptLifter: Lifter {
13721385 let VALUE = input ( 0 )
13731386 w. emit ( " fuzzilli('FUZZILLI_PRINT', \( VALUE) ); " )
13741387
1388+ case . loopNestedContinue( let op) :
1389+ w. liftContinueLabel ( LabelType . loopblock, expDepth: op. depth)
1390+
13751391 case . createWasmGlobal( let op) :
13761392 let V = w. declare ( instr. output)
13771393 let LET = w. varKeyword
@@ -1640,6 +1656,7 @@ public class JavaScriptLifter: Lifter {
16401656 w. emitComment ( footer)
16411657 }
16421658
1659+ assert ( w. labelStackDepth ( LabelType . loopblock) == 0 )
16431660 return w. code
16441661 }
16451662
@@ -1846,6 +1863,11 @@ public class JavaScriptLifter: Lifter {
18461863 return writer. code
18471864 }
18481865
1866+ // Used for nestBreak series operations to record location information during append code, where temporary data structures such as temporaryOutputBufferStack may not necessarily be empty
1867+ var codeLength : Int {
1868+ return writer. code. count
1869+ }
1870+
18491871 // Maps each FuzzIL variable to its JavaScript expression.
18501872 // The expression for a FuzzIL variable can generally either be
18511873 // * an identifier like "v42" if the FuzzIL variable is mapped to a JavaScript variable OR
@@ -1861,6 +1883,11 @@ public class JavaScriptLifter: Lifter {
18611883 // See `reassign()` for more details about reassignment inlining.
18621884 private var inlinedReassignments = VariableMap < Expression > ( )
18631885
1886+ // Trace nested code block to break/continue a label
1887+ private var labelStack : [ LabelType : [ LabelPin ] ] = [
1888+ LabelType . loopblock: [ ]
1889+ ]
1890+
18641891 init ( analyzer: DefUseAnalyzer , version: ECMAScriptVersion , stripComments: Bool = false , includeLineNumbers: Bool = false , indent: Int = 4 ) {
18651892 self . writer = ScriptWriter ( stripComments: stripComments, includeLineNumbers: includeLineNumbers, indent: indent)
18661893 self . analyzer = analyzer
@@ -2236,6 +2263,66 @@ public class JavaScriptLifter: Lifter {
22362263 return analyzer. numUses ( of: v) <= 1
22372264 }
22382265 }
2266+
2267+ /// Records a new label pin at the current code position.
2268+ mutating func pushLabelStack( _ labelStackType: LabelType ) {
2269+ labelStack [ labelStackType, default: [ ] ] . append (
2270+ LabelPin (
2271+ beginPos: codeLength,
2272+ hasLabel: false ,
2273+ indention: writer. getCurrentIndention ( )
2274+ )
2275+ )
2276+ }
2277+
2278+ /// Removes the most recently recorded label pin.
2279+ mutating func popLabelStack( _ labelStackType: LabelType ) {
2280+ _ = labelStack [ labelStackType, default: [ ] ] . popLast ( )
2281+ }
2282+
2283+ /// Checks whether a label has already been inserted at the specified index.
2284+ private mutating func labelExists( _ labelStack: inout [ LabelPin ] , at index: Int ) -> Bool {
2285+ return labelStack [ index] . hasLabel
2286+ }
2287+
2288+ /// Updates the label stack when a label is inserted into the code.
2289+ ///
2290+ /// This method:
2291+ /// - Marks the label at the specified index as inserted.
2292+ /// - Inserts the label content at the given code position.
2293+ /// - Shifts the positions of subsequent labels accordingly.
2294+ private mutating func insertLabel( _ type: LabelType , _ index: Int , _ labelContent: String ) {
2295+ var stack = labelStack [ type] !
2296+ let insertPos = stack [ index] . beginPos
2297+ let indention = stack [ index] . indention
2298+
2299+ writer. insert ( insertPos, labelContent, indention)
2300+
2301+ stack [ index] . hasLabel = true
2302+ let delta = labelContent. count
2303+
2304+ for i in index+ 1 ..< stack. count {
2305+ stack [ i] . beginPos += delta
2306+ }
2307+
2308+ labelStack [ type] = stack
2309+ }
2310+
2311+ mutating func liftContinueLabel( _ type: LabelType , expDepth: Int ) {
2312+ let stack = labelStack [ type] !
2313+ let d = expDepth % stack. count
2314+ let labelName = " label \( d) : "
2315+
2316+ if !stack[ d] . hasLabel {
2317+ insertLabel ( type, d, labelName)
2318+ }
2319+
2320+ emit ( " continue label \( d) ; " )
2321+ }
2322+
2323+ mutating func labelStackDepth( _ type: LabelType ) -> Int {
2324+ return labelStack [ type] !. count
2325+ }
22392326 }
22402327
22412328 // Helper class for formatting object literals.
@@ -2268,4 +2355,15 @@ public class JavaScriptLifter: Lifter {
22682355 fields [ fields. count - 1 ] += body + " } "
22692356 }
22702357 }
2358+
2359+ // Every possible position of label
2360+ struct LabelPin {
2361+ var beginPos : Int
2362+ var hasLabel : Bool
2363+ var indention : String
2364+ }
2365+
2366+ enum LabelType {
2367+ case loopblock
2368+ }
22712369}
0 commit comments