From c76ea0edfb5a59e0e762a2c1a01351f4604b3935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Thu, 9 Mar 2023 15:01:17 +0100 Subject: [PATCH] Redesign For-Loops in FuzzIL Similar to while- and do-while loops, for-loops now consists of multiple blocks: BeginForLoopInitializer // ... // v0 = initial value of the (single) loop variable BeginForLoopCondition v0 -> v1 // v1 = current value of the (single) loop variable // ... BeginForLoopAfterthought -> v2 // v2 = current value of the (single) loop variable // ... BeginForLoopBody -> v3 // v3 = current value of the (single) loop variable // ... EndForLoop A simple for-loop will be lifted to: for (let i = init; cond; afterthought) { body(i); } However, more (and less) complex for loops are also possible. Some examples include: for (;;) { body(); } for (let i4 = f1(), i5 = f2(); f3(i4), f4(i5); f5(i5), f6(i4)) { body(i4, i5); } for (let [i6, i7] = (() => { const v1 = f(); const v3 = g(); h(); return [v1, v3]; })(); i6 < i7; i6 += 2) { body(i6, i7); } See the added tests for yet more examples. --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 43 +- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 27 +- .../Fuzzilli/CodeGen/ProgramTemplates.swift | 14 +- Sources/Fuzzilli/Compiler/Compiler.swift | 93 ++-- Sources/Fuzzilli/Compiler/Parser/parser.js | 83 ++-- Sources/Fuzzilli/FuzzIL/Code.swift | 17 + Sources/Fuzzilli/FuzzIL/Instruction.swift | 61 ++- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 48 +- Sources/Fuzzilli/FuzzIL/JsOperations.swift | 87 +++- Sources/Fuzzilli/FuzzIL/Opcodes.swift | 7 +- Sources/Fuzzilli/FuzzIL/Semantics.swift | 10 +- Sources/Fuzzilli/Fuzzer.swift | 2 +- Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 40 +- .../Fuzzilli/Lifting/JavaScriptLifter.swift | 330 +++++++++++--- Sources/Fuzzilli/Lifting/ScriptWriter.swift | 3 +- .../Fuzzilli/Minimization/BlockReducer.swift | 27 +- .../Fuzzilli/Mutators/OperationMutator.swift | 6 - Sources/Fuzzilli/Protobuf/ast.pb.swift | 295 ++++++++++-- Sources/Fuzzilli/Protobuf/ast.proto | 26 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 199 +++++--- Sources/Fuzzilli/Protobuf/operations.proto | 29 +- Sources/Fuzzilli/Protobuf/program.pb.swift | 327 ++++++++----- Sources/Fuzzilli/Protobuf/program.proto | 21 +- .../FuzzilliCli/CodeGeneratorWeights.swift | 3 +- Sources/FuzzilliCli/Profiles/JSCProfile.swift | 4 +- .../FuzzilliCli/Profiles/QtjsProfile.swift | 2 +- .../Profiles/SpidermonkeyProfile.swift | 2 +- Sources/FuzzilliCli/Profiles/V8Profile.swift | 8 +- Tests/FuzzilliTests/AnalyzerTest.swift | 23 +- .../CompilerTests/advanced_loops.js | 27 +- Tests/FuzzilliTests/JSTyperTests.swift | 60 ++- Tests/FuzzilliTests/LifterTest.swift | 428 ++++++++++++++++-- Tests/FuzzilliTests/ProgramBuilderTest.swift | 39 +- 33 files changed, 1792 insertions(+), 599 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 2812f891..b8285589 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -2190,9 +2190,42 @@ public class ProgramBuilder { emit(EndDoWhileLoop(), withInputs: [cond]) } - public func buildForLoop(_ start: Variable, _ comparator: Comparator, _ end: Variable, _ op: BinaryOperator, _ rhs: Variable, _ body: (Variable) -> ()) { - let i = emit(BeginForLoop(comparator: comparator, op: op), withInputs: [start, end, rhs]).innerOutput - body(i) + // Build a simple for loop that declares one loop variable. + public func buildForLoop(i initializer: () -> Variable, _ cond: (Variable) -> Variable, _ afterthought: (Variable) -> (), _ body: (Variable) -> ()) { + emit(BeginForLoopInitializer()) + let initialValue = initializer() + var loopVar = emit(BeginForLoopCondition(numLoopVariables: 1), withInputs: [initialValue]).innerOutput + let cond = cond(loopVar) + loopVar = emit(BeginForLoopAfterthought(numLoopVariables: 1), withInputs: [cond]).innerOutput + afterthought(loopVar) + loopVar = emit(BeginForLoopBody(numLoopVariables: 1)).innerOutput + body(loopVar) + emit(EndForLoop()) + } + + // Build arbitrarily complex for loops without any loop variables. + public func buildForLoop(_ initializer: (() -> ())? = nil, _ cond: (() -> Variable)? = nil, _ afterthought: (() -> ())? = nil, _ body: () -> ()) { + emit(BeginForLoopInitializer()) + initializer?() + emit(BeginForLoopCondition(numLoopVariables: 0)) + let cond = cond?() ?? loadBool(true) + emit(BeginForLoopAfterthought(numLoopVariables: 0), withInputs: [cond]) + afterthought?() + emit(BeginForLoopBody(numLoopVariables: 0)) + body() + emit(EndForLoop()) + } + + // Build arbitrarily complex for loops with one or more loop variables. + public func buildForLoop(_ initializer: () -> [Variable], _ cond: (([Variable]) -> Variable)? = nil, _ afterthought: (([Variable]) -> ())? = nil, _ body: ([Variable]) -> ()) { + emit(BeginForLoopInitializer()) + let initialValues = initializer() + var loopVars = emit(BeginForLoopCondition(numLoopVariables: initialValues.count), withInputs: initialValues).innerOutputs + let cond = cond?(Array(loopVars)) ?? loadBool(true) + loopVars = emit(BeginForLoopAfterthought(numLoopVariables: initialValues.count), withInputs: [cond]).innerOutputs + afterthought?(Array(loopVars)) + loopVars = emit(BeginForLoopBody(numLoopVariables: initialValues.count)).innerOutputs + body(Array(loopVars)) emit(EndForLoop()) } @@ -2209,12 +2242,12 @@ public class ProgramBuilder { } public func buildForOfLoop(_ obj: Variable, selecting indices: [Int64], hasRestElement: Bool = false, _ body: ([Variable]) -> ()) { - let instr = emit(BeginForOfWithDestructLoop(indices: indices, hasRestElement: hasRestElement), withInputs: [obj]) + let instr = emit(BeginForOfLoopWithDestruct(indices: indices, hasRestElement: hasRestElement), withInputs: [obj]) body(Array(instr.innerOutputs)) emit(EndForOfLoop()) } - public func buildRepeat(n numIterations: Int, _ body: (Variable) -> ()) { + public func buildRepeatLoop(n numIterations: Int, _ body: (Variable) -> ()) { let i = emit(BeginRepeatLoop(iterations: numIterations)).innerOutput body(i) emit(EndRepeatLoop()) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 4ac08a28..c9a95f9e 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -1176,15 +1176,28 @@ public let CodeGenerators: [CodeGenerator] = [ }, while: { b.compare(loopVar, with: b.loadInt(Int64.random(in: 0...10)), using: .lessThan) }) }, - RecursiveCodeGenerator("ForLoopGenerator") { b in - let start = b.reuseOrLoadInt(0) - let end = b.reuseOrLoadInt(Int64.random(in: 0...10)) - let step = b.reuseOrLoadInt(1) - b.buildForLoop(start, .lessThan, end, .Add, step) { _ in + RecursiveCodeGenerator("SimpleForLoopGenerator") { b in + b.buildForLoop(i: { b.loadInt(0) }, { i in b.compare(i, with: b.loadInt(Int64.random(in: 0...10)), using: .lessThan) }, { i in b.unary(.PostInc, i) }) { _ in b.buildRecursive() } }, + RecursiveCodeGenerator("ComplexForLoopGenerator") { b in + if probability(0.5) { + // Generate a for-loop without any loop variables. + let counter = b.loadInt(10) + b.buildForLoop({}, { b.unary(.PostDec, counter) }) { + b.buildRecursive() + } + } else { + // Generate a for-loop with two loop variables. + // TODO could also generate loops with even more loop variables? + b.buildForLoop({ return [b.loadInt(0), b.loadInt(10)] }, { vs in b.compare(vs[0], with: vs[1], using: .lessThan) }, { vs in b.unary(.PostInc, vs[0]); b.unary(.PostDec, vs[0]) }) { _ in + b.buildRecursive() + } + } + }, + RecursiveCodeGenerator("ForInLoopGenerator", input: .object()) { b, obj in b.buildForInLoop(obj) { _ in b.buildRecursive() @@ -1218,7 +1231,7 @@ public let CodeGenerators: [CodeGenerator] = [ RecursiveCodeGenerator("RepeatLoopGenerator") { b in let numIterations = Int.random(in: 2...100) - b.buildRepeat(n: numIterations) { _ in + b.buildRepeatLoop(n: numIterations) { _ in b.buildRecursive() } }, @@ -1463,7 +1476,7 @@ public let CodeGenerators: [CodeGenerator] = [ b.buildRecursive(block: 2, of: 3) b.doReturn(b.randomVariable()) } - b.buildRepeat(n: numIterations) { i in + b.buildRepeatLoop(n: numIterations) { i in b.buildIf(b.compare(i, with: lastIteration, using: .equal)) { b.buildRecursive(block: 3, of: 3, n: 3) } diff --git a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift index 76ee6c19..3f2f46f9 100644 --- a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift +++ b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift @@ -44,7 +44,7 @@ public let ProgramTemplates = [ b.build(n: genSize) // trigger JIT - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: b.generateCallArguments(for: signature)) } @@ -53,7 +53,7 @@ public let ProgramTemplates = [ b.callFunction(f, withArgs: b.generateCallArguments(for: signature)) // maybe trigger recompilation - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: b.generateCallArguments(for: signature)) } @@ -99,12 +99,12 @@ public let ProgramTemplates = [ b.build(n: genSize) // trigger JIT for first function - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f1, withArgs: b.generateCallArguments(for: signature1)) } // trigger JIT for second function - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f2, withArgs: b.generateCallArguments(for: signature2)) } @@ -115,12 +115,12 @@ public let ProgramTemplates = [ b.callFunction(f1, withArgs: b.generateCallArguments(for: signature1)) // maybe trigger recompilation - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f1, withArgs: b.generateCallArguments(for: signature1)) } // maybe trigger recompilation - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f2, withArgs: b.generateCallArguments(for: signature2)) } @@ -156,7 +156,7 @@ public let ProgramTemplates = [ b.callFunction(f, withArgs: initialArgs) - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { _ in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: optimizationArgs) } diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 68529b47..c5be5bf7 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -200,55 +200,48 @@ public class JavaScriptCompiler { emit(EndDoWhileLoop(), withInputs: [cond]) case .forLoop(let forLoop): - // TODO change the IL to avoid this special handling. - - // Process initializer. - let initializer = forLoop.init_p; - guard initializer.hasValue else { - throw CompilerError.invalidNodeError("Expected an initial value in the for loop initializer") - } - let start = try compileExpression(initializer.value) + try enterNewScope { + var loopVariables = [String]() + + // Process initializer. + var initialLoopVariableValues = [Variable]() + emit(BeginForLoopInitializer()) + if let initializer = forLoop.initializer { + switch initializer { + case .declaration(let declaration): + for declarator in declaration.declarations { + loopVariables.append(declarator.name) + initialLoopVariableValues.append(try compileExpression(declarator.value)) + } + case .expression(let expression): + try compileExpression(expression) + } + } - // Process test expression. - guard case .binaryExpression(let test) = forLoop.test.expression, let comparator = Comparator(rawValue: test.operator) else { - throw CompilerError.invalidNodeError("Expected a comparison as part of the test of a for loop") - } - guard case .identifier(let identifier) = test.lhs.expression else { - throw CompilerError.invalidNodeError("Expected an identifier as lhs of the test expression in a for loop") - } - guard identifier.name == initializer.name else { - throw CompilerError.invalidNodeError("Expected the lhs of the test expression in a for loop to be the loop variable") - } - let end = try compileExpression(test.rhs) + // Process condition. + var outputs = emit(BeginForLoopCondition(numLoopVariables: loopVariables.count), withInputs: initialLoopVariableValues).innerOutputs + zip(loopVariables, outputs).forEach({ map($0, to: $1 )}) + let cond: Variable + if forLoop.hasCondition { + cond = try compileExpression(forLoop.condition) + } else { + cond = emit(LoadBoolean(value: true)).output + } - // Process update expression. - guard case .updateExpression(let update) = forLoop.update.expression else { - throw CompilerError.invalidNodeError("Expected an update expression as final part of a for loop") - } - guard case .identifier(let identifier) = update.argument.expression else { - throw CompilerError.invalidNodeError("Expected an identifier as argument to the update expression in a for loop") - } - guard identifier.name == initializer.name else { - throw CompilerError.invalidNodeError("Expected the update expression in a for loop to update the loop variable") - } - let one = emit(LoadInteger(value: 1)).output - let op: BinaryOperator - switch update.operator { - case "++": - op = .Add - case "--": - op = .Sub - default: - throw CompilerError.invalidNodeError("Unexpected operator in for loop update: \(update.operator)") - } + // Process afterthought. + outputs = emit(BeginForLoopAfterthought(numLoopVariables: loopVariables.count), withInputs: [cond]).innerOutputs + zip(loopVariables, outputs).forEach({ remap($0, to: $1 )}) + if forLoop.hasAfterthought { + try compileExpression(forLoop.afterthought) + } - let loopVar = emit(BeginForLoop(comparator: comparator, op: op), withInputs: [start, end, one]).innerOutput - try enterNewScope { - map(initializer.name, to: loopVar) + // Process body + outputs = emit(BeginForLoopBody(numLoopVariables: loopVariables.count)).innerOutputs + zip(loopVariables, outputs).forEach({ remap($0, to: $1 )}) try compileBody(forLoop.body) - } - emit(EndForLoop()) + emit(EndForLoop()) + } case .forInLoop(let forInLoop): let initializer = forInLoop.left; @@ -282,6 +275,13 @@ public class JavaScriptCompiler { emit(EndForOfLoop()) + case .breakStatement: + // TODO currently we assume this is a LoopBreak, but once we support switch-statements, it could also be a SwitchBreak + emit(LoopBreak()) + + case .continueStatement: + emit(LoopContinue()) + case .tryStatement(let tryStatement): emit(BeginTry()) try enterNewScope { @@ -791,6 +791,11 @@ public class JavaScriptCompiler { scopes.top[identifier] = v } + private func remap(_ identifier: String, to v: Variable) { + assert(scopes.top[identifier] != nil) + scopes.top[identifier] = v + } + private func mapParameters(_ parameters: [Compiler_Protobuf_Parameter], to variables: ArraySlice) { assert(parameters.count == variables.count) for (param, v) in zip(parameters, variables) { diff --git a/Sources/Fuzzilli/Compiler/Parser/parser.js b/Sources/Fuzzilli/Compiler/Parser/parser.js index 1252e4cc..6db8a768 100644 --- a/Sources/Fuzzilli/Compiler/Parser/parser.js +++ b/Sources/Fuzzilli/Compiler/Parser/parser.js @@ -39,7 +39,7 @@ function parse(script, proto) { function assertNoError(err) { if (err) throw err; } - + function dump(node) { console.log(JSON.stringify(node, null, 2)); } @@ -71,12 +71,38 @@ function parse(script, proto) { assertNoError(Statement.verify(statement)); return Statement.create(statement); } - + function visitParameter(param) { assert(param.type == 'Identifier'); return make('Parameter', { name: param.name }); } + function visitVariableDeclaration(node) { + let kind; + if (node.kind === "var") { + kind = 0; + } else if (node.kind === "let") { + kind = 1; + } else if (node.kind === "const") { + kind = 2; + } else { + throw "Unknown variable declaration kind: " + node.kind; + } + + let declarations = []; + for (let decl of node.declarations) { + assert(decl.type === 'VariableDeclarator', "Expected variable declarator nodes inside variable declaration, found " + decl.type); + let outDecl = {name: decl.id.name}; + if (decl.init !== null) { + outDecl.value = visitExpression(decl.init); + } + declarations.push(make('VariableDeclarator', outDecl)); + } + + return { kind, declarations }; + } + + function visitStatement(node) { switch (node.type) { case 'EmptyStatement': { @@ -94,28 +120,7 @@ function parse(script, proto) { return makeStatement('ExpressionStatement', {expression: expr}); } case 'VariableDeclaration': { - let kind; - if (node.kind === "var") { - kind = 0; - } else if (node.kind === "let") { - kind = 1; - } else if (node.kind === "const") { - kind = 2; - } else { - throw "Unknown variable declaration kind: " + node.kind; - } - - let declarations = []; - for (let decl of node.declarations) { - assert(decl.type === 'VariableDeclarator', "Expected variable declarator nodes inside variable declaration, found " + decl.type); - let outDecl = {name: decl.id.name}; - if (decl.init !== null) { - outDecl.value = visitExpression(decl.init); - } - declarations.push(make('VariableDeclarator', outDecl)); - } - - return makeExpression('VariableDeclaration', { kind, declarations }); + return makeStatement('VariableDeclaration', visitVariableDeclaration(node)); } case 'FunctionDeclaration': { assert(node.id.type === 'Identifier', "Expected an identifier as function declaration name"); @@ -162,20 +167,20 @@ function parse(script, proto) { return makeStatement('DoWhileLoop', doWhileLoop); } case 'ForStatement': { - assert(node.init !== null, "Expected for loop with initializer") - assert(node.test !== null, "Expected for loop with test expression") - assert(node.update !== null, "Expected for loop with update expression") - assert(node.init.type === 'VariableDeclaration', "Expected variable declaration as init part of a for loop, found " + node.init.type); - assert(node.init.declarations.length === 1, "Expected exactly one variable declaration in the init part of a for loop"); - let decl = node.init.declarations[0]; let forLoop = {}; - let initDecl = { name: decl.id.name }; - if (decl.init !== null) { - initDecl.value = visitExpression(decl.init); + if (node.init !== null) { + if (node.init.type === 'VariableDeclaration') { + forLoop.declaration = make('VariableDeclaration', visitVariableDeclaration(node.init)); + } else { + forLoop.expression = visitExpression(node.init); + } + } + if (node.test !== null) { + forLoop.condition = visitExpression(node.test); + } + if (node.update !== null) { + forLoop.afterthought = visitExpression(node.update); } - forLoop.init = make('VariableDeclarator', initDecl); - forLoop.test = visitExpression(node.test); - forLoop.update = visitExpression(node.update); forLoop.body = visitStatement(node.body); return makeStatement('ForLoop', forLoop); } @@ -203,6 +208,12 @@ function parse(script, proto) { forOfLoop.body = visitStatement(node.body); return makeStatement('ForOfLoop', forOfLoop); } + case 'BreakStatement': { + return makeStatement('BreakStatement', {}); + } + case 'ContinueStatement': { + return makeStatement('ContinueStatement', {}); + } case 'TryStatement': { assert(node.block.type === 'BlockStatement', "Expected block statement as body of a try block"); let tryStatement = {} diff --git a/Sources/Fuzzilli/FuzzIL/Code.swift b/Sources/Fuzzilli/FuzzIL/Code.swift index d04d5ae8..a02f34c3 100644 --- a/Sources/Fuzzilli/FuzzIL/Code.swift +++ b/Sources/Fuzzilli/FuzzIL/Code.swift @@ -192,6 +192,7 @@ public struct Code: Collection { var visibleScopes = [scopeCounter] var contextAnalyzer = ContextAnalyzer() var blockHeads = [Operation]() + var forLoopHeaderStack = Stack() // Contains the number of loop variables, which must be the same for every block in the for-loop's header. var defaultSwitchCaseStack = Stack() func defineVariable(_ v: Variable, in scope: Int) throws { @@ -274,6 +275,22 @@ public struct Code: Collection { defaultSwitchCaseStack.push(true) } + + // Ensure that all blocks in a for-loop's header have the same number of loop variables. + if instr.op is BeginForLoopCondition { + guard instr.numInputs == instr.numInnerOutputs else { + throw FuzzilliError.codeVerificationError("for-loop header is inconsistent") + } + forLoopHeaderStack.push(instr.numInnerOutputs) + } else if instr.op is BeginForLoopAfterthought { + guard instr.numInnerOutputs == forLoopHeaderStack.top else { + throw FuzzilliError.codeVerificationError("for-loop header is inconsistent") + } + } else if instr.op is BeginForLoopBody { + guard instr.numInnerOutputs == forLoopHeaderStack.pop() else { + throw FuzzilliError.codeVerificationError("for-loop header is inconsistent") + } + } } // Ensure inner output variables don't exist yet diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 2efbe49f..7cfa1124 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -701,30 +701,33 @@ extension Instruction: ProtobufConvertible { $0.beginDoWhileLoopHeader = Fuzzilli_Protobuf_BeginDoWhileLoopHeader() case .endDoWhileLoop: $0.endDoWhileLoop = Fuzzilli_Protobuf_EndDoWhileLoop() - case .beginForLoop(let op): - $0.beginFor = Fuzzilli_Protobuf_BeginFor.with { - $0.comparator = convertEnum(op.comparator, Comparator.allCases) - $0.op = convertEnum(op.op, BinaryOperator.allCases) - } + case .beginForLoopInitializer: + $0.beginForLoopInitializer = Fuzzilli_Protobuf_BeginForLoopInitializer() + case .beginForLoopCondition: + $0.beginForLoopCondition = Fuzzilli_Protobuf_BeginForLoopCondition() + case .beginForLoopAfterthought: + $0.beginForLoopAfterthought = Fuzzilli_Protobuf_BeginForLoopAfterthought() + case .beginForLoopBody: + $0.beginForLoopBody = Fuzzilli_Protobuf_BeginForLoopBody() case .endForLoop: - $0.endFor = Fuzzilli_Protobuf_EndFor() + $0.endForLoop = Fuzzilli_Protobuf_EndForLoop() case .beginForInLoop: - $0.beginForIn = Fuzzilli_Protobuf_BeginForIn() + $0.beginForInLoop = Fuzzilli_Protobuf_BeginForInLoop() case .endForInLoop: - $0.endForIn = Fuzzilli_Protobuf_EndForIn() + $0.endForInLoop = Fuzzilli_Protobuf_EndForInLoop() case .beginForOfLoop: - $0.beginForOf = Fuzzilli_Protobuf_BeginForOf() - case .beginForOfWithDestructLoop(let op): - $0.beginForOfWithDestruct = Fuzzilli_Protobuf_BeginForOfWithDestruct.with { + $0.beginForOfLoop = Fuzzilli_Protobuf_BeginForOfLoop() + case .beginForOfLoopWithDestruct(let op): + $0.beginForOfLoopWithDestruct = Fuzzilli_Protobuf_BeginForOfLoopWithDestruct.with { $0.indices = op.indices.map({ Int32($0) }) $0.hasRestElement_p = op.hasRestElement } case .endForOfLoop: - $0.endForOf = Fuzzilli_Protobuf_EndForOf() + $0.endForOfLoop = Fuzzilli_Protobuf_EndForOfLoop() case .beginRepeatLoop(let op): - $0.beginRepeat = Fuzzilli_Protobuf_BeginRepeat.with { $0.iterations = Int64(op.iterations) } + $0.beginRepeatLoop = Fuzzilli_Protobuf_BeginRepeatLoop.with { $0.iterations = Int64(op.iterations) } case .endRepeatLoop: - $0.endRepeat = Fuzzilli_Protobuf_EndRepeat() + $0.endRepeatLoop = Fuzzilli_Protobuf_EndRepeatLoop() case .loopBreak: $0.loopBreak = Fuzzilli_Protobuf_LoopBreak() case .loopContinue: @@ -1103,23 +1106,31 @@ extension Instruction: ProtobufConvertible { op = BeginDoWhileLoopHeader() case .endDoWhileLoop: op = EndDoWhileLoop() - case .beginFor(let p): - op = BeginForLoop(comparator: try convertEnum(p.comparator, Comparator.allCases), op: try convertEnum(p.op, BinaryOperator.allCases)) - case .endFor: + case .beginForLoopInitializer: + op = BeginForLoopInitializer() + case .beginForLoopCondition: + assert(inouts.count % 2 == 0) + op = BeginForLoopCondition(numLoopVariables: inouts.count / 2) + case .beginForLoopAfterthought: + // First input is the condition + op = BeginForLoopAfterthought(numLoopVariables: inouts.count - 1) + case .beginForLoopBody: + op = BeginForLoopBody(numLoopVariables: inouts.count) + case .endForLoop: op = EndForLoop() - case .beginForIn: + case .beginForInLoop: op = BeginForInLoop() - case .endForIn: + case .endForInLoop: op = EndForInLoop() - case .beginForOf: + case .beginForOfLoop: op = BeginForOfLoop() - case .beginForOfWithDestruct(let p): - op = BeginForOfWithDestructLoop(indices: p.indices.map({ Int64($0) }), hasRestElement: p.hasRestElement_p) - case .endForOf: + case .beginForOfLoopWithDestruct(let p): + op = BeginForOfLoopWithDestruct(indices: p.indices.map({ Int64($0) }), hasRestElement: p.hasRestElement_p) + case .endForOfLoop: op = EndForOfLoop() - case .beginRepeat(let p): + case .beginRepeatLoop(let p): op = BeginRepeatLoop(iterations: Int(p.iterations)) - case .endRepeat: + case .endRepeatLoop: op = EndRepeatLoop() case .loopBreak: op = LoopBreak() diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 85f3a3bc..670914a9 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -46,6 +46,9 @@ public struct JSTyper: Analyzer { var classType: JSType } + // A stack for active for loops containing the types of the loop variables. + private var activeForLoopVariableTypes = Stack<[JSType]>() + // The index of the last instruction that was processed. Just used for debug assertions. private var indexOfLastInstruction = -1 @@ -288,11 +291,24 @@ public struct JSTyper: Analyzer { .endDoWhileLoop: // Do-While loop headers _and_ bodies execute unconditionally (at least once). break + case .beginForLoopInitializer, + .beginForLoopCondition: + // The initializer and the condition of a for-loop's header execute unconditionally. + break + case .beginForLoopAfterthought: + // A for-loop's afterthought and body block execute conditionally. + // We can reuse the same child state for both blocks though. + state.pushChildState() + state.pushSiblingState(typeChanges: &typeChanges) + case .beginForLoopBody: + // We keep using the child states pushed above for the body block. + break + case .endForLoop: + state.mergeStates(typeChanges: &typeChanges) case .beginWhileLoopBody, - .beginForLoop, .beginForInLoop, .beginForOfLoop, - .beginForOfWithDestructLoop, + .beginForOfLoopWithDestruct, .beginRepeatLoop, .beginObjectLiteralMethod, .beginObjectLiteralGetter, @@ -320,7 +336,6 @@ public struct JSTyper: Analyzer { // Push state representing the types in the loop/function state.pushSiblingState(typeChanges: &typeChanges) case .endWhileLoop, - .endForLoop, .endForInLoop, .endForOfLoop, .endRepeatLoop, @@ -345,7 +360,6 @@ public struct JSTyper: Analyzer { .endClassPrivateInstanceMethod, .endClassPrivateStaticMethod, .endCodeString: - // TODO consider adding BeginAnyLoop, EndAnyLoop operations state.mergeStates(typeChanges: &typeChanges) case .beginTry, .beginCatch, @@ -726,9 +740,23 @@ public struct JSTyper: Analyzer { // TODO: support superclass property assignment - case .beginForLoop: - // Primitive type is currently guaranteed due to the structure of for loops - set(instr.innerOutput, .primitive) + case .beginForLoopCondition: + // For now, we use only the initial type of the loop variables (at the point of the for-loop's initializer block) + // without tracking any type changes in the other parts of the for loop. + let inputTypes = instr.inputs.map({ state.type(of: $0) }) + activeForLoopVariableTypes.push(inputTypes) + assert(inputTypes.count == instr.numInnerOutputs) + zip(instr.innerOutputs, inputTypes).forEach({ set($0, $1) }) + + case .beginForLoopAfterthought: + let inputTypes = activeForLoopVariableTypes.top + assert(inputTypes.count == instr.numInnerOutputs) + zip(instr.innerOutputs, inputTypes).forEach({ set($0, $1) }) + + case .beginForLoopBody: + let inputTypes = activeForLoopVariableTypes.pop() + assert(inputTypes.count == instr.numInnerOutputs) + zip(instr.innerOutputs, inputTypes).forEach({ set($0, $1) }) case .beginForInLoop: set(instr.innerOutput, .string) @@ -736,9 +764,9 @@ public struct JSTyper: Analyzer { case .beginForOfLoop: set(instr.innerOutput, .unknown) - case .beginForOfWithDestructLoop: - instr.innerOutputs.forEach { - set($0, .unknown) + case .beginForOfLoopWithDestruct: + for v in instr.innerOutputs { + set(v, .unknown) } case .beginRepeatLoop: diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index c8841466..4395a21a 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -1345,7 +1345,7 @@ final class Reassign: JsOperation { override var opcode: Opcode { .reassign(self) } init() { - super.init(numInputs: 2, numOutputs: 0) + super.init(numInputs: 2) } } @@ -1357,7 +1357,7 @@ final class Update: JsOperation { init(_ op: BinaryOperator) { self.op = op - super.init(numInputs: 2, numOutputs: 0) + super.init(numInputs: 2) } } @@ -1775,16 +1775,79 @@ final class EndDoWhileLoop: JsOperation { } } -final class BeginForLoop: JsOperation { - override var opcode: Opcode { .beginForLoop(self) } +/// +/// For loops. +/// +/// For loops have the following shape: +/// +/// BeginForLoopInitializer +/// // ... +/// // v0 = initial value of the (single) loop variable +/// BeginForLoopCondition v0 -> v1 +/// // v1 = current value of the (single) loop variable +/// // ... +/// BeginForLoopAfterthought -> v2 +/// // v2 = current value of the (single) loop variable +/// // ... +/// BeginForLoopBody -> v3 +/// // v3 = current value of the (single) loop variable +/// // ... +/// EndForLoop +/// +/// This would be lifted to: +/// +/// for (let vX = init; cond; afterthought) { +/// body +/// } +/// +/// This format allows arbitrary computations to be performed in every part of the loop header. It also +/// allows zero, one, or multiple loop variables to be declared, which correspond to the inner outputs +/// of the blocks. During lifting, all the inner outputs are expected to lift to the same identifier (vX in +/// the example above). +/// Similar to while- and do-while loops, the code in the header blocks may be lifted to arrow functions +/// if it requires more than one expression. +/// +final class BeginForLoopInitializer: JsOperation { + override var opcode: Opcode { .beginForLoopInitializer(self) } - let comparator: Comparator - let op: BinaryOperator + init() { + super.init(attributes: .isBlockStart, contextOpened: .javascript) + } +} - init(comparator: Comparator, op: BinaryOperator) { - self.comparator = comparator - self.op = op - super.init(numInputs: 3, numInnerOutputs: 1, attributes: [.isMutable, .isBlockStart, .propagatesSurroundingContext], contextOpened: [.javascript, .loop]) +final class BeginForLoopCondition: JsOperation { + override var opcode: Opcode { .beginForLoopCondition(self) } + + var numLoopVariables: Int { + return numInnerOutputs + } + + init(numLoopVariables: Int) { + super.init(numInputs: numLoopVariables, numInnerOutputs: numLoopVariables, attributes: [.isBlockStart, .isBlockEnd], contextOpened: .javascript) + } +} + +final class BeginForLoopAfterthought: JsOperation { + override var opcode: Opcode { .beginForLoopAfterthought(self) } + + var numLoopVariables: Int { + return numInnerOutputs + } + + init(numLoopVariables: Int) { + super.init(numInputs: 1, numInnerOutputs: numLoopVariables, attributes: [.isBlockStart, .isBlockEnd], contextOpened: .javascript) + } +} + +final class BeginForLoopBody: JsOperation { + override var opcode: Opcode { .beginForLoopBody(self) } + + var numLoopVariables: Int { + return numInnerOutputs + } + + init(numLoopVariables: Int) { + super.init(numInnerOutputs: numLoopVariables, attributes: [.isBlockStart, .isBlockEnd, .propagatesSurroundingContext], contextOpened: [.javascript, .loop]) } } @@ -1820,8 +1883,8 @@ final class BeginForOfLoop: JsOperation { } } -final class BeginForOfWithDestructLoop: JsOperation { - override var opcode: Opcode { .beginForOfWithDestructLoop(self) } +final class BeginForOfLoopWithDestruct: JsOperation { + override var opcode: Opcode { .beginForOfLoopWithDestruct(self) } let indices: [Int64] let hasRestElement: Bool diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index f5b3bc45..f958e54d 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -167,12 +167,15 @@ enum Opcode { case beginDoWhileLoopBody(BeginDoWhileLoopBody) case beginDoWhileLoopHeader(BeginDoWhileLoopHeader) case endDoWhileLoop(EndDoWhileLoop) - case beginForLoop(BeginForLoop) + case beginForLoopInitializer(BeginForLoopInitializer) + case beginForLoopCondition(BeginForLoopCondition) + case beginForLoopAfterthought(BeginForLoopAfterthought) + case beginForLoopBody(BeginForLoopBody) case endForLoop(EndForLoop) case beginForInLoop(BeginForInLoop) case endForInLoop(EndForInLoop) case beginForOfLoop(BeginForOfLoop) - case beginForOfWithDestructLoop(BeginForOfWithDestructLoop) + case beginForOfLoopWithDestruct(BeginForOfLoopWithDestruct) case endForOfLoop(EndForOfLoop) case beginRepeatLoop(BeginRepeatLoop) case endRepeatLoop(EndRepeatLoop) diff --git a/Sources/Fuzzilli/FuzzIL/Semantics.swift b/Sources/Fuzzilli/FuzzIL/Semantics.swift index 74f0f320..a8a4704d 100644 --- a/Sources/Fuzzilli/FuzzIL/Semantics.swift +++ b/Sources/Fuzzilli/FuzzIL/Semantics.swift @@ -196,12 +196,18 @@ extension Operation { return endOp is BeginDoWhileLoopHeader case .beginDoWhileLoopHeader: return endOp is EndDoWhileLoop - case .beginForLoop: + case .beginForLoopInitializer: + return endOp is BeginForLoopCondition + case .beginForLoopCondition: + return endOp is BeginForLoopAfterthought + case .beginForLoopAfterthought: + return endOp is BeginForLoopBody + case .beginForLoopBody: return endOp is EndForLoop case .beginForInLoop: return endOp is EndForInLoop case .beginForOfLoop, - .beginForOfWithDestructLoop: + .beginForOfLoopWithDestruct: return endOp is EndForOfLoop case .beginRepeatLoop: return endOp is EndRepeatLoop diff --git a/Sources/Fuzzilli/Fuzzer.swift b/Sources/Fuzzilli/Fuzzer.swift index c92fb275..b117f93a 100644 --- a/Sources/Fuzzilli/Fuzzer.swift +++ b/Sources/Fuzzilli/Fuzzer.swift @@ -683,7 +683,7 @@ public class Fuzzer { b.doReturn(p) } - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(1000), .Add, b.loadInt(1)) { i in + b.buildRepeatLoop(n: 1000) { i in let x = b.loadInt(42) let y = b.loadInt(43) let arg1 = b.createObject(with: ["x": x, "y": y]) diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 5f856cf2..7168c093 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -571,6 +571,7 @@ public class FuzzILLifter: Lifter { w.increaseIndentionLevel() case .beginWhileLoopBody: + w.decreaseIndentionLevel() w.emit("BeginWhileLoopBody \(input(0))") w.increaseIndentionLevel() @@ -583,6 +584,7 @@ public class FuzzILLifter: Lifter { w.increaseIndentionLevel() case .beginDoWhileLoopHeader: + w.decreaseIndentionLevel() w.emit("BeginDoWhileLoopHeader") w.increaseIndentionLevel() @@ -590,8 +592,38 @@ public class FuzzILLifter: Lifter { w.decreaseIndentionLevel() w.emit("EndDoWhileLoop \(input(0))") - case .beginForLoop(let op): - w.emit("BeginForLoop \(input(0)), '\(op.comparator.token)', \(input(1)), '\(op.op.token)', \(input(2)) -> \(innerOutput())") + case .beginForLoopInitializer: + w.emit("BeginForLoopInitializer") + w.increaseIndentionLevel() + + case .beginForLoopCondition(let op): + w.decreaseIndentionLevel() + if op.numLoopVariables > 0 { + let loopVariables = instr.innerOutputs.map(lift).joined(separator: ", ") + w.emit("BeginForLoopCondition -> \(loopVariables)") + } else { + w.emit("BeginForLoopCondition") + } + w.increaseIndentionLevel() + + case .beginForLoopAfterthought(let op): + w.decreaseIndentionLevel() + if op.numLoopVariables > 0 { + let loopVariables = instr.innerOutputs.map(lift).joined(separator: ", ") + w.emit("BeginForLoopAfterthought \(input(0)) -> \(loopVariables)") + } else { + w.emit("BeginForLoopAfterthought \(input(0))") + } + w.increaseIndentionLevel() + + case .beginForLoopBody(let op): + w.decreaseIndentionLevel() + if op.numLoopVariables > 0 { + let loopVariables = instr.innerOutputs.map(lift).joined(separator: ", ") + w.emit("BeginForLoopBody -> \(loopVariables)") + } else { + w.emit("BeginForLoopBody") + } w.increaseIndentionLevel() case .endForLoop: @@ -610,9 +642,9 @@ public class FuzzILLifter: Lifter { w.emit("BeginForOfLoop \(input(0)) -> \(innerOutput())") w.increaseIndentionLevel() - case .beginForOfWithDestructLoop(let op): + case .beginForOfLoopWithDestruct(let op): let outputs = instr.innerOutputs.map(lift) - w.emit("BeginForOfLoop \(input(0)) -> [\(liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))]") + w.emit("BeginForOfLoopWithDestruct \(input(0)) -> [\(liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))]") w.increaseIndentionLevel() case .endForOfLoop: diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index 98c82c4c..8c71c75e 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -32,6 +32,17 @@ public class JavaScriptLifter: Lifter { /// Counter to assist the lifter in detecting nested CodeStrings private var codeStringNestingLevel = 0 + /// Stack of for-loop header parts. A for-loop's header consists of three different blocks (initializer, condition, afterthought), which + /// are lifted independently but should then typically be combined into a single line. This helper stack makes that possible. + struct ForLoopHeader { + var initializer = "" + var condition = "" + // No need for the afterthought string since this struct will be consumed + // by the handler for the afterthought block. + var loopVariables = [String]() + } + private var forLoopHeaderStack = Stack() + public init(prefix: String = "", suffix: String = "", ecmaVersion: ECMAScriptVersion) { @@ -87,6 +98,13 @@ public class JavaScriptLifter: Lifter { nextExpressionToFetch += 1 return inputs[i] } + // Retrieves the expression for the given input and makes sure that it is an identifier. If necessary, this will create a temporary variable. + func inputAsIdentifier(_ i: Int) -> Expression { + let expr = input(i) + let identifier = w.ensureIsIdentifier(expr, for: instr.input(i)) + assert(identifier.type === Identifier) + return identifier + } switch instr.op.opcode { case .loadInteger(let op): @@ -455,21 +473,21 @@ public class JavaScriptLifter: Lifter { case .setProperty(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let PROPERTY = MemberExpression.new() + obj + "." + op.propertyName let VALUE = input(1) w.emit("\(PROPERTY) = \(VALUE);") case .updateProperty(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let PROPERTY = MemberExpression.new() + obj + "." + op.propertyName let VALUE = input(1) w.emit("\(PROPERTY) \(op.op.token)= \(VALUE);") case .deleteProperty(let op): // For aesthetic reasons, we don't want to inline the lhs of a property deletion, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let target = MemberExpression.new() + obj + "." + op.propertyName let expr = UnaryExpression.new() + "delete " + target w.assign(expr, to: instr.output) @@ -487,21 +505,21 @@ public class JavaScriptLifter: Lifter { case .setElement(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let ELEMENT = MemberExpression.new() + obj + "[" + op.index + "]" let VALUE = input(1) w.emit("\(ELEMENT) = \(VALUE);") case .updateElement(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let ELEMENT = MemberExpression.new() + obj + "[" + op.index + "]" let VALUE = input(1) w.emit("\(ELEMENT) \(op.op.token)= \(VALUE);") case .deleteElement(let op): // For aesthetic reasons, we don't want to inline the lhs of an element deletion, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let target = MemberExpression.new() + obj + "[" + op.index + "]" let expr = UnaryExpression.new() + "delete " + target w.assign(expr, to: instr.output) @@ -519,21 +537,21 @@ public class JavaScriptLifter: Lifter { case .setComputedProperty: // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let PROPERTY = MemberExpression.new() + obj + "[" + input(1).text + "]" let VALUE = input(2) w.emit("\(PROPERTY) = \(VALUE);") case .updateComputedProperty(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let PROPERTY = MemberExpression.new() + obj + "[" + input(1).text + "]" let VALUE = input(2) w.emit("\(PROPERTY) \(op.op.token)= \(VALUE);") case .deleteComputedProperty: // For aesthetic reasons, we don't want to inline the lhs of a property deletion, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let target = MemberExpression.new() + obj + "[" + input(1).text + "]" let expr = UnaryExpression.new() + "delete " + target w.assign(expr, to: instr.output) @@ -651,19 +669,19 @@ public class JavaScriptLifter: Lifter { case .callFunction: // Avoid inlining of the function expression. This is mostly for aesthetic reasons, but is also required if the expression for // the function is a MemberExpression since it would otherwise be interpreted as a method call, not a function call. - let f = w.maybeStoreInTemporaryVariable(input(0)) + let f = inputAsIdentifier(0) let args = inputs.dropFirst() let expr = CallExpression.new() + f + "(" + liftCallArguments(args) + ")" w.assign(expr, to: instr.output) case .callFunctionWithSpread(let op): - let f = input(0) + let f = inputAsIdentifier(0) let args = inputs.dropFirst() let expr = CallExpression.new() + f + "(" + liftCallArguments(args, spreading: op.spreads) + ")" w.assign(expr, to: instr.output) case .construct: - let f = input(0) + let f = inputAsIdentifier(0) let args = inputs.dropFirst() let EXPR = NewExpression.new() + "new " + f + "(" + liftCallArguments(args) + ")" // For aesthetic reasons we disallow inlining "new" expressions and always assign their result to a new variable. @@ -672,7 +690,7 @@ public class JavaScriptLifter: Lifter { w.emit("\(LET) \(V) = \(EXPR);") case .constructWithSpread(let op): - let f = input(0) + let f = inputAsIdentifier(0) let args = inputs.dropFirst() let EXPR = NewExpression.new() + "new " + f + "(" + liftCallArguments(args, spreading: op.spreads) + ")" // For aesthetic reasons we disallow inlining "new" expressions and always assign their result to a new variable. @@ -732,16 +750,16 @@ public class JavaScriptLifter: Lifter { w.assign(expr, to: instr.output) case .reassign: - let DEST = input(0) - let VALUE = input(1) - assert(DEST.type === Identifier) - w.emit("\(DEST) = \(VALUE);") + let dest = input(0) + assert(dest.type === Identifier) + let expr = AssignmentExpression.new() + dest + " = " + input(1) + w.reassign(instr.input(0), to: expr) case .update(let op): - let DEST = input(0) - let VALUE = input(1) - assert(DEST.type === Identifier) - w.emit("\(DEST) \(op.op.token)= \(VALUE);") + let dest = input(0) + assert(dest.type === Identifier) + let expr = AssignmentExpression.new() + dest + " \(op.op.token)= " + input(1) + w.reassign(instr.input(0), to: expr) case .dup: let LET = w.declarationKeyword(for: instr.output) @@ -852,14 +870,14 @@ public class JavaScriptLifter: Lifter { case .setPrivateProperty(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let PROPERTY = MemberExpression.new() + obj + ".#" + op.propertyName let VALUE = input(1) w.emit("\(PROPERTY) = \(VALUE);") case .updatePrivateProperty(let op): // For aesthetic reasons, we don't want to inline the lhs of an assignment, so force it to be stored in a variable. - let obj = w.maybeStoreInTemporaryVariable(input(0)) + let obj = inputAsIdentifier(0) let PROPERTY = MemberExpression.new() + obj + ".#" + op.propertyName let VALUE = input(1) w.emit("\(PROPERTY) \(op.op.token)= \(VALUE);") @@ -927,12 +945,12 @@ public class JavaScriptLifter: Lifter { w.emit("}") case .beginWhileLoopHeader: - // Must not inline accross loop boundaries as that would change the program's semantics. + // Must not inline across loop boundaries as that would change the program's semantics. w.emitPendingExpressions() - handleBeginLoopHeader(with: &w) + handleBeginSingleExpressionContext(with: &w, initialIndentionLevel: 2) case .beginWhileLoopBody: - let COND = handleEndLoopHeader(loopVar: input(0), with: &w) + let COND = handleEndSingleExpressionContext(result: input(0), with: &w) w.emitBlock("while (\(COND)) {") w.enterNewBlock() @@ -946,29 +964,100 @@ public class JavaScriptLifter: Lifter { case .beginDoWhileLoopHeader: w.leaveCurrentBlock() - handleBeginLoopHeader(with: &w) + handleBeginSingleExpressionContext(with: &w, initialIndentionLevel: 2) case .endDoWhileLoop: - let COND = handleEndLoopHeader(loopVar: input(0), with: &w) + let COND = handleEndSingleExpressionContext(result: input(0), with: &w) w.emitBlock("} while (\(COND))") - case .beginForLoop(let op): - let I = w.declare(instr.innerOutput) - let INITIAL = input(0) - let COND = BinaryExpression.new() + I + " " + op.comparator.token + " " + input(1) - let EXPR: Expression - // This is a bit of a hack. Instead, maybe we should have a way of simplifying expressions through some pattern matching code? - let step = input(2) - if step.text == "1" && op.op == .Add { - EXPR = PostfixExpression.new() + I + "++" - } else if step.text == "1" && op.op == .Sub { - EXPR = PostfixExpression.new() + I + "--" + case .beginForLoopInitializer: + // While we could inline into the loop header, we probably don't want to do that as it will often lead + // to the initializer block becoming an arrow function, which is not very readable. So instead force + // all pending expressions to be emitted now, before the loop. + w.emitPendingExpressions() + + // The loop initializer is a bit odd: it may be a single expression (`for (foo(); ...)`), but it + // could also be a variable declaration containing multiple expressions (`for (let i = X, j = Y; ...`). + // However, we'll figure this out at the end of the block in the .beginForLoopCondition case. + handleBeginSingleExpressionContext(with: &w, initialIndentionLevel: 2) + + case .beginForLoopCondition(let op): + let loopVars = w.declareAll(instr.innerOutputs, usePrefix: "i") + + // The logic for a for-loop's initializer block is a little different from the lifting logic for other block headers. + let initializer: String + if op.numLoopVariables == 0 { + assert(loopVars.isEmpty) + // In this case the initializer really is a single expression where the result is unused. + initializer = handleEndSingleExpressionContext(with: &w) } else { - let newValue = BinaryExpression.new() + I + " " + op.op.token + " " + step - EXPR = AssignmentExpression.new() + I + " = " + newValue + // In this case, the initializer declares one or more variables. We first try to lift the variable declarations + // as `let i = X, j = Y, ...`, however, this is _only_ possible if we have as many expressions as we have + // variables to declare _and_ if they are in the correct order. + // In particular, the following syntax is invalid: `let i = foo(), bar(), j = baz()` and so we cannot chain + // independent expressions using the comma operator as we do for the other loop headers. + // In all other cases, we lift the initializer to something like `let [i, j] = (() => { CODE })()`. + if w.isCurrentTemporaryBufferEmpty && w.numPendingExpressions == 0 { + // The "good" case: we can emit `let i = X, j = Y, ...` + assert(loopVars.count == inputs.count) + let declarations = zip(loopVars, inputs).map({ "\($0) = \($1)" }).joined(separator: ", ") + initializer = "let \(declarations)" + let code = w.popTemporaryOutputBuffer() + assert(code.isEmpty) + } else { + // In this case, we have to emit a temporary arrow function that returns all initial values in an array + w.emitPendingExpressions() + if op.numLoopVariables == 1 { + // Emit a `let i = (() => { ...; return X; })()` + w.emit("return \(input(0));") + let I = loopVars[0] + let CODE = w.popTemporaryOutputBuffer() + initializer = "let \(I) = (() => {\n\(CODE) })()" + } else { + // Emit a `let [i, j, k] = (() => { ...; return [X, Y, Z]; })()` + let initialLoopVarValues = inputs.map({ $0.text }).joined(separator: ", ") + w.emit("return [\(initialLoopVarValues)];") + let VARS = loopVars.joined(separator: ", ") + let CODE = w.popTemporaryOutputBuffer() + initializer = "let [\(VARS)] = (() => {\n\(CODE) })()" + } + } } - let LET = w.varKeyword - w.emit("for (\(LET) \(I) = \(INITIAL); \(COND); \(EXPR)) {") + + forLoopHeaderStack.push(ForLoopHeader(initializer: initializer, loopVariables: loopVars)) + handleBeginSingleExpressionContext(with: &w, initialIndentionLevel: 2) + + case .beginForLoopAfterthought: + var condition = handleEndSingleExpressionContext(result: input(0), with: &w) + // Small syntactic "optimization": an empty condition is always true, so we can replace the constant "true" with an empty condition. + if condition == "true" { + condition = "" + } + + forLoopHeaderStack.top.condition = condition + + w.declareAll(instr.innerOutputs, as: forLoopHeaderStack.top.loopVariables) + handleBeginSingleExpressionContext(with: &w, initialIndentionLevel: 2) + + case .beginForLoopBody: + let header = forLoopHeaderStack.pop() + let INITIALIZER = header.initializer + var CONDITION = header.condition + var AFTERTHOUGHT = handleEndSingleExpressionContext(with: &w) + + if !INITIALIZER.contains("\n") && !CONDITION.contains("\n") && !AFTERTHOUGHT.contains("\n") { + if !CONDITION.isEmpty { CONDITION = " " + CONDITION } + if !AFTERTHOUGHT.isEmpty { AFTERTHOUGHT = " " + AFTERTHOUGHT } + w.emit("for (\(INITIALIZER);\(CONDITION);\(AFTERTHOUGHT)) {") + } else { + w.emitBlock(""" + for (\(INITIALIZER); + \(CONDITION); + \(AFTERTHOUGHT)) { + """) + } + + w.declareAll(instr.innerOutputs, as: header.loopVariables) w.enterNewBlock() case .endForLoop: @@ -993,7 +1082,7 @@ public class JavaScriptLifter: Lifter { w.emit("for (\(LET) \(V) of \(OBJ)) {") w.enterNewBlock() - case .beginForOfWithDestructLoop(let op): + case .beginForOfLoopWithDestruct(let op): let outputs = w.declareAll(instr.innerOutputs) let PATTERN = liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement) let LET = w.varKeyword @@ -1093,36 +1182,46 @@ public class JavaScriptLifter: Lifter { return w.code } - private func handleBeginLoopHeader(with w: inout JavaScriptWriter) { - // Lift the header content into a temporary buffer so that it can either be emitted + // Signal that the following code needs to be lifted into a single expression. + private func handleBeginSingleExpressionContext(with w: inout JavaScriptWriter, initialIndentionLevel: Int) { + // Lift the following code into a temporary buffer so that it can either be emitted // as a single expression, or as body of a temporary function, see below. - w.pushTemporaryOutputBuffer(initialIndentionLevel: 1) + w.pushTemporaryOutputBuffer(initialIndentionLevel: initialIndentionLevel) } - private func handleEndLoopHeader(loopVar: Expression, with w: inout JavaScriptWriter) -> String { + // Lift all code between the begin and end of the single expression context (e.g. a loop header) into a single expression. + // The optional result parameter contains the value to which the entire expression must ultimately evaluate. + private func handleEndSingleExpressionContext(result maybeResult: Expression? = nil, with w: inout JavaScriptWriter) -> String { if w.isCurrentTemporaryBufferEmpty { - // This means that the header consists entirely of expressions that can be inlined, and that the loop condition + // This means that the code consists entirely of expressions that can be inlined, and that the result // variable is either not an inlined expression (but instead e.g. the identifier for a local variable), or that // it is the most recent pending expression (in which case previously pending expressions are not emitted). // - // In this case, we can emit a single expression for the header (by combining all pending expressions using - // the comma operator). + // In this case, we can emit a single expression by combining all pending expressions using the comma operator. var COND = CommaExpression.new() - for expr in w.takePendingExpressions() { - COND = COND + expr + ", " + let expressions = w.takePendingExpressions() + (maybeResult != nil ? [maybeResult!] : []) + for expr in expressions { + if COND.text.isEmpty { + COND = COND + expr + } else { + COND = COND + ", " + expr + } } - COND = COND + loopVar let headerContent = w.popTemporaryOutputBuffer() assert(headerContent.isEmpty) return COND.text } else { - // The header is more complicated, so emit a temporary function and call it. + // The code is more complicated, so emit a temporary function and call it. w.emitPendingExpressions() - w.emit("return \(loopVar);") + if let result = maybeResult { + w.emit("return \(result);") + } let CODE = w.popTemporaryOutputBuffer() - return "(() => {\n\(CODE)})()" + assert(CODE.contains("\n")) + assert(CODE.hasSuffix("\n")) + return "(() => {\n\(CODE) })()" } } @@ -1264,6 +1363,11 @@ public class JavaScriptLifter: Lifter { // The expressions are identified by the FuzzIL output variable that they generate. The actual expression is stored in the expressions dictionary. private var pendingExpressions = [Variable]() + // We also try to inline reassignments once, into the next use of the reassigned FuzzIL variable. However, for all subsequent uses we have to use the + // identifier of the JavaScript variable again (the lhs of the reassignment). This map is used to remember these identifiers. + // See `reassign()` for more details about reassignment inlining. + private var inlinedReassignments = VariableMap() + init(analyzer: VariableAnalyzer, version: ECMAScriptVersion, stripComments: Bool = false, includeLineNumbers: Bool = false, indent: Int = 4) { self.writer = ScriptWriter(stripComments: stripComments, includeLineNumbers: includeLineNumbers, indent: indent) self.analyzer = analyzer @@ -1297,13 +1401,48 @@ public class JavaScriptLifter: Lifter { } } + /// Reassign a FuzzIL variable to a new JavaScript expression. + /// The given expression is expected to be an AssignmentExpression. + /// + /// Variable reassignments such as `a = b` or `c += d` can be inlined once into the next use of the reassigned variable. All subsequent uses then again use the variable. + /// For example: + /// + /// a += b; + /// foo(a); + /// bar(a); + /// + /// Can also be lifted as: + /// + /// foo(a += b); + /// bar(a); + /// + /// However, this is only possible if the next use is not again a reassignment, otherwise it'd lead to something like `(a = b) = c;`which is invalid. + /// To simplify things, we therefore only allow the inlining if there is exactly one reassignment. + mutating func reassign(_ v: Variable, to expr: Expression) { + assert(expr.type === AssignmentExpression) + assert(analyzer.numAssignments(of: v) > 1) + guard analyzer.numAssignments(of: v) == 2 else { + // There are multiple (re-)assignments, so we cannot inline the assignment expression. + return emit("\(expr);") + } + + guard let identifier = expressions[v] else { + fatalError("Missing identifier for reassignment") + } + assert(identifier.type === Identifier) + expressions[v] = expr + pendingExpressions.append(v) + assert(!inlinedReassignments.contains(v)) + inlinedReassignments[v] = identifier + } + /// Retrieve the JavaScript expressions assigned to the given FuzzIL variables. /// /// The returned expressions _must_ subsequently execute exactly in the order that they are returned (i.e. in the order of the input variables). /// Otherwise, expression inlining will change the semantics of the program. /// /// This is a mutating operation as it can modify the list of pending expressions or emit pending expression to retain the correct ordering. - mutating func retrieve(expressionsFor vars: ArraySlice) -> [Expression] { + mutating func retrieve(expressionsFor queriedVariables: ArraySlice) -> [Expression] { // If any of the expression for the variables is pending, then one of two things will happen: // // 1. Iff the pending expressions that are being retrieved are an exact suffix match of the pending expressions list, then these pending expressions @@ -1333,11 +1472,19 @@ public class JavaScriptLifter: Lifter { var results = [Expression]() var matchingSuffixLength = 0 - let queriedPendingExpressions = vars.filter(pendingExpressions.contains) + // Filter the queried variables for the suffix matching: for that we only care about + // - variables for which the expressions are currently pending (i.e. are being inlined) + // - the first occurance of every variable. This is irrelevant for "normal" pending expressions + // since they can only occur once (otherwise, they wouldn't be inlined), but is important + // for inlined reassignments, e.g. to be able to correctly handle `foo(a = 42, a, bar(), a);` + var queriedPendingExpressions = [Variable]() + for v in queriedVariables where pendingExpressions.contains(v) && !queriedPendingExpressions.contains(v) { + queriedPendingExpressions.append(v) + } for v in queriedPendingExpressions.reversed() { assert(matchingSuffixLength < pendingExpressions.count) let currentSuffixPosition = pendingExpressions.count - 1 - matchingSuffixLength - if v == pendingExpressions[currentSuffixPosition] { + if matchingSuffixLength < pendingExpressions.count && v == pendingExpressions[currentSuffixPosition] { matchingSuffixLength += 1 } } @@ -1354,13 +1501,12 @@ public class JavaScriptLifter: Lifter { } pendingExpressions.removeLast(matchingSuffixLength) - for v in vars { + for v in queriedVariables { guard let expression = expressions[v] else { fatalError("Don't have an expression for variable \(v)") } if expression.isEffectful { - // Inlined, effectful expressions must only be used once. To guarantee that, remove the expression from the dictionary. - expressions.removeValue(forKey: v) + usePendingExpression(expression, forVariable: v) } results.append(expression) } @@ -1370,11 +1516,17 @@ public class JavaScriptLifter: Lifter { /// If the given expression is not an identifier, create a temporary variable and assign the expression to it. /// - /// Mostly used for aesthetical reasons, if an expression is more readable if some subexpression is always - /// an identifier. - mutating func maybeStoreInTemporaryVariable(_ expr: Expression) -> Expression { + /// Mostly used for aesthetical reasons, if an expression is more readable if some subexpression is always an identifier. + mutating func ensureIsIdentifier(_ expr: Expression, for v: Variable) -> Expression { if expr.type === Identifier { return expr + } else if expr.type === AssignmentExpression { + // Just need to emit the assignment now and return the lhs. + emit("\(expr);") + guard let identifier = inlinedReassignments[v] else { + fatalError("Don't have an identifier for a reassignment") + } + return identifier } else { let LET = constKeyword // We use a different naming scheme for these temporary variables since we may end up defining @@ -1422,6 +1574,12 @@ public class JavaScriptLifter: Lifter { return vars.map({ declare($0, as: prefix + String($0.number)) }) } + /// Declare all of the given variables. Equivalent to calling declare() for each of them. + mutating func declareAll(_ vars: ArraySlice, as names: [String]) { + assert(vars.count == names.count) + zip(vars, names).forEach({ declare($0, as: $1) }) + } + /// Determine the correct variable declaration keyword (e.g. 'let' or 'const') for the given variable. mutating func declarationKeyword(for v: Variable) -> String { if analyzer.numAssignments(of: v) == 1 { @@ -1464,13 +1622,12 @@ public class JavaScriptLifter: Lifter { for v in pendingExpressions { emitPendingExpression(forVariable: v) } - pendingExpressions.removeAll(keepingCapacity: true) + pendingExpressions.removeAll() } - mutating func pushTemporaryOutputBuffer(initialIndentionLevel: Int = 0) { + mutating func pushTemporaryOutputBuffer(initialIndentionLevel: Int) { temporaryOutputBufferStack.push(writer) - writer = ScriptWriter(stripComments: writer.stripComments, includeLineNumbers: false, indent: writer.indent.count) - writer.increaseIndentionLevel(by: initialIndentionLevel) + writer = ScriptWriter(stripComments: writer.stripComments, includeLineNumbers: false, indent: writer.indent.count, initialIndentionLevel: initialIndentionLevel) } mutating func popTemporaryOutputBuffer() -> String { @@ -1484,6 +1641,10 @@ public class JavaScriptLifter: Lifter { return writer.code.isEmpty } + var numPendingExpressions: Int { + return pendingExpressions.count + } + // The following methods are mostly useful for lifting loop headers. See the corresponding for more details. mutating func takePendingExpressions() -> [Expression] { var result = [Expression]() @@ -1491,7 +1652,7 @@ public class JavaScriptLifter: Lifter { guard let expr = expressions[v] else { fatalError("Missing expression for variable \(v)") } - expressions.removeValue(forKey: v) + usePendingExpression(expr, forVariable: v) result.append(expr) } pendingExpressions.removeAll() @@ -1507,13 +1668,19 @@ public class JavaScriptLifter: Lifter { } /// Emit the pending expression for the given variable. - /// Note: this does _not_ remove the variable from the pendingExpressions list. It is the caller's responsibility to do so. + /// Note: this does _not_ remove the variable from the pendingExpressions list. It is the caller's responsibility to do so (as the caller can usually batch multiple removals together). private mutating func emitPendingExpression(forVariable v: Variable) { guard let EXPR = expressions[v] else { fatalError("Missing expression for variable \(v)") } - expressions.removeValue(forKey: v) - if analyzer.numUses(of: v) > 0 { + + usePendingExpression(EXPR, forVariable: v) + + if EXPR.type === AssignmentExpression { + // Reassignments require special handling: there is already a variable declared for the lhs, + // so we only need to emit the AssignmentExpression as an expression statement. + writer.emit("\(EXPR);") + } else if analyzer.numUses(of: v) > 0 { let LET = declarationKeyword(for: v) let V = declare(v) // Need to use writer.emit instead of emit here as the latter will emit all pending expressions. @@ -1526,6 +1693,21 @@ public class JavaScriptLifter: Lifter { } } + /// When a pending expression is used (either emitted or attached to another expression), it should be removed from the list of + /// available expressions. Further, inlined reassignments require additional handling, see `reassign` for more details. + /// This function takes care of both of these things. + private mutating func usePendingExpression(_ expr: Expression, forVariable v: Variable) { + // Inlined expressions must only be used once, so delete it from the list of available expressions. + expressions.removeValue(forKey: v) + + // If the inlined expression is an assignment expression, we now have to restore the previous + // expression for that variable (which must be an identifier). See `reassign` for more details. + if let lhs = inlinedReassignments[v] { + assert(expr.type === AssignmentExpression) + expressions[v] = lhs + } + } + /// Decide if we should attempt to inline the given expression. We do that if: /// * The output variable is not reassigned later on (otherwise, that reassignment would fail as the variable was never defined) /// * The output variable is pure OR diff --git a/Sources/Fuzzilli/Lifting/ScriptWriter.swift b/Sources/Fuzzilli/Lifting/ScriptWriter.swift index 09468cd7..d5bf5a38 100644 --- a/Sources/Fuzzilli/Lifting/ScriptWriter.swift +++ b/Sources/Fuzzilli/Lifting/ScriptWriter.swift @@ -32,8 +32,9 @@ struct ScriptWriter { /// Current line, used when including line numbers in the output. public private(set) var currentLineNumber = 0 - public init (stripComments: Bool = false, includeLineNumbers: Bool = false, indent: Int = 4) { + public init (stripComments: Bool = false, includeLineNumbers: Bool = false, indent: Int = 4, initialIndentionLevel: Int = 0) { self.indent = String(repeating: " ", count: indent) + self.currentIndention = String(repeating: " ", count: indent * initialIndentionLevel) self.stripComments = stripComments self.includeLineNumbers = includeLineNumbers } diff --git a/Sources/Fuzzilli/Minimization/BlockReducer.swift b/Sources/Fuzzilli/Minimization/BlockReducer.swift index 5831505e..d9bcedb1 100644 --- a/Sources/Fuzzilli/Minimization/BlockReducer.swift +++ b/Sources/Fuzzilli/Minimization/BlockReducer.swift @@ -44,16 +44,13 @@ struct BlockReducer: Reducer { case .beginWhileLoopHeader, .beginDoWhileLoopBody, + .beginForLoopInitializer, .beginForInLoop, .beginForOfLoop, - .beginForOfWithDestructLoop, + .beginForOfLoopWithDestruct, .beginRepeatLoop: reduceLoop(group, in: &code, with: helper) - case .beginForLoop: - assert(group.numBlocks == 1) - reduceLegacyLoop(loop: group.block(0), in: &code, with: helper) - case .beginIf: reduceIfElse(group, in: &code, with: helper) @@ -204,26 +201,6 @@ struct BlockReducer: Reducer { helper.tryNopping(candidates, in: &code) } - private func reduceLegacyLoop(loop: Block, in code: inout Code, with helper: MinimizationHelper) { - - // We reduce loops by removing the loop itself as well as - // any 'break' or 'continue' instructions in the loop body. - var candidates = [Int]() - candidates.append(loop.head) - candidates.append(loop.tail) - - // Scan the body for break or continue instructions and remove those as well - var analyzer = ContextAnalyzer() - for instr in loop.body { - analyzer.analyze(instr) - if !analyzer.context.contains(.loop) && instr.op.requiredContext.contains(.loop) { - candidates.append(instr.index) - } - } - - helper.tryNopping(candidates, in: &code) - } - private func reduceIfElse(_ group: BlockGroup, in code: inout Code, with helper: MinimizationHelper) { assert(group.begin.op is BeginIf) assert(group.end.op is EndIf) diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 8a22f310..49a21cea 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -216,12 +216,6 @@ public class OperationMutator: BaseInstructionMutator { newOp = UpdateSuperProperty(propertyName: b.randomPropertyName(), operator: chooseUniform(from: BinaryOperator.allCases)) case .beginIf(let op): newOp = BeginIf(inverted: !op.inverted) - case .beginForLoop(let op): - if probability(0.5) { - newOp = BeginForLoop(comparator: chooseUniform(from: Comparator.allCases), op: op.op) - } else { - newOp = BeginForLoop(comparator: op.comparator, op: chooseUniform(from: BinaryOperator.allCases)) - } default: fatalError("Unhandled Operation: \(type(of: instr.op))") } diff --git a/Sources/Fuzzilli/Protobuf/ast.pb.swift b/Sources/Fuzzilli/Protobuf/ast.pb.swift index 57021d2f..b6b1c5fd 100644 --- a/Sources/Fuzzilli/Protobuf/ast.pb.swift +++ b/Sources/Fuzzilli/Protobuf/ast.pb.swift @@ -380,32 +380,47 @@ public struct Compiler_Protobuf_ForLoop { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var init_p: Compiler_Protobuf_VariableDeclarator { - get {return _storage._init_p ?? Compiler_Protobuf_VariableDeclarator()} - set {_uniqueStorage()._init_p = newValue} + /// This field is optional + public var initializer: OneOf_Initializer? { + get {return _storage._initializer} + set {_uniqueStorage()._initializer = newValue} } - /// Returns true if `init_p` has been explicitly set. - public var hasInit_p: Bool {return _storage._init_p != nil} - /// Clears the value of `init_p`. Subsequent reads from it will return its default value. - public mutating func clearInit_p() {_uniqueStorage()._init_p = nil} - public var test: Compiler_Protobuf_Expression { - get {return _storage._test ?? Compiler_Protobuf_Expression()} - set {_uniqueStorage()._test = newValue} + public var declaration: Compiler_Protobuf_VariableDeclaration { + get { + if case .declaration(let v)? = _storage._initializer {return v} + return Compiler_Protobuf_VariableDeclaration() + } + set {_uniqueStorage()._initializer = .declaration(newValue)} } - /// Returns true if `test` has been explicitly set. - public var hasTest: Bool {return _storage._test != nil} - /// Clears the value of `test`. Subsequent reads from it will return its default value. - public mutating func clearTest() {_uniqueStorage()._test = nil} - public var update: Compiler_Protobuf_Expression { - get {return _storage._update ?? Compiler_Protobuf_Expression()} - set {_uniqueStorage()._update = newValue} + public var expression: Compiler_Protobuf_Expression { + get { + if case .expression(let v)? = _storage._initializer {return v} + return Compiler_Protobuf_Expression() + } + set {_uniqueStorage()._initializer = .expression(newValue)} + } + + /// This field is optional + public var condition: Compiler_Protobuf_Expression { + get {return _storage._condition ?? Compiler_Protobuf_Expression()} + set {_uniqueStorage()._condition = newValue} } - /// Returns true if `update` has been explicitly set. - public var hasUpdate: Bool {return _storage._update != nil} - /// Clears the value of `update`. Subsequent reads from it will return its default value. - public mutating func clearUpdate() {_uniqueStorage()._update = nil} + /// Returns true if `condition` has been explicitly set. + public var hasCondition: Bool {return _storage._condition != nil} + /// Clears the value of `condition`. Subsequent reads from it will return its default value. + public mutating func clearCondition() {_uniqueStorage()._condition = nil} + + /// This field is optional + public var afterthought: Compiler_Protobuf_Expression { + get {return _storage._afterthought ?? Compiler_Protobuf_Expression()} + set {_uniqueStorage()._afterthought = newValue} + } + /// Returns true if `afterthought` has been explicitly set. + public var hasAfterthought: Bool {return _storage._afterthought != nil} + /// Clears the value of `afterthought`. Subsequent reads from it will return its default value. + public mutating func clearAfterthought() {_uniqueStorage()._afterthought = nil} public var body: Compiler_Protobuf_Statement { get {return _storage._body ?? Compiler_Protobuf_Statement()} @@ -418,6 +433,31 @@ public struct Compiler_Protobuf_ForLoop { public var unknownFields = SwiftProtobuf.UnknownStorage() + /// This field is optional + public enum OneOf_Initializer: Equatable { + case declaration(Compiler_Protobuf_VariableDeclaration) + case expression(Compiler_Protobuf_Expression) + + #if !swift(>=4.1) + public static func ==(lhs: Compiler_Protobuf_ForLoop.OneOf_Initializer, rhs: Compiler_Protobuf_ForLoop.OneOf_Initializer) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.declaration, .declaration): return { + guard case .declaration(let l) = lhs, case .declaration(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.expression, .expression): return { + guard case .expression(let l) = lhs, case .expression(let r) = rhs else { preconditionFailure() } + return l == r + }() + default: return false + } + } + #endif + } + public init() {} fileprivate var _storage = _StorageClass.defaultInstance @@ -501,6 +541,26 @@ public struct Compiler_Protobuf_ForOfLoop { fileprivate var _storage = _StorageClass.defaultInstance } +public struct Compiler_Protobuf_BreakStatement { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Compiler_Protobuf_ContinueStatement { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + public struct Compiler_Protobuf_CatchClause { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -699,6 +759,22 @@ public struct Compiler_Protobuf_Statement { set {_uniqueStorage()._statement = .forOfLoop(newValue)} } + public var breakStatement: Compiler_Protobuf_BreakStatement { + get { + if case .breakStatement(let v)? = _storage._statement {return v} + return Compiler_Protobuf_BreakStatement() + } + set {_uniqueStorage()._statement = .breakStatement(newValue)} + } + + public var continueStatement: Compiler_Protobuf_ContinueStatement { + get { + if case .continueStatement(let v)? = _storage._statement {return v} + return Compiler_Protobuf_ContinueStatement() + } + set {_uniqueStorage()._statement = .continueStatement(newValue)} + } + public var tryStatement: Compiler_Protobuf_TryStatement { get { if case .tryStatement(let v)? = _storage._statement {return v} @@ -730,6 +806,8 @@ public struct Compiler_Protobuf_Statement { case forLoop(Compiler_Protobuf_ForLoop) case forInLoop(Compiler_Protobuf_ForInLoop) case forOfLoop(Compiler_Protobuf_ForOfLoop) + case breakStatement(Compiler_Protobuf_BreakStatement) + case continueStatement(Compiler_Protobuf_ContinueStatement) case tryStatement(Compiler_Protobuf_TryStatement) case throwStatement(Compiler_Protobuf_ThrowStatement) @@ -787,6 +865,14 @@ public struct Compiler_Protobuf_Statement { guard case .forOfLoop(let l) = lhs, case .forOfLoop(let r) = rhs else { preconditionFailure() } return l == r }() + case (.breakStatement, .breakStatement): return { + guard case .breakStatement(let l) = lhs, case .breakStatement(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.continueStatement, .continueStatement): return { + guard case .continueStatement(let l) = lhs, case .continueStatement(let r) = rhs else { preconditionFailure() } + return l == r + }() case (.tryStatement, .tryStatement): return { guard case .tryStatement(let l) = lhs, case .tryStatement(let r) = rhs else { preconditionFailure() } return l == r @@ -1889,8 +1975,11 @@ extension Compiler_Protobuf_IfStatement: @unchecked Sendable {} extension Compiler_Protobuf_WhileLoop: @unchecked Sendable {} extension Compiler_Protobuf_DoWhileLoop: @unchecked Sendable {} extension Compiler_Protobuf_ForLoop: @unchecked Sendable {} +extension Compiler_Protobuf_ForLoop.OneOf_Initializer: @unchecked Sendable {} extension Compiler_Protobuf_ForInLoop: @unchecked Sendable {} extension Compiler_Protobuf_ForOfLoop: @unchecked Sendable {} +extension Compiler_Protobuf_BreakStatement: @unchecked Sendable {} +extension Compiler_Protobuf_ContinueStatement: @unchecked Sendable {} extension Compiler_Protobuf_CatchClause: @unchecked Sendable {} extension Compiler_Protobuf_FinallyClause: @unchecked Sendable {} extension Compiler_Protobuf_TryStatement: @unchecked Sendable {} @@ -2608,16 +2697,17 @@ extension Compiler_Protobuf_DoWhileLoop: SwiftProtobuf.Message, SwiftProtobuf._M extension Compiler_Protobuf_ForLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ForLoop" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "init"), - 2: .same(proto: "test"), - 3: .same(proto: "update"), - 4: .same(proto: "body"), + 1: .same(proto: "declaration"), + 2: .same(proto: "expression"), + 3: .same(proto: "condition"), + 4: .same(proto: "afterthought"), + 5: .same(proto: "body"), ] fileprivate class _StorageClass { - var _init_p: Compiler_Protobuf_VariableDeclarator? = nil - var _test: Compiler_Protobuf_Expression? = nil - var _update: Compiler_Protobuf_Expression? = nil + var _initializer: Compiler_Protobuf_ForLoop.OneOf_Initializer? + var _condition: Compiler_Protobuf_Expression? = nil + var _afterthought: Compiler_Protobuf_Expression? = nil var _body: Compiler_Protobuf_Statement? = nil static let defaultInstance = _StorageClass() @@ -2625,9 +2715,9 @@ extension Compiler_Protobuf_ForLoop: SwiftProtobuf.Message, SwiftProtobuf._Messa private init() {} init(copying source: _StorageClass) { - _init_p = source._init_p - _test = source._test - _update = source._update + _initializer = source._initializer + _condition = source._condition + _afterthought = source._afterthought _body = source._body } } @@ -2647,10 +2737,35 @@ extension Compiler_Protobuf_ForLoop: SwiftProtobuf.Message, SwiftProtobuf._Messa // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularMessageField(value: &_storage._init_p) }() - case 2: try { try decoder.decodeSingularMessageField(value: &_storage._test) }() - case 3: try { try decoder.decodeSingularMessageField(value: &_storage._update) }() - case 4: try { try decoder.decodeSingularMessageField(value: &_storage._body) }() + case 1: try { + var v: Compiler_Protobuf_VariableDeclaration? + var hadOneofValue = false + if let current = _storage._initializer { + hadOneofValue = true + if case .declaration(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._initializer = .declaration(v) + } + }() + case 2: try { + var v: Compiler_Protobuf_Expression? + var hadOneofValue = false + if let current = _storage._initializer { + hadOneofValue = true + if case .expression(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._initializer = .expression(v) + } + }() + case 3: try { try decoder.decodeSingularMessageField(value: &_storage._condition) }() + case 4: try { try decoder.decodeSingularMessageField(value: &_storage._afterthought) }() + case 5: try { try decoder.decodeSingularMessageField(value: &_storage._body) }() default: break } } @@ -2663,18 +2778,26 @@ extension Compiler_Protobuf_ForLoop: SwiftProtobuf.Message, SwiftProtobuf._Messa // allocates stack space for every if/case branch local when no optimizations // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and // https://github.com/apple/swift-protobuf/issues/1182 - try { if let v = _storage._init_p { + switch _storage._initializer { + case .declaration?: try { + guard case .declaration(let v)? = _storage._initializer else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } }() - try { if let v = _storage._test { + }() + case .expression?: try { + guard case .expression(let v)? = _storage._initializer else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } }() - try { if let v = _storage._update { + }() + case nil: break + } + try { if let v = _storage._condition { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) } }() - try { if let v = _storage._body { + try { if let v = _storage._afterthought { try visitor.visitSingularMessageField(value: v, fieldNumber: 4) } }() + try { if let v = _storage._body { + try visitor.visitSingularMessageField(value: v, fieldNumber: 5) + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -2684,9 +2807,9 @@ extension Compiler_Protobuf_ForLoop: SwiftProtobuf.Message, SwiftProtobuf._Messa let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in let _storage = _args.0 let rhs_storage = _args.1 - if _storage._init_p != rhs_storage._init_p {return false} - if _storage._test != rhs_storage._test {return false} - if _storage._update != rhs_storage._update {return false} + if _storage._initializer != rhs_storage._initializer {return false} + if _storage._condition != rhs_storage._condition {return false} + if _storage._afterthought != rhs_storage._afterthought {return false} if _storage._body != rhs_storage._body {return false} return true } @@ -2865,6 +2988,44 @@ extension Compiler_Protobuf_ForOfLoop: SwiftProtobuf.Message, SwiftProtobuf._Mes } } +extension Compiler_Protobuf_BreakStatement: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BreakStatement" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { + } + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Compiler_Protobuf_BreakStatement, rhs: Compiler_Protobuf_BreakStatement) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Compiler_Protobuf_ContinueStatement: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".ContinueStatement" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { + } + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Compiler_Protobuf_ContinueStatement, rhs: Compiler_Protobuf_ContinueStatement) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Compiler_Protobuf_CatchClause: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".CatchClause" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -3070,8 +3231,10 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes 10: .same(proto: "forLoop"), 11: .same(proto: "forInLoop"), 12: .same(proto: "forOfLoop"), - 13: .same(proto: "tryStatement"), - 14: .same(proto: "throwStatement"), + 13: .same(proto: "breakStatement"), + 14: .same(proto: "continueStatement"), + 15: .same(proto: "tryStatement"), + 16: .same(proto: "throwStatement"), ] fileprivate class _StorageClass { @@ -3258,6 +3421,32 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes } }() case 13: try { + var v: Compiler_Protobuf_BreakStatement? + var hadOneofValue = false + if let current = _storage._statement { + hadOneofValue = true + if case .breakStatement(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._statement = .breakStatement(v) + } + }() + case 14: try { + var v: Compiler_Protobuf_ContinueStatement? + var hadOneofValue = false + if let current = _storage._statement { + hadOneofValue = true + if case .continueStatement(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._statement = .continueStatement(v) + } + }() + case 15: try { var v: Compiler_Protobuf_TryStatement? var hadOneofValue = false if let current = _storage._statement { @@ -3270,7 +3459,7 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes _storage._statement = .tryStatement(v) } }() - case 14: try { + case 16: try { var v: Compiler_Protobuf_ThrowStatement? var hadOneofValue = false if let current = _storage._statement { @@ -3344,13 +3533,21 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes guard case .forOfLoop(let v)? = _storage._statement else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 12) }() + case .breakStatement?: try { + guard case .breakStatement(let v)? = _storage._statement else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 13) + }() + case .continueStatement?: try { + guard case .continueStatement(let v)? = _storage._statement else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 14) + }() case .tryStatement?: try { guard case .tryStatement(let v)? = _storage._statement else { preconditionFailure() } - try visitor.visitSingularMessageField(value: v, fieldNumber: 13) + try visitor.visitSingularMessageField(value: v, fieldNumber: 15) }() case .throwStatement?: try { guard case .throwStatement(let v)? = _storage._statement else { preconditionFailure() } - try visitor.visitSingularMessageField(value: v, fieldNumber: 14) + try visitor.visitSingularMessageField(value: v, fieldNumber: 16) }() case nil: break } diff --git a/Sources/Fuzzilli/Protobuf/ast.proto b/Sources/Fuzzilli/Protobuf/ast.proto index 4c4b9884..8303ec5b 100644 --- a/Sources/Fuzzilli/Protobuf/ast.proto +++ b/Sources/Fuzzilli/Protobuf/ast.proto @@ -89,10 +89,16 @@ message DoWhileLoop { } message ForLoop { - VariableDeclarator init = 1; - Expression test = 2; - Expression update = 3; - Statement body = 4; + // This field is optional + oneof initializer { + VariableDeclaration declaration = 1; + Expression expression = 2; + } + // This field is optional + Expression condition = 3; + // This field is optional + Expression afterthought = 4; + Statement body = 5; } message ForInLoop { @@ -107,6 +113,12 @@ message ForOfLoop { Statement body = 3; } +message BreakStatement { +} + +message ContinueStatement { +} + message CatchClause { // The parameter is optional Parameter parameter = 1; @@ -143,8 +155,10 @@ message Statement { ForLoop forLoop = 10; ForInLoop forInLoop = 11; ForOfLoop forOfLoop = 12; - TryStatement tryStatement = 13; - ThrowStatement throwStatement = 14; + BreakStatement breakStatement = 13; + ContinueStatement continueStatement = 14; + TryStatement tryStatement = 15; + ThrowStatement throwStatement = 16; } } diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 3de4b909..1a03535d 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -2134,21 +2134,47 @@ public struct Fuzzilli_Protobuf_EndDoWhileLoop { public init() {} } -public struct Fuzzilli_Protobuf_BeginFor { +public struct Fuzzilli_Protobuf_BeginForLoopInitializer { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var comparator: Fuzzilli_Protobuf_Comparator = .equal + public var unknownFields = SwiftProtobuf.UnknownStorage() - public var op: Fuzzilli_Protobuf_BinaryOperator = .add + public init() {} +} + +public struct Fuzzilli_Protobuf_BeginForLoopCondition { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Fuzzilli_Protobuf_BeginForLoopAfterthought { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Fuzzilli_Protobuf_BeginForLoopBody { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} } -public struct Fuzzilli_Protobuf_EndFor { +public struct Fuzzilli_Protobuf_EndForLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2158,7 +2184,7 @@ public struct Fuzzilli_Protobuf_EndFor { public init() {} } -public struct Fuzzilli_Protobuf_BeginForIn { +public struct Fuzzilli_Protobuf_BeginForInLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2168,7 +2194,7 @@ public struct Fuzzilli_Protobuf_BeginForIn { public init() {} } -public struct Fuzzilli_Protobuf_EndForIn { +public struct Fuzzilli_Protobuf_EndForInLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2178,7 +2204,7 @@ public struct Fuzzilli_Protobuf_EndForIn { public init() {} } -public struct Fuzzilli_Protobuf_BeginForOf { +public struct Fuzzilli_Protobuf_BeginForOfLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2188,7 +2214,7 @@ public struct Fuzzilli_Protobuf_BeginForOf { public init() {} } -public struct Fuzzilli_Protobuf_BeginForOfWithDestruct { +public struct Fuzzilli_Protobuf_BeginForOfLoopWithDestruct { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2202,7 +2228,7 @@ public struct Fuzzilli_Protobuf_BeginForOfWithDestruct { public init() {} } -public struct Fuzzilli_Protobuf_EndForOf { +public struct Fuzzilli_Protobuf_EndForOfLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2212,7 +2238,7 @@ public struct Fuzzilli_Protobuf_EndForOf { public init() {} } -public struct Fuzzilli_Protobuf_BeginRepeat { +public struct Fuzzilli_Protobuf_BeginRepeatLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2224,7 +2250,7 @@ public struct Fuzzilli_Protobuf_BeginRepeat { public init() {} } -public struct Fuzzilli_Protobuf_EndRepeat { +public struct Fuzzilli_Protobuf_EndRepeatLoop { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2506,15 +2532,18 @@ extension Fuzzilli_Protobuf_EndWhileLoop: @unchecked Sendable {} extension Fuzzilli_Protobuf_BeginDoWhileLoopBody: @unchecked Sendable {} extension Fuzzilli_Protobuf_BeginDoWhileLoopHeader: @unchecked Sendable {} extension Fuzzilli_Protobuf_EndDoWhileLoop: @unchecked Sendable {} -extension Fuzzilli_Protobuf_BeginFor: @unchecked Sendable {} -extension Fuzzilli_Protobuf_EndFor: @unchecked Sendable {} -extension Fuzzilli_Protobuf_BeginForIn: @unchecked Sendable {} -extension Fuzzilli_Protobuf_EndForIn: @unchecked Sendable {} -extension Fuzzilli_Protobuf_BeginForOf: @unchecked Sendable {} -extension Fuzzilli_Protobuf_BeginForOfWithDestruct: @unchecked Sendable {} -extension Fuzzilli_Protobuf_EndForOf: @unchecked Sendable {} -extension Fuzzilli_Protobuf_BeginRepeat: @unchecked Sendable {} -extension Fuzzilli_Protobuf_EndRepeat: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForLoopInitializer: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForLoopCondition: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForLoopAfterthought: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForLoopBody: @unchecked Sendable {} +extension Fuzzilli_Protobuf_EndForLoop: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForInLoop: @unchecked Sendable {} +extension Fuzzilli_Protobuf_EndForInLoop: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForOfLoop: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginForOfLoopWithDestruct: @unchecked Sendable {} +extension Fuzzilli_Protobuf_EndForOfLoop: @unchecked Sendable {} +extension Fuzzilli_Protobuf_BeginRepeatLoop: @unchecked Sendable {} +extension Fuzzilli_Protobuf_EndRepeatLoop: @unchecked Sendable {} extension Fuzzilli_Protobuf_LoopBreak: @unchecked Sendable {} extension Fuzzilli_Protobuf_LoopContinue: @unchecked Sendable {} extension Fuzzilli_Protobuf_BeginTry: @unchecked Sendable {} @@ -6764,46 +6793,84 @@ extension Fuzzilli_Protobuf_EndDoWhileLoop: SwiftProtobuf.Message, SwiftProtobuf } } -extension Fuzzilli_Protobuf_BeginFor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".BeginFor" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "comparator"), - 2: .same(proto: "op"), - ] +extension Fuzzilli_Protobuf_BeginForLoopInitializer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForLoopInitializer" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularEnumField(value: &self.comparator) }() - case 2: try { try decoder.decodeSingularEnumField(value: &self.op) }() - default: break - } + while let _ = try decoder.nextFieldNumber() { + } + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_BeginForLoopInitializer, rhs: Fuzzilli_Protobuf_BeginForLoopInitializer) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Fuzzilli_Protobuf_BeginForLoopCondition: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForLoopCondition" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { } } public func traverse(visitor: inout V) throws { - if self.comparator != .equal { - try visitor.visitSingularEnumField(value: self.comparator, fieldNumber: 1) + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_BeginForLoopCondition, rhs: Fuzzilli_Protobuf_BeginForLoopCondition) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Fuzzilli_Protobuf_BeginForLoopAfterthought: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForLoopAfterthought" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { } - if self.op != .add { - try visitor.visitSingularEnumField(value: self.op, fieldNumber: 2) + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_BeginForLoopAfterthought, rhs: Fuzzilli_Protobuf_BeginForLoopAfterthought) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Fuzzilli_Protobuf_BeginForLoopBody: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForLoopBody" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { } + } + + public func traverse(visitor: inout V) throws { try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_BeginFor, rhs: Fuzzilli_Protobuf_BeginFor) -> Bool { - if lhs.comparator != rhs.comparator {return false} - if lhs.op != rhs.op {return false} + public static func ==(lhs: Fuzzilli_Protobuf_BeginForLoopBody, rhs: Fuzzilli_Protobuf_BeginForLoopBody) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_EndFor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".EndFor" +extension Fuzzilli_Protobuf_EndForLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EndForLoop" public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { @@ -6815,14 +6882,14 @@ extension Fuzzilli_Protobuf_EndFor: SwiftProtobuf.Message, SwiftProtobuf._Messag try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_EndFor, rhs: Fuzzilli_Protobuf_EndFor) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_EndForLoop, rhs: Fuzzilli_Protobuf_EndForLoop) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_BeginForIn: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".BeginForIn" +extension Fuzzilli_Protobuf_BeginForInLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForInLoop" public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { @@ -6834,14 +6901,14 @@ extension Fuzzilli_Protobuf_BeginForIn: SwiftProtobuf.Message, SwiftProtobuf._Me try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_BeginForIn, rhs: Fuzzilli_Protobuf_BeginForIn) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_BeginForInLoop, rhs: Fuzzilli_Protobuf_BeginForInLoop) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_EndForIn: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".EndForIn" +extension Fuzzilli_Protobuf_EndForInLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EndForInLoop" public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { @@ -6853,14 +6920,14 @@ extension Fuzzilli_Protobuf_EndForIn: SwiftProtobuf.Message, SwiftProtobuf._Mess try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_EndForIn, rhs: Fuzzilli_Protobuf_EndForIn) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_EndForInLoop, rhs: Fuzzilli_Protobuf_EndForInLoop) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_BeginForOf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".BeginForOf" +extension Fuzzilli_Protobuf_BeginForOfLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForOfLoop" public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { @@ -6872,14 +6939,14 @@ extension Fuzzilli_Protobuf_BeginForOf: SwiftProtobuf.Message, SwiftProtobuf._Me try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_BeginForOf, rhs: Fuzzilli_Protobuf_BeginForOf) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_BeginForOfLoop, rhs: Fuzzilli_Protobuf_BeginForOfLoop) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_BeginForOfWithDestruct: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".BeginForOfWithDestruct" +extension Fuzzilli_Protobuf_BeginForOfLoopWithDestruct: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginForOfLoopWithDestruct" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "indices"), 2: .same(proto: "hasRestElement"), @@ -6908,7 +6975,7 @@ extension Fuzzilli_Protobuf_BeginForOfWithDestruct: SwiftProtobuf.Message, Swift try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_BeginForOfWithDestruct, rhs: Fuzzilli_Protobuf_BeginForOfWithDestruct) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct, rhs: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct) -> Bool { if lhs.indices != rhs.indices {return false} if lhs.hasRestElement_p != rhs.hasRestElement_p {return false} if lhs.unknownFields != rhs.unknownFields {return false} @@ -6916,8 +6983,8 @@ extension Fuzzilli_Protobuf_BeginForOfWithDestruct: SwiftProtobuf.Message, Swift } } -extension Fuzzilli_Protobuf_EndForOf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".EndForOf" +extension Fuzzilli_Protobuf_EndForOfLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EndForOfLoop" public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { @@ -6929,14 +6996,14 @@ extension Fuzzilli_Protobuf_EndForOf: SwiftProtobuf.Message, SwiftProtobuf._Mess try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_EndForOf, rhs: Fuzzilli_Protobuf_EndForOf) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_EndForOfLoop, rhs: Fuzzilli_Protobuf_EndForOfLoop) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_BeginRepeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".BeginRepeat" +extension Fuzzilli_Protobuf_BeginRepeatLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginRepeatLoop" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "iterations"), ] @@ -6960,15 +7027,15 @@ extension Fuzzilli_Protobuf_BeginRepeat: SwiftProtobuf.Message, SwiftProtobuf._M try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_BeginRepeat, rhs: Fuzzilli_Protobuf_BeginRepeat) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_BeginRepeatLoop, rhs: Fuzzilli_Protobuf_BeginRepeatLoop) -> Bool { if lhs.iterations != rhs.iterations {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Fuzzilli_Protobuf_EndRepeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".EndRepeat" +extension Fuzzilli_Protobuf_EndRepeatLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EndRepeatLoop" public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { @@ -6980,7 +7047,7 @@ extension Fuzzilli_Protobuf_EndRepeat: SwiftProtobuf.Message, SwiftProtobuf._Mes try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Fuzzilli_Protobuf_EndRepeat, rhs: Fuzzilli_Protobuf_EndRepeat) -> Bool { + public static func ==(lhs: Fuzzilli_Protobuf_EndRepeatLoop, rhs: Fuzzilli_Protobuf_EndRepeatLoop) -> Bool { if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index 3519ba25..9d2e7de0 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -627,36 +627,43 @@ message BeginDoWhileLoopHeader { message EndDoWhileLoop { } -message BeginFor { - Comparator comparator = 1; - BinaryOperator op = 2; +message BeginForLoopInitializer { +} + +message BeginForLoopCondition { +} + +message BeginForLoopAfterthought { +} + +message BeginForLoopBody { } -message EndFor { +message EndForLoop { } -message BeginForIn { +message BeginForInLoop { } -message EndForIn { +message EndForInLoop { } -message BeginForOf { +message BeginForOfLoop { } -message BeginForOfWithDestruct { +message BeginForOfLoopWithDestruct { repeated int32 indices = 1; bool hasRestElement = 2; } -message EndForOf { +message EndForOfLoop { } -message BeginRepeat { +message BeginRepeatLoop { int64 iterations = 1; } -message EndRepeat { +message EndRepeatLoop { } message LoopBreak { diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift index d0d4002e..7527bb48 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -1207,76 +1207,100 @@ public struct Fuzzilli_Protobuf_Instruction { set {operation = .endDoWhileLoop(newValue)} } - public var beginFor: Fuzzilli_Protobuf_BeginFor { + public var beginForLoopInitializer: Fuzzilli_Protobuf_BeginForLoopInitializer { get { - if case .beginFor(let v)? = operation {return v} - return Fuzzilli_Protobuf_BeginFor() + if case .beginForLoopInitializer(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForLoopInitializer() } - set {operation = .beginFor(newValue)} + set {operation = .beginForLoopInitializer(newValue)} } - public var endFor: Fuzzilli_Protobuf_EndFor { + public var beginForLoopCondition: Fuzzilli_Protobuf_BeginForLoopCondition { get { - if case .endFor(let v)? = operation {return v} - return Fuzzilli_Protobuf_EndFor() + if case .beginForLoopCondition(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForLoopCondition() } - set {operation = .endFor(newValue)} + set {operation = .beginForLoopCondition(newValue)} } - public var beginForIn: Fuzzilli_Protobuf_BeginForIn { + public var beginForLoopAfterthought: Fuzzilli_Protobuf_BeginForLoopAfterthought { get { - if case .beginForIn(let v)? = operation {return v} - return Fuzzilli_Protobuf_BeginForIn() + if case .beginForLoopAfterthought(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForLoopAfterthought() } - set {operation = .beginForIn(newValue)} + set {operation = .beginForLoopAfterthought(newValue)} } - public var endForIn: Fuzzilli_Protobuf_EndForIn { + public var beginForLoopBody: Fuzzilli_Protobuf_BeginForLoopBody { get { - if case .endForIn(let v)? = operation {return v} - return Fuzzilli_Protobuf_EndForIn() + if case .beginForLoopBody(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForLoopBody() } - set {operation = .endForIn(newValue)} + set {operation = .beginForLoopBody(newValue)} } - public var beginForOf: Fuzzilli_Protobuf_BeginForOf { + public var endForLoop: Fuzzilli_Protobuf_EndForLoop { get { - if case .beginForOf(let v)? = operation {return v} - return Fuzzilli_Protobuf_BeginForOf() + if case .endForLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_EndForLoop() } - set {operation = .beginForOf(newValue)} + set {operation = .endForLoop(newValue)} } - public var beginForOfWithDestruct: Fuzzilli_Protobuf_BeginForOfWithDestruct { + public var beginForInLoop: Fuzzilli_Protobuf_BeginForInLoop { get { - if case .beginForOfWithDestruct(let v)? = operation {return v} - return Fuzzilli_Protobuf_BeginForOfWithDestruct() + if case .beginForInLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForInLoop() } - set {operation = .beginForOfWithDestruct(newValue)} + set {operation = .beginForInLoop(newValue)} } - public var endForOf: Fuzzilli_Protobuf_EndForOf { + public var endForInLoop: Fuzzilli_Protobuf_EndForInLoop { get { - if case .endForOf(let v)? = operation {return v} - return Fuzzilli_Protobuf_EndForOf() + if case .endForInLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_EndForInLoop() } - set {operation = .endForOf(newValue)} + set {operation = .endForInLoop(newValue)} } - public var beginRepeat: Fuzzilli_Protobuf_BeginRepeat { + public var beginForOfLoop: Fuzzilli_Protobuf_BeginForOfLoop { get { - if case .beginRepeat(let v)? = operation {return v} - return Fuzzilli_Protobuf_BeginRepeat() + if case .beginForOfLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForOfLoop() } - set {operation = .beginRepeat(newValue)} + set {operation = .beginForOfLoop(newValue)} } - public var endRepeat: Fuzzilli_Protobuf_EndRepeat { + public var beginForOfLoopWithDestruct: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct { get { - if case .endRepeat(let v)? = operation {return v} - return Fuzzilli_Protobuf_EndRepeat() + if case .beginForOfLoopWithDestruct(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginForOfLoopWithDestruct() } - set {operation = .endRepeat(newValue)} + set {operation = .beginForOfLoopWithDestruct(newValue)} + } + + public var endForOfLoop: Fuzzilli_Protobuf_EndForOfLoop { + get { + if case .endForOfLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_EndForOfLoop() + } + set {operation = .endForOfLoop(newValue)} + } + + public var beginRepeatLoop: Fuzzilli_Protobuf_BeginRepeatLoop { + get { + if case .beginRepeatLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginRepeatLoop() + } + set {operation = .beginRepeatLoop(newValue)} + } + + public var endRepeatLoop: Fuzzilli_Protobuf_EndRepeatLoop { + get { + if case .endRepeatLoop(let v)? = operation {return v} + return Fuzzilli_Protobuf_EndRepeatLoop() + } + set {operation = .endRepeatLoop(newValue)} } public var loopBreak: Fuzzilli_Protobuf_LoopBreak { @@ -1540,15 +1564,18 @@ public struct Fuzzilli_Protobuf_Instruction { case beginDoWhileLoopBody(Fuzzilli_Protobuf_BeginDoWhileLoopBody) case beginDoWhileLoopHeader(Fuzzilli_Protobuf_BeginDoWhileLoopHeader) case endDoWhileLoop(Fuzzilli_Protobuf_EndDoWhileLoop) - case beginFor(Fuzzilli_Protobuf_BeginFor) - case endFor(Fuzzilli_Protobuf_EndFor) - case beginForIn(Fuzzilli_Protobuf_BeginForIn) - case endForIn(Fuzzilli_Protobuf_EndForIn) - case beginForOf(Fuzzilli_Protobuf_BeginForOf) - case beginForOfWithDestruct(Fuzzilli_Protobuf_BeginForOfWithDestruct) - case endForOf(Fuzzilli_Protobuf_EndForOf) - case beginRepeat(Fuzzilli_Protobuf_BeginRepeat) - case endRepeat(Fuzzilli_Protobuf_EndRepeat) + case beginForLoopInitializer(Fuzzilli_Protobuf_BeginForLoopInitializer) + case beginForLoopCondition(Fuzzilli_Protobuf_BeginForLoopCondition) + case beginForLoopAfterthought(Fuzzilli_Protobuf_BeginForLoopAfterthought) + case beginForLoopBody(Fuzzilli_Protobuf_BeginForLoopBody) + case endForLoop(Fuzzilli_Protobuf_EndForLoop) + case beginForInLoop(Fuzzilli_Protobuf_BeginForInLoop) + case endForInLoop(Fuzzilli_Protobuf_EndForInLoop) + case beginForOfLoop(Fuzzilli_Protobuf_BeginForOfLoop) + case beginForOfLoopWithDestruct(Fuzzilli_Protobuf_BeginForOfLoopWithDestruct) + case endForOfLoop(Fuzzilli_Protobuf_EndForOfLoop) + case beginRepeatLoop(Fuzzilli_Protobuf_BeginRepeatLoop) + case endRepeatLoop(Fuzzilli_Protobuf_EndRepeatLoop) case loopBreak(Fuzzilli_Protobuf_LoopBreak) case loopContinue(Fuzzilli_Protobuf_LoopContinue) case beginTry(Fuzzilli_Protobuf_BeginTry) @@ -2150,40 +2177,52 @@ public struct Fuzzilli_Protobuf_Instruction { guard case .endDoWhileLoop(let l) = lhs, case .endDoWhileLoop(let r) = rhs else { preconditionFailure() } return l == r }() - case (.beginFor, .beginFor): return { - guard case .beginFor(let l) = lhs, case .beginFor(let r) = rhs else { preconditionFailure() } + case (.beginForLoopInitializer, .beginForLoopInitializer): return { + guard case .beginForLoopInitializer(let l) = lhs, case .beginForLoopInitializer(let r) = rhs else { preconditionFailure() } return l == r }() - case (.endFor, .endFor): return { - guard case .endFor(let l) = lhs, case .endFor(let r) = rhs else { preconditionFailure() } + case (.beginForLoopCondition, .beginForLoopCondition): return { + guard case .beginForLoopCondition(let l) = lhs, case .beginForLoopCondition(let r) = rhs else { preconditionFailure() } return l == r }() - case (.beginForIn, .beginForIn): return { - guard case .beginForIn(let l) = lhs, case .beginForIn(let r) = rhs else { preconditionFailure() } + case (.beginForLoopAfterthought, .beginForLoopAfterthought): return { + guard case .beginForLoopAfterthought(let l) = lhs, case .beginForLoopAfterthought(let r) = rhs else { preconditionFailure() } return l == r }() - case (.endForIn, .endForIn): return { - guard case .endForIn(let l) = lhs, case .endForIn(let r) = rhs else { preconditionFailure() } + case (.beginForLoopBody, .beginForLoopBody): return { + guard case .beginForLoopBody(let l) = lhs, case .beginForLoopBody(let r) = rhs else { preconditionFailure() } return l == r }() - case (.beginForOf, .beginForOf): return { - guard case .beginForOf(let l) = lhs, case .beginForOf(let r) = rhs else { preconditionFailure() } + case (.endForLoop, .endForLoop): return { + guard case .endForLoop(let l) = lhs, case .endForLoop(let r) = rhs else { preconditionFailure() } return l == r }() - case (.beginForOfWithDestruct, .beginForOfWithDestruct): return { - guard case .beginForOfWithDestruct(let l) = lhs, case .beginForOfWithDestruct(let r) = rhs else { preconditionFailure() } + case (.beginForInLoop, .beginForInLoop): return { + guard case .beginForInLoop(let l) = lhs, case .beginForInLoop(let r) = rhs else { preconditionFailure() } return l == r }() - case (.endForOf, .endForOf): return { - guard case .endForOf(let l) = lhs, case .endForOf(let r) = rhs else { preconditionFailure() } + case (.endForInLoop, .endForInLoop): return { + guard case .endForInLoop(let l) = lhs, case .endForInLoop(let r) = rhs else { preconditionFailure() } return l == r }() - case (.beginRepeat, .beginRepeat): return { - guard case .beginRepeat(let l) = lhs, case .beginRepeat(let r) = rhs else { preconditionFailure() } + case (.beginForOfLoop, .beginForOfLoop): return { + guard case .beginForOfLoop(let l) = lhs, case .beginForOfLoop(let r) = rhs else { preconditionFailure() } return l == r }() - case (.endRepeat, .endRepeat): return { - guard case .endRepeat(let l) = lhs, case .endRepeat(let r) = rhs else { preconditionFailure() } + case (.beginForOfLoopWithDestruct, .beginForOfLoopWithDestruct): return { + guard case .beginForOfLoopWithDestruct(let l) = lhs, case .beginForOfLoopWithDestruct(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.endForOfLoop, .endForOfLoop): return { + guard case .endForOfLoop(let l) = lhs, case .endForOfLoop(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.beginRepeatLoop, .beginRepeatLoop): return { + guard case .beginRepeatLoop(let l) = lhs, case .beginRepeatLoop(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.endRepeatLoop, .endRepeatLoop): return { + guard case .endRepeatLoop(let l) = lhs, case .endRepeatLoop(let r) = rhs else { preconditionFailure() } return l == r }() case (.loopBreak, .loopBreak): return { @@ -2446,15 +2485,18 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M 50: .same(proto: "beginDoWhileLoopBody"), 51: .same(proto: "beginDoWhileLoopHeader"), 189: .same(proto: "endDoWhileLoop"), - 52: .same(proto: "beginFor"), - 53: .same(proto: "endFor"), - 54: .same(proto: "beginForIn"), - 55: .same(proto: "endForIn"), - 56: .same(proto: "beginForOf"), - 103: .same(proto: "beginForOfWithDestruct"), - 57: .same(proto: "endForOf"), - 133: .same(proto: "beginRepeat"), - 134: .same(proto: "endRepeat"), + 52: .same(proto: "beginForLoopInitializer"), + 53: .same(proto: "beginForLoopCondition"), + 190: .same(proto: "beginForLoopAfterthought"), + 191: .same(proto: "beginForLoopBody"), + 192: .same(proto: "endForLoop"), + 54: .same(proto: "beginForInLoop"), + 55: .same(proto: "endForInLoop"), + 56: .same(proto: "beginForOfLoop"), + 103: .same(proto: "beginForOfLoopWithDestruct"), + 57: .same(proto: "endForOfLoop"), + 133: .same(proto: "beginRepeatLoop"), + 134: .same(proto: "endRepeatLoop"), 58: .same(proto: "loopBreak"), 59: .same(proto: "loopContinue"), 60: .same(proto: "beginTry"), @@ -3046,81 +3088,81 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M } }() case 52: try { - var v: Fuzzilli_Protobuf_BeginFor? + var v: Fuzzilli_Protobuf_BeginForLoopInitializer? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .beginFor(let m) = current {v = m} + if case .beginForLoopInitializer(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .beginFor(v) + self.operation = .beginForLoopInitializer(v) } }() case 53: try { - var v: Fuzzilli_Protobuf_EndFor? + var v: Fuzzilli_Protobuf_BeginForLoopCondition? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .endFor(let m) = current {v = m} + if case .beginForLoopCondition(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .endFor(v) + self.operation = .beginForLoopCondition(v) } }() case 54: try { - var v: Fuzzilli_Protobuf_BeginForIn? + var v: Fuzzilli_Protobuf_BeginForInLoop? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .beginForIn(let m) = current {v = m} + if case .beginForInLoop(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .beginForIn(v) + self.operation = .beginForInLoop(v) } }() case 55: try { - var v: Fuzzilli_Protobuf_EndForIn? + var v: Fuzzilli_Protobuf_EndForInLoop? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .endForIn(let m) = current {v = m} + if case .endForInLoop(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .endForIn(v) + self.operation = .endForInLoop(v) } }() case 56: try { - var v: Fuzzilli_Protobuf_BeginForOf? + var v: Fuzzilli_Protobuf_BeginForOfLoop? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .beginForOf(let m) = current {v = m} + if case .beginForOfLoop(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .beginForOf(v) + self.operation = .beginForOfLoop(v) } }() case 57: try { - var v: Fuzzilli_Protobuf_EndForOf? + var v: Fuzzilli_Protobuf_EndForOfLoop? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .endForOf(let m) = current {v = m} + if case .endForOfLoop(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .endForOf(v) + self.operation = .endForOfLoop(v) } }() case 58: try { @@ -3631,16 +3673,16 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M } }() case 103: try { - var v: Fuzzilli_Protobuf_BeginForOfWithDestruct? + var v: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .beginForOfWithDestruct(let m) = current {v = m} + if case .beginForOfLoopWithDestruct(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .beginForOfWithDestruct(v) + self.operation = .beginForOfLoopWithDestruct(v) } }() case 104: try { @@ -3930,29 +3972,29 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M } }() case 133: try { - var v: Fuzzilli_Protobuf_BeginRepeat? + var v: Fuzzilli_Protobuf_BeginRepeatLoop? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .beginRepeat(let m) = current {v = m} + if case .beginRepeatLoop(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .beginRepeat(v) + self.operation = .beginRepeatLoop(v) } }() case 134: try { - var v: Fuzzilli_Protobuf_EndRepeat? + var v: Fuzzilli_Protobuf_EndRepeatLoop? var hadOneofValue = false if let current = self.operation { hadOneofValue = true - if case .endRepeat(let m) = current {v = m} + if case .endRepeatLoop(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.operation = .endRepeat(v) + self.operation = .endRepeatLoop(v) } }() case 135: try { @@ -4657,6 +4699,45 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .endDoWhileLoop(v) } }() + case 190: try { + var v: Fuzzilli_Protobuf_BeginForLoopAfterthought? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .beginForLoopAfterthought(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .beginForLoopAfterthought(v) + } + }() + case 191: try { + var v: Fuzzilli_Protobuf_BeginForLoopBody? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .beginForLoopBody(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .beginForLoopBody(v) + } + }() + case 192: try { + var v: Fuzzilli_Protobuf_EndForLoop? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .endForLoop(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .endForLoop(v) + } + }() default: break } } @@ -4847,28 +4928,28 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .beginDoWhileLoopHeader(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 51) }() - case .beginFor?: try { - guard case .beginFor(let v)? = self.operation else { preconditionFailure() } + case .beginForLoopInitializer?: try { + guard case .beginForLoopInitializer(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 52) }() - case .endFor?: try { - guard case .endFor(let v)? = self.operation else { preconditionFailure() } + case .beginForLoopCondition?: try { + guard case .beginForLoopCondition(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 53) }() - case .beginForIn?: try { - guard case .beginForIn(let v)? = self.operation else { preconditionFailure() } + case .beginForInLoop?: try { + guard case .beginForInLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 54) }() - case .endForIn?: try { - guard case .endForIn(let v)? = self.operation else { preconditionFailure() } + case .endForInLoop?: try { + guard case .endForInLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 55) }() - case .beginForOf?: try { - guard case .beginForOf(let v)? = self.operation else { preconditionFailure() } + case .beginForOfLoop?: try { + guard case .beginForOfLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 56) }() - case .endForOf?: try { - guard case .endForOf(let v)? = self.operation else { preconditionFailure() } + case .endForOfLoop?: try { + guard case .endForOfLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 57) }() case .loopBreak?: try { @@ -5027,8 +5108,8 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .createTemplateString(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 102) }() - case .beginForOfWithDestruct?: try { - guard case .beginForOfWithDestruct(let v)? = self.operation else { preconditionFailure() } + case .beginForOfLoopWithDestruct?: try { + guard case .beginForOfLoopWithDestruct(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 103) }() case .switchBreak?: try { @@ -5119,12 +5200,12 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .probe(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 132) }() - case .beginRepeat?: try { - guard case .beginRepeat(let v)? = self.operation else { preconditionFailure() } + case .beginRepeatLoop?: try { + guard case .beginRepeatLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 133) }() - case .endRepeat?: try { - guard case .endRepeat(let v)? = self.operation else { preconditionFailure() } + case .endRepeatLoop?: try { + guard case .endRepeatLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 134) }() case .createIntArray?: try { @@ -5343,6 +5424,18 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .endDoWhileLoop(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 189) }() + case .beginForLoopAfterthought?: try { + guard case .beginForLoopAfterthought(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 190) + }() + case .beginForLoopBody?: try { + guard case .beginForLoopBody(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 191) + }() + case .endForLoop?: try { + guard case .endForLoop(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 192) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index da399aff..dd1df8b1 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -169,15 +169,18 @@ message Instruction { BeginDoWhileLoopBody beginDoWhileLoopBody = 50; BeginDoWhileLoopHeader beginDoWhileLoopHeader = 51; EndDoWhileLoop endDoWhileLoop= 189; - BeginFor beginFor = 52; - EndFor endFor = 53; - BeginForIn beginForIn = 54; - EndForIn endForIn = 55; - BeginForOf beginForOf = 56; - BeginForOfWithDestruct beginForOfWithDestruct = 103; - EndForOf endForOf = 57; - BeginRepeat beginRepeat = 133; - EndRepeat endRepeat = 134; + BeginForLoopInitializer beginForLoopInitializer = 52; + BeginForLoopCondition beginForLoopCondition = 53; + BeginForLoopAfterthought beginForLoopAfterthought = 190; + BeginForLoopBody beginForLoopBody = 191; + EndForLoop endForLoop = 192; + BeginForInLoop beginForInLoop = 54; + EndForInLoop endForInLoop = 55; + BeginForOfLoop beginForOfLoop = 56; + BeginForOfLoopWithDestruct beginForOfLoopWithDestruct = 103; + EndForOfLoop endForOfLoop = 57; + BeginRepeatLoop beginRepeatLoop = 133; + EndRepeatLoop endRepeatLoop = 134; LoopBreak loopBreak = 58; LoopContinue loopContinue = 59; BeginTry beginTry = 60; diff --git a/Sources/FuzzilliCli/CodeGeneratorWeights.swift b/Sources/FuzzilliCli/CodeGeneratorWeights.swift index 67db4b51..ce599a82 100644 --- a/Sources/FuzzilliCli/CodeGeneratorWeights.swift +++ b/Sources/FuzzilliCli/CodeGeneratorWeights.swift @@ -140,7 +140,8 @@ let codeGeneratorWeights = [ "SwitchBlockGenerator": 5, "WhileLoopGenerator": 15, "DoWhileLoopGenerator": 15, - "ForLoopGenerator": 15, + "SimpleForLoopGenerator": 10, + "ComplexForLoopGenerator": 10, "ForInLoopGenerator": 10, "ForOfLoopGenerator": 10, "ForOfWithDestructLoopGenerator": 3, diff --git a/Sources/FuzzilliCli/Profiles/JSCProfile.swift b/Sources/FuzzilliCli/Profiles/JSCProfile.swift index 696aae97..889ca61f 100644 --- a/Sources/FuzzilliCli/Profiles/JSCProfile.swift +++ b/Sources/FuzzilliCli/Profiles/JSCProfile.swift @@ -20,7 +20,7 @@ fileprivate let ForceDFGCompilationGenerator = CodeGenerator("ForceDFGCompilatio guard b.type(of: f).Is(.function()) else { return } guard let arguments = b.randomCallArguments(for: f) else { return } - b.buildRepeat(n: 10) { _ in + b.buildRepeatLoop(n: 10) { _ in b.callFunction(f, withArgs: arguments) } } @@ -29,7 +29,7 @@ fileprivate let ForceFTLCompilationGenerator = CodeGenerator("ForceFTLCompilatio guard b.type(of: f).Is(.function()) else { return } guard let arguments = b.randomCallArguments(for: f) else { return } - b.buildRepeat(n: 100) { _ in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: arguments) } } diff --git a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift index b394ec95..25c706bf 100644 --- a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift @@ -19,7 +19,7 @@ fileprivate let ForceQV4JITGenerator = CodeGenerator("ForceQV4JITGenerator", inp // For both these reasons, we abort here if we cannot prove that f is indeed a function. guard b.type(of: f).Is(.function()) else { return } guard let arguments = b.randomCallArguments(for: f) else { return } - b.buildRepeat(n: 100){ _ in + b.buildRepeatLoop(n: 100){ _ in b.callFunction(f, withArgs: arguments) } } diff --git a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift index 739b5e64..b73c9e42 100644 --- a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift +++ b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift @@ -20,7 +20,7 @@ fileprivate let ForceSpidermonkeyIonGenerator = CodeGenerator("ForceSpidermonkey guard b.type(of: f).Is(.function()) else { return } guard let arguments = b.randomCallArguments(for: f) else { return } - b.buildRepeat(n: 100) { _ in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: arguments) } } diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 34e8701d..ed7aad02 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -20,7 +20,7 @@ fileprivate let ForceJITCompilationThroughLoopGenerator = CodeGenerator("ForceJI guard b.type(of: f).Is(.function()) else { return } guard let arguments = b.randomCallArguments(for: f) else { return } - b.buildRepeat(n: 100) { _ in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: arguments) } } @@ -194,7 +194,7 @@ fileprivate let MapTransitionsTemplate = ProgramTemplate("MapTransitionsTemplate let functionJitCallGenerator = CodeGenerator("FunctionJitCall", input: .function()) { b, f in let args = b.randomCallArguments(for: sig)! assert(objects.contains(args[0]) && objects.contains(args[1])) - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { _ in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: args) // Rval goes out-of-scope immediately, so no need to track it } } @@ -260,7 +260,7 @@ fileprivate let VerifyTypeTemplate = ProgramTemplate("VerifyTypeTemplate") { b i b.build(n: genSize) // trigger JIT - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: b.generateCallArguments(for: signature)) } @@ -269,7 +269,7 @@ fileprivate let VerifyTypeTemplate = ProgramTemplate("VerifyTypeTemplate") { b i b.callFunction(f, withArgs: b.generateCallArguments(for: signature)) // maybe trigger recompilation - b.buildForLoop(b.loadInt(0), .lessThan, b.loadInt(100), .Add, b.loadInt(1)) { args in + b.buildRepeatLoop(n: 100) { _ in b.callFunction(f, withArgs: b.generateCallArguments(for: signature)) } diff --git a/Tests/FuzzilliTests/AnalyzerTest.swift b/Tests/FuzzilliTests/AnalyzerTest.swift index e8868d60..767704c3 100644 --- a/Tests/FuzzilliTests/AnalyzerTest.swift +++ b/Tests/FuzzilliTests/AnalyzerTest.swift @@ -78,10 +78,7 @@ class AnalyzerTests: XCTestCase { XCTAssertEqual(b.context, [.javascript, .subroutine, .loop]) b.buildPlainFunction(with: .parameters(n: 2)) { args in XCTAssertEqual(b.context, [.javascript, .subroutine]) - let v1 = b.loadInt(0) - let v2 = b.loadInt(10) - let v3 = b.loadInt(20) - b.buildForLoop(v1, .lessThan, v2, .Add, v3) { _ in + b.buildRepeatLoop(n: 10) { _ in XCTAssertEqual(b.context, [.javascript, .subroutine, .loop]) } } @@ -205,10 +202,7 @@ class AnalyzerTests: XCTestCase { XCTAssertEqual(b.context, .javascript) let _ = b.buildCodeString() { XCTAssertEqual(b.context, .javascript) - let v11 = b.loadInt(0) - let v12 = b.loadInt(2) - let v13 = b.loadInt(1) - b.buildForLoop(v11, .lessThan, v12, .Add, v13) { _ in + b.buildRepeatLoop(n: 10) { _ in b.loadInt(1337) XCTAssertEqual(b.context, [.javascript, .loop]) let _ = b.buildCodeString() { @@ -251,6 +245,17 @@ class AnalyzerTests: XCTestCase { return b.loadBool(false) }) + b.buildForLoop({ + XCTAssertEqual(b.context, [.javascript]) + }, { + XCTAssertEqual(b.context, [.javascript]) + return b.loadBool(false) + }, { + XCTAssertEqual(b.context, [.javascript]) + }) { + XCTAssertEqual(b.context, [.javascript, .subroutine, .loop]) + } + b.buildForInLoop(args[1]) { _ in XCTAssertEqual(b.context, [.javascript, .subroutine, .loop]) } @@ -263,7 +268,7 @@ class AnalyzerTests: XCTestCase { XCTAssertEqual(b.context, [.javascript, .subroutine, .loop]) } - b.buildRepeat(n: 100) { _ in + b.buildRepeatLoop(n: 100) { _ in XCTAssertEqual(b.context, [.javascript, .subroutine, .loop]) } diff --git a/Tests/FuzzilliTests/CompilerTests/advanced_loops.js b/Tests/FuzzilliTests/CompilerTests/advanced_loops.js index f3512b66..7f40a8bf 100644 --- a/Tests/FuzzilliTests/CompilerTests/advanced_loops.js +++ b/Tests/FuzzilliTests/CompilerTests/advanced_loops.js @@ -57,12 +57,35 @@ do { resetCounter(); -/* +// +// For loops +// for (;;) { if (!counter--) { break; } output("inside for loop body"); + continue; + output("should not be reached"); +} +resetCounter(); + +for (let i = 0, j = 10; i < j; i++, j--) { + output("inside for loop body, i: " + i + " j: " + j); +} + +for (; countdown();) { + output("inside for loop body"); +} +resetCounter(); + +for (let i = 0; ; i++) { + output("inside for loop body"); + if (i >= 5) break; +} + +for (output("inside for loop initializer"); output("inside for loop condition"), true; output("inside for loop afterthought")) { + output("inside for loop body"); + if (!countdown()) break; } resetCounter(); -*/ diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index 0ce0a80f..6e0dbed2 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -460,7 +460,7 @@ class JSTyperTests: XCTestCase { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - for i in 0..<5 { + for i in 0..<6 { let intVar1 = b.loadInt(0) let intVar2 = b.loadInt(100) let intVar3 = b.loadInt(42) @@ -482,8 +482,7 @@ class JSTyperTests: XCTestCase { // Select loop type switch i { case 0: - b.buildForLoop(intVar1, .lessThan, intVar2, .Add, intVar3) { loopVar in - XCTAssertEqual(b.type(of: loopVar), .primitive) + b.buildForLoop() { body() } case 1: @@ -502,6 +501,10 @@ class JSTyperTests: XCTestCase { body() } case 4: + b.buildRepeatLoop(n: 10) { _ in + body() + } + case 5: b.buildPlainFunction(with: .parameters(n: 3)) { _ in body() } @@ -880,6 +883,57 @@ class JSTyperTests: XCTestCase { XCTAssertEqual(b.type(of: v2), .string) } + func testForLoopHandling() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let v1 = b.loadInt(0) + let v2 = b.loadInt(1) + let v3 = b.loadInt(2) + let v4 = b.loadInt(3) + // The initializer block and the condition block are always executed. + // The afterthought and body block may not be executed. + b.buildForLoop({ + b.reassign(v1, to: b.loadString("foo")) + }, { + b.reassign(v2, to: b.loadString("bar")) + return b.loadBool(false) + }, { + b.reassign(v3, to: b.loadString("baz")) + }) { + b.reassign(v4, to: b.loadString("bla")) + } + + XCTAssertEqual(b.type(of: v1), .string) + XCTAssertEqual(b.type(of: v2), .string) + XCTAssertEqual(b.type(of: v3), .integer | .string) + XCTAssertEqual(b.type(of: v4), .integer | .string) + } + + func testForLoopLoopVariableTyping() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop(i: { b.loadInt(0) }, + { i in XCTAssertEqual(b.type(of: i), .integer); return b.compare(i, with: b.loadInt(10), using: .lessThan) }, + { i in XCTAssertEqual(b.type(of: i), .integer); b.unary(.PostInc, i) }) { i in + XCTAssertEqual(b.type(of: i), .integer) + } + + b.buildForLoop(i: { b.loadInt(0) }, + { i in + XCTAssertEqual(b.type(of: i), .integer); + b.buildForLoop(i: { b.loadFloat(12.34) }, { i in XCTAssertEqual(b.type(of: i), .float); return b.loadBool(false) }, { i in XCTAssertEqual(b.type(of: i), .float )}) { i in + XCTAssertEqual(b.type(of: i), .float) + } + return b.compare(i, with: b.loadInt(10), using: .lessThan) + + }, + { i in XCTAssertEqual(b.type(of: i), .integer); b.unary(.PostInc, i) }) { i in + XCTAssertEqual(b.type(of: i), .integer) + } + } + func testSwitchStatementHandling() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index e10a9662..5becc87b 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -694,8 +694,7 @@ class LifterTests: XCTestCase { let expected = """ let v6 = "foobar"; - v6 = undefined; - const v8 = [1,2,,4,,6,v6]; + const v8 = [1,2,,4,,6,v6 = undefined]; [301,,]; [,]; [...v8,,]; @@ -974,7 +973,37 @@ class LifterTests: XCTestCase { XCTAssertEqual(actual, expected) } - func testBinaryOperationReassignLifting() { + func testReassignmentLifting() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let v0 = b.loadInt(1337) + let v1 = b.loadFloat(13.37) + b.reassign(v0, to: v1, with: .Add) + let v2 = b.loadString("Hello") + b.reassign(v1, to: v2) + let v3 = b.loadInt(1336) + let v4 = b.unary(.PreInc, v3) + b.unary(.PostInc, v4) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + let v0 = 1337; + let v1 = 13.37; + v0 += v1; + v1 = "Hello"; + let v3 = 1336; + let v4 = ++v3; + v4++; + + """ + + XCTAssertEqual(actual, expected) + } + + func testUpdateLifting() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() @@ -1003,30 +1032,76 @@ class LifterTests: XCTestCase { XCTAssertEqual(actual, expected) } - func testReassignmentLifting() { + func testReassignmentInlining() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - let v0 = b.loadInt(1337) - let v1 = b.loadFloat(13.37) - b.reassign(v0, to: v1, with: .Add) - let v2 = b.loadString("Hello") - b.reassign(v1, to: v2) - let v3 = b.loadInt(1336) - let v4 = b.unary(.PreInc, v3) - b.unary(.PostInc, v4) + let foo = b.loadBuiltin("foo") + let bar = b.loadBuiltin("bar") + let baz = b.loadBuiltin("baz") + let i = b.loadInt(42) + b.reassign(i, to: b.loadInt(43)) + b.callFunction(foo, withArgs: [i]) + b.callFunction(bar, withArgs: [i]) + + let j = b.loadInt(44) + b.reassign(j, to: b.loadInt(45)) + b.reassign(j, to: b.loadInt(46)) + b.callFunction(foo, withArgs: [j]) + + let k = b.loadInt(47) + b.buildRepeatLoop(n: 10) { i in + b.reassign(k, to: i) + } + b.callFunction(foo, withArgs: [k]) + + let l = b.loadInt(48) + b.reassign(l, to: b.loadInt(49)) + b.callFunction(foo, withArgs: [l, l, l]) + + let m = b.loadInt(50) + b.reassign(m, to: b.loadInt(51)) + var t = b.callFunction(baz) + t = b.callFunction(bar, withArgs: [m, m, t, m]) + b.callFunction(foo, withArgs: [t]) + + // Some operations such as element stores force the lhs to be an identifier, so test that here. + let n = b.loadInt(52) + b.reassign(n, to: b.createArray(with: [])) + b.setElement(42, of: n, to: n) + + let o = b.loadInt(53) + b.buildWhileLoop({ b.reassign(o, to: i); return b.loadBool(false) }) { + } + b.callFunction(foo, withArgs: [o]) let program = b.finalize() let actual = fuzzer.lifter.lift(program) let expected = """ - let v0 = 1337; - let v1 = 13.37; - v0 += v1; - v1 = "Hello"; - let v3 = 1336; - let v4 = ++v3; - v4++; + let v3 = 42; + foo(v3 = 43); + bar(v3); + let v7 = 44; + v7 = 45; + v7 = 46; + foo(v7); + let v11 = 47; + for (let v12 = 0; v12 < 10; v12++) { + v11 = v12; + } + foo(v11); + let v14 = 48; + foo(v14 = 49, v14, v14); + let v17 = 50; + foo(bar(v17 = 51, v17, baz(), v17)); + let v22 = 52; + v22 = []; + v22[42] = v22; + let v24 = 53; + while (v24 = v3, false) { + } + foo(v24); """ @@ -1841,6 +1916,7 @@ class LifterTests: XCTestCase { f = b.loadBuiltin("f") g = b.loadBuiltin("g") b.buildWhileLoop({ let cond = b.callFunction(f); b.callFunction(g); return cond }) { + b.callFunction(b.loadBuiltin("body")) } program = b.finalize() @@ -1848,10 +1924,11 @@ class LifterTests: XCTestCase { expected = """ while ((() => { - const v2 = f(); - g(); - return v2; - })()) { + const v2 = f(); + g(); + return v2; + })()) { + body(); } """ @@ -1877,9 +1954,9 @@ class LifterTests: XCTestCase { let expected = """ while ((() => { - const v1 = foobar(); - return v1 + v1; - })()) { + const v1 = foobar(); + return v1 + v1; + })()) { doLoopBodyStuff(); } @@ -1888,6 +1965,36 @@ class LifterTests: XCTestCase { XCTAssertEqual(actual, expected) } + func testWhileLoopLifting6() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let print = b.loadBuiltin("print") + let f = b.callFunction(b.loadBuiltin("f")) + let g = b.callFunction(b.loadBuiltin("g")) + let h = b.callFunction(b.loadBuiltin("h")) + b.buildWhileLoop({ b.callFunction(print, withArgs: [f]); return b.loadBool(false) }) { + b.callFunction(print, withArgs: [g]) + } + b.callFunction(print, withArgs: [h]) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const v2 = f(); + const v4 = g(); + const v6 = h(); + while (print(v2), false) { + print(v4); + } + print(v6); + + """ + + XCTAssertEqual(actual, expected) + } + func testDoWhileLoopLifting1() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() @@ -1941,17 +2048,272 @@ class LifterTests: XCTestCase { XCTAssertEqual(actual, expected) } - func testForLoopLifting() { + func testDoWhileLoopLifting3() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let print = b.loadBuiltin("print") + let f = b.callFunction(b.loadBuiltin("f")) + let g = b.callFunction(b.loadBuiltin("g")) + let h = b.callFunction(b.loadBuiltin("h")) + b.buildDoWhileLoop(do: { + b.callFunction(print, withArgs: [f]) + }, while: { b.callFunction(print, withArgs: [g]); return b.loadBool(false) }) + b.callFunction(print, withArgs: [h]) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const v2 = f(); + const v4 = g(); + const v6 = h(); + do { + print(v2); + } while (print(v4), false) + print(v6); + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting1() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop(i: { b.loadInt(0) }, { i in b.compare(i, with: b.loadInt(10), using: .lessThan) }, { i in b.unary(.PostInc, i) }) { i in + b.buildForLoop(i: { b.loadInt(0) }, { j in b.compare(j, with: i, using: .lessThan) }, { j in b.unary(.PostInc, j) }) { j in + let print = b.loadBuiltin("print") + b.callFunction(print, withArgs: [i, j]) + } + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (let i1 = 0; i1 < 10; i1++) { + for (let i8 = 0; i8 < i1; i8++) { + print(i1, i8); + } + } + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting2() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop() { + b.loopBreak() + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (;;) { + break; + } + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting3() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop({ return [b.callFunction(b.loadBuiltin("f1")), b.callFunction(b.loadBuiltin("f2"))] }, + { vars in b.callFunction(b.loadBuiltin("f3"), withArgs: [vars[0]]); return b.callFunction(b.loadBuiltin("f4"), withArgs: [vars[1]]) }, + { vars in b.callFunction(b.loadBuiltin("f5"), withArgs: [vars[1]]); b.callFunction(b.loadBuiltin("f6"), withArgs: [vars[0]]) }) { vars in + b.callFunction(b.loadBuiltin("f7"), withArgs: vars) + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (let i4 = f1(), i5 = f2(); f3(i4), f4(i5); f5(i5), f6(i4)) { + f7(i4, i5); + } + + """ + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting4() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop({ let x = b.callFunction(b.loadBuiltin("f")); let y = b.callFunction(b.loadBuiltin("g")); b.callFunction(b.loadBuiltin("h")); return [x, y] }, + { vs in return b.compare(vs[0], with: vs[1], using: .lessThan) }, + { vs in b.reassign(vs[0], to: vs[1], with: .Add) }) { vs in + b.callFunction(b.loadBuiltin("print"), withArgs: vs) + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (let [i6, i7] = (() => { + const v1 = f(); + const v3 = g(); + h(); + return [v1, v3]; + })(); + i6 < i7; + i6 += i7) { + print(i6, i7); + } + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting5() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop({ b.callFunction(b.loadBuiltin("foo")); b.callFunction(b.loadBuiltin("bar")) }, + { + let shouldContinue = b.callFunction(b.loadBuiltin("shouldContinue")) + b.buildIf(b.callFunction(b.loadBuiltin("shouldNotContinue"))) { + b.reassign(shouldContinue, to: b.loadBool(false)) + } + return shouldContinue + }) { + b.loopBreak() + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (foo(), bar(); + (() => { + let v5 = shouldContinue(); + if (shouldNotContinue()) { + v5 = false; + } + return v5; + })(); + ) { + break; + } + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting6() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop(i: { b.loadInt(0) }, { b.compare($0, with: b.loadInt(100), using: .lessThan) }, { b.reassign($0, to: b.loadInt(10), with: .Add) }) { i in + b.callFunction(b.loadBuiltin("print"), withArgs: [i]) + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (let i1 = 0; i1 < 100; i1 += 10) { + print(i1); + } + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting7() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildForLoop(i: { b.callFunction(b.loadBuiltin("f")); return b.callFunction(b.loadBuiltin("g")) }, {_ in b.loadBool(true) }, { _ in }) { i in + b.loopBreak() + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + for (let i4 = (() => { + f(); + return g(); + })(); + ; + ) { + break; + } + + """ + + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting8() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - let start = b.loadInt(42) - let end = b.loadInt(0) - let step = b.loadInt(2) - b.buildForLoop(start, .greaterThan, end, .Div, step) { i in - let print = b.loadBuiltin("print") + b.buildPlainFunction(with: .parameters(n: 3)) { args in + b.buildForLoop(i: { args[0] }, { i in b.compare(i, with: args[1], using: .greaterThanOrEqual) }, { i in b.reassign(i, to: args[2], with: .Sub)}) { vars in + } + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + function f0(a1, a2, a3) { + for (let i4 = a1; i4 >= a2; i4 -= a3) { + } + } + + """ + XCTAssertEqual(actual, expected) + } + + func testForLoopLifting9() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let print = b.loadBuiltin("print") + let f = b.callFunction(b.loadBuiltin("f")) + let g = b.callFunction(b.loadBuiltin("g")) + let h = b.callFunction(b.loadBuiltin("h")) + let i = b.callFunction(b.loadBuiltin("i")) + let j = b.callFunction(b.loadBuiltin("j")) + + b.buildForLoop({ b.callFunction(print, withArgs: [f]) }, { b.callFunction(print, withArgs: [g]) }, { b.callFunction(print, withArgs: [h]) }) { b.callFunction(print, withArgs: [i]) } + b.callFunction(print, withArgs: [j]) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const v2 = f(); + const v4 = g(); + const v6 = h(); + const v8 = i(); + const v10 = j(); + for (print(v2); print(v4); print(v6)) { + print(v8); + } + print(v10); + + """ + XCTAssertEqual(actual, expected) } func testRepeatLoopLifting() { @@ -1959,7 +2321,7 @@ class LifterTests: XCTestCase { let b = fuzzer.makeBuilder() let s = b.loadInt(0) - b.buildRepeat(n: 1337) { i in + b.buildRepeatLoop(n: 1337) { i in b.reassign(s, to: i, with: .Add) } let print = b.loadBuiltin("print") diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index b5c94245..29006e95 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -71,7 +71,7 @@ class ProgramBuilderTests: XCTestCase { b.loadInt(Int64.random(in: 0..<100)) } let recursiveGenerator = RecursiveCodeGenerator("RecursiveGenerator") { b in - b.buildRepeat(n: 5) { _ in + b.buildRepeatLoop(n: 5) { _ in b.buildRecursive() } } @@ -1294,9 +1294,9 @@ class ProgramBuilderTests: XCTestCase { // Original Program // var f = b.buildPlainFunction(with: .parameters(n: 2)) { args in - let step = b.loadInt(1) splicePoint = b.indexOfNextInstruction() - b.buildForLoop(args[0], .lessThan, args[1], .Add, step) { _ in + b.buildForLoop(i: { args[0] }, { i in b.compare(i, with: args[1], using: .lessThan) }, { i in b.unary(.PostInc, i) }) { i in + b.callFunction(b.loadBuiltin("print"), withArgs: [i]) b.loopBreak() } } @@ -1315,9 +1315,9 @@ class ProgramBuilderTests: XCTestCase { // Expected Program // f = b.buildPlainFunction(with: .parameters(n: 2)) { args in - let step = b.loadInt(1) splicePoint = b.indexOfNextInstruction() - b.buildForLoop(args[0], .lessThan, args[1], .Add, step) { _ in + b.buildForLoop(i: { args[0] }, { i in b.compare(i, with: args[1], using: .lessThan) }, { i in b.unary(.PostInc, i) }) { i in + b.callFunction(b.loadBuiltin("print"), withArgs: [i]) b.loopBreak() } } @@ -1394,7 +1394,7 @@ class ProgramBuilderTests: XCTestCase { cls.addConstructor(with: .parameters(n: 1)) { params in } - //cls.defineProperty("a") + cls.addInstanceProperty("a") cls.addInstanceMethod("f", with: .parameters(n: 1)) { params in b.doReturn(b.loadString("foobar")) @@ -1402,17 +1402,14 @@ class ProgramBuilderTests: XCTestCase { } let _ = b.buildClassDefinition(withSuperclass: superclass) { cls in cls.addConstructor(with: .parameters(n: 1)) { params in - let v3 = b.loadInt(0) - let v4 = b.loadInt(2) - let v5 = b.loadInt(1) - b.buildForLoop(v3, .lessThan, v4, .Add, v5) { _ in + b.buildRepeatLoop(n: 10) { _ in let v0 = b.loadInt(42) let v1 = b.createObject(with: ["foo": v0]) splicePoint = b.indexOfNextInstruction() b.callSuperConstructor(withArgs: [v1]) } } - //cls.defineProperty("b") + cls.addInstanceProperty("b") cls.addInstanceMethod("g", with: .parameters(n: 1)) { params in b.buildPlainFunction(with: .parameters(n: 0)) { _ in @@ -1466,18 +1463,15 @@ class ProgramBuilderTests: XCTestCase { // Original Program // b.buildAsyncGeneratorFunction(with: .parameters(n: 2)) { _ in - let v3 = b.loadInt(0) - let v4 = b.loadInt(2) - let v5 = b.loadInt(1) - b.buildForLoop(v3, .lessThan, v4, .Add, v5) { _ in + let p = b.loadBuiltin("thePromise") + b.buildDoWhileLoop(do: { let v0 = b.loadInt(42) let _ = b.createObject(with: ["foo": v0]) splicePoint = b.indexOfNextInstruction() - b.await(v3) + b.await(p) let v8 = b.loadInt(1337) b.yield(v8) - } - b.doReturn(v4) + }, while: { b.loadBool(false) }) } let original = b.finalize() @@ -1494,8 +1488,8 @@ class ProgramBuilderTests: XCTestCase { // Expected Program // b.buildAsyncFunction(with: .parameters(n: 1)) { _ in - let v0 = b.loadInt(0) - let _ = b.await(v0) + let p = b.loadBuiltin("thePromise") + let _ = b.await(p) } let expected = b.finalize() @@ -1708,10 +1702,7 @@ class ProgramBuilderTests: XCTestCase { // // Original Program // - let v2 = b.loadInt(0) - let v3 = b.loadInt(10) - let v4 = b.loadInt(20) - b.buildForLoop(v2, .lessThan, v3, .Add, v4) { _ in + b.buildRepeatLoop(n: 5) { _ in b.loadThis() let code = b.buildCodeString() { let i = b.loadInt(42)