@@ -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