@@ -101,6 +101,8 @@ public class JavaScriptLifter: Lifter {
101
101
102
102
var w = JavaScriptWriter ( analyzer: analyzer, version: version, stripComments: !options. contains ( . includeComments) , includeLineNumbers: options. contains ( . includeLineNumbers) )
103
103
104
+ var loopLabelStack = LabelStack ( type: . loopblock)
105
+
104
106
var wasmCodeStarts : Int ? = nil
105
107
106
108
if options. contains ( . includeComments) , let header = program. comments. at ( . header) {
@@ -1150,14 +1152,19 @@ public class JavaScriptLifter: Lifter {
1150
1152
1151
1153
case . beginWhileLoopBody:
1152
1154
let COND = handleEndSingleExpressionContext ( result: input ( 0 ) , with: & w)
1155
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1153
1156
w. emitBlock ( " while ( \( COND) ) { " )
1154
1157
w. enterNewBlock ( )
1155
1158
1159
+
1156
1160
case . endWhileLoop:
1157
1161
w. leaveCurrentBlock ( )
1158
1162
w. emit ( " } " )
1163
+ loopLabelStack. pop ( )
1164
+
1159
1165
1160
1166
case . beginDoWhileLoopBody:
1167
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1161
1168
w. emit ( " do { " )
1162
1169
w. enterNewBlock ( )
1163
1170
@@ -1168,6 +1175,7 @@ public class JavaScriptLifter: Lifter {
1168
1175
case . endDoWhileLoop:
1169
1176
let COND = handleEndSingleExpressionContext ( result: input ( 0 ) , with: & w)
1170
1177
w. emitBlock ( " } while ( \( COND) ) " )
1178
+ loopLabelStack. pop ( )
1171
1179
1172
1180
case . beginForLoopInitializer:
1173
1181
// 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 {
1243
1251
let INITIALIZER = header. initializer
1244
1252
var CONDITION = header. condition
1245
1253
var AFTERTHOUGHT = handleEndSingleExpressionContext ( with: & w)
1254
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1246
1255
1247
1256
if !INITIALIZER. contains ( " \n " ) && !CONDITION. contains ( " \n " ) && !AFTERTHOUGHT. contains ( " \n " ) {
1248
1257
if !CONDITION. isEmpty { CONDITION = " " + CONDITION }
@@ -1262,36 +1271,48 @@ public class JavaScriptLifter: Lifter {
1262
1271
case . endForLoop:
1263
1272
w. leaveCurrentBlock ( )
1264
1273
w. emit ( " } " )
1274
+ loopLabelStack. pop ( )
1275
+
1265
1276
1266
1277
case . beginForInLoop:
1267
1278
let LET = w. declarationKeyword ( for: instr. innerOutput)
1268
1279
let V = w. declare ( instr. innerOutput)
1269
1280
let OBJ = input ( 0 )
1281
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1270
1282
w. emit ( " for ( \( LET) \( V) in \( OBJ) ) { " )
1271
1283
w. enterNewBlock ( )
1272
1284
1285
+
1273
1286
case . endForInLoop:
1274
1287
w. leaveCurrentBlock ( )
1275
1288
w. emit ( " } " )
1289
+ loopLabelStack. pop ( )
1290
+
1276
1291
1277
1292
case . beginForOfLoop:
1278
1293
let V = w. declare ( instr. innerOutput)
1279
1294
let LET = w. declarationKeyword ( for: instr. innerOutput)
1280
1295
let OBJ = input ( 0 )
1296
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1281
1297
w. emit ( " for ( \( LET) \( V) of \( OBJ) ) { " )
1282
1298
w. enterNewBlock ( )
1283
1299
1300
+
1284
1301
case . beginForOfLoopWithDestruct( let op) :
1285
1302
let outputs = w. declareAll ( instr. innerOutputs)
1286
1303
let PATTERN = liftArrayDestructPattern ( indices: op. indices, outputs: outputs, hasRestElement: op. hasRestElement)
1287
1304
let LET = w. varKeyword
1288
1305
let OBJ = input ( 0 )
1306
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1289
1307
w. emit ( " for ( \( LET) [ \( PATTERN) ] of \( OBJ) ) { " )
1290
1308
w. enterNewBlock ( )
1291
1309
1310
+
1292
1311
case . endForOfLoop:
1293
1312
w. leaveCurrentBlock ( )
1294
1313
w. emit ( " } " )
1314
+ loopLabelStack. pop ( )
1315
+
1295
1316
1296
1317
case . beginRepeatLoop( let op) :
1297
1318
let LET = w. varKeyword
@@ -1302,12 +1323,16 @@ public class JavaScriptLifter: Lifter {
1302
1323
I = " i "
1303
1324
}
1304
1325
let ITERATIONS = op. iterations
1326
+ loopLabelStack. push ( currentCodeLength: w. code. count)
1305
1327
w. emit ( " for ( \( LET) \( I) = 0; \( I) < \( ITERATIONS) ; \( I) ++) { " )
1306
1328
w. enterNewBlock ( )
1307
1329
1330
+
1308
1331
case . endRepeatLoop:
1309
1332
w. leaveCurrentBlock ( )
1310
1333
w. emit ( " } " )
1334
+ loopLabelStack. pop ( )
1335
+
1311
1336
1312
1337
case . loopBreak( _) ,
1313
1338
. switchBreak:
@@ -1361,6 +1386,7 @@ public class JavaScriptLifter: Lifter {
1361
1386
w. emit ( " { " )
1362
1387
w. enterNewBlock ( )
1363
1388
1389
+
1364
1390
case . endBlockStatement:
1365
1391
w. leaveCurrentBlock ( )
1366
1392
w. emit ( " } " )
@@ -1372,6 +1398,11 @@ public class JavaScriptLifter: Lifter {
1372
1398
let VALUE = input ( 0 )
1373
1399
w. emit ( " fuzzilli('FUZZILLI_PRINT', \( VALUE) ); " )
1374
1400
1401
+ case . loopNestedContinue( let op) :
1402
+ w. withScriptWriter { writer in
1403
+ loopLabelStack. translateContinueLabel ( w: & writer, expDepth: op. depth)
1404
+ }
1405
+
1375
1406
case . createWasmGlobal( let op) :
1376
1407
let V = w. declare ( instr. output)
1377
1408
let LET = w. varKeyword
@@ -1818,6 +1849,7 @@ public class JavaScriptLifter: Lifter {
1818
1849
}
1819
1850
}
1820
1851
1852
+
1821
1853
/// A wrapper around a ScriptWriter. It's main responsibility is expression inlining.
1822
1854
///
1823
1855
/// Expression inlining roughly works as follows:
@@ -2236,6 +2268,10 @@ public class JavaScriptLifter: Lifter {
2236
2268
return analyzer. numUses ( of: v) <= 1
2237
2269
}
2238
2270
}
2271
+
2272
+ mutating func withScriptWriter( _ body: ( inout ScriptWriter ) -> Void ) {
2273
+ body ( & writer)
2274
+ }
2239
2275
}
2240
2276
2241
2277
// Helper class for formatting object literals.
@@ -2268,4 +2304,89 @@ public class JavaScriptLifter: Lifter {
2268
2304
fields [ fields. count - 1 ] += body + " } "
2269
2305
}
2270
2306
}
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
+ }
2271
2392
}
0 commit comments