Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/forawaitof #496

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Sources/Fuzzilli/Base/ProgramBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2748,6 +2748,19 @@ public class ProgramBuilder {
emit(EndForLoop())
}

public func buildForAwaitOfLoop(_ obj: Variable, _ body: (Variable) -> ()) {
let i = emit(BeginForAwaitOfLoop(), withInputs: [obj]).innerOutput
body(i)
emit(EndForAwaitOfLoop())
}

public func buildForAwaitOfLoop(_ obj: Variable, selecting indices: [Int64], hasRestElement: Bool = false, _ body: ([Variable]) -> ()) {
let instr = emit(BeginForAwaitOfLoopWithDestruct(indices: indices, hasRestElement: hasRestElement), withInputs: [obj])
body(Array(instr.innerOutputs))
emit(EndForAwaitOfLoop())
}


public func buildForInLoop(_ obj: Variable, _ body: (Variable) -> ()) {
let i = emit(BeginForInLoop(), withInputs: [obj]).innerOutput
body(i)
Expand Down
2 changes: 2 additions & 0 deletions Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ public let codeGeneratorWeights = [
"DoWhileLoopGenerator": 15,
"SimpleForLoopGenerator": 10,
"ComplexForLoopGenerator": 10,
"ForAwaitOfLoopGenerator": 10,
"ForAwaitOfWithDestructLoopGenerator": 3,
"ForInLoopGenerator": 10,
"ForOfLoopGenerator": 10,
"ForOfWithDestructLoopGenerator": 3,
Expand Down
27 changes: 27 additions & 0 deletions Sources/Fuzzilli/CodeGen/CodeGenerators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,33 @@ public let CodeGenerators: [CodeGenerator] = [
}
},

RecursiveCodeGenerator("ForAwaitOfLoopGenerator", inContext: .asyncFunction, inputs: .preferred(.iterable)) { b, obj in
assert(b.context.contains(.asyncFunction))

b.buildForAwaitOfLoop(obj) { _ in
b.buildRecursive()
}
},

RecursiveCodeGenerator("ForAwaitOfWithDestructLoopGenerator", inContext: .asyncFunction, inputs: .preferred(.iterable)) { b, obj in
assert(b.context.contains(.asyncFunction))

var indices: [Int64] = []
for idx in 0..<Int64.random(in: 1..<5) {
withProbability(0.8) {
indices.append(idx)
}
}

if indices.isEmpty {
indices = [0]
}

b.buildForAwaitOfLoop(obj, selecting: indices, hasRestElement: probability(0.2)) { _ in
b.buildRecursive()
}
},

RecursiveCodeGenerator("ForInLoopGenerator", inputs: .preferred(.object())) { b, obj in
b.buildForInLoop(obj) { _ in
b.buildRecursive()
Expand Down
21 changes: 21 additions & 0 deletions Sources/Fuzzilli/Compiler/Compiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,27 @@ public class JavaScriptCompiler {

emit(EndForLoop())

case .forAwaitOfLoop(let forAwaitOfLoop):
let initializer = forAwaitOfLoop.left;

if !contextAnalyzer.context.contains(.asyncFunction) {
throw CompilerError.invalidNodeError("`for await...of` is currently only supported in async functions")
}

guard !initializer.hasValue else {
throw CompilerError.invalidNodeError("Expected no initial value for the variable declared in a for-await-of loop")
}

let obj = try compileExpression(forAwaitOfLoop.right)

let loopVar = emit(BeginForAwaitOfLoop(), withInputs: [obj]).innerOutput
try enterNewScope {
map(initializer.name, to: loopVar)
try compileBody(forAwaitOfLoop.body)
}

emit(EndForAwaitOfLoop())

case .forInLoop(let forInLoop):
let initializer = forInLoop.left;
guard !initializer.hasValue else {
Expand Down
18 changes: 15 additions & 3 deletions Sources/Fuzzilli/Compiler/Parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,18 @@ function parse(script, proto) {
forLoop.body = visitStatement(node.body);
return makeStatement('ForLoop', forLoop);
}
case 'ForAwaitOfStatement': {
assert(node.left.type === 'VaraibleDeclaration', "Expected variable declaration as init part of a for-await-of loop, found " + node.left.type)
assert(node.left.declarations.length === 1, "Expected exactly one variable declaration in the init part of a for-await-of loop");
let decl = node.left.declarations[0];
let forAwaitOfLoop = {};
let initDecl = { name: decl.id.name };
assert(decl.init == null, "Expected no initial value for the varaible declared as part of a for-await-of loop")
forAwaitOfLoop.left = make('VariableDeclarator', initDecl);
forAwaitOfLoop.right = visitExpression(node.right);
forAwaitOfLoop.body = visitStatement(node.body);
return makeStatement('ForAwaitOfLoop', forAwaitOfLoop);
}
case 'ForInStatement': {
assert(node.left.type === 'VariableDeclaration', "Expected variable declaration as init part of a for-in loop, found " + node.left.type);
assert(node.left.declarations.length === 1, "Expected exactly one variable declaration in the init part of a for-in loop");
Expand All @@ -294,12 +306,12 @@ function parse(script, proto) {
return makeStatement('ForInLoop', forInLoop);
}
case 'ForOfStatement': {
assert(node.left.type === 'VariableDeclaration', "Expected variable declaration as init part of a for-in loop, found " + node.left.type);
assert(node.left.declarations.length === 1, "Expected exactly one variable declaration in the init part of a for-in loop");
assert(node.left.type === 'VariableDeclaration', "Expected variable declaration as init part of a for-of loop, found " + node.left.type);
assert(node.left.declarations.length === 1, "Expected exactly one variable declaration in the init part of a for-of loop");
let decl = node.left.declarations[0];
let forOfLoop = {};
let initDecl = { name: decl.id.name };
assert(decl.init == null, "Expected no initial value for the variable declared as part of a for-in loop")
assert(decl.init == null, "Expected no initial value for the variable declared as part of a for-of loop")
forOfLoop.left = make('VariableDeclarator', initDecl);
forOfLoop.right = visitExpression(node.right);
forOfLoop.body = visitStatement(node.body);
Expand Down
15 changes: 15 additions & 0 deletions Sources/Fuzzilli/FuzzIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,15 @@ extension Instruction: ProtobufConvertible {
$0.beginForLoopBody = Fuzzilli_Protobuf_BeginForLoopBody()
case .endForLoop:
$0.endForLoop = Fuzzilli_Protobuf_EndForLoop()
case .beginForAwaitOfLoop:
$0.beginForAwaitOfLoop = Fuzzilli_Protobuf_BeginForAwaitOfLoop()
case .beginForAwaitOfLoopWithDestruct(let op):
$0.beginForAwaitOfLoopWithDestruct = Fuzzilli_Protobuf_BeginForAwaitOfLoopWithDestruct.with {
$0.indices = op.indices.map({ Int32($0) })
$0.hasRestElement_p = op.hasRestElement
}
case .endForAwaitOfLoop:
$0.endForAwaitOfLoop = Fuzzilli_Protobuf_EndForAwaitOfLoop()
case .beginForInLoop:
$0.beginForInLoop = Fuzzilli_Protobuf_BeginForInLoop()
case .endForInLoop:
Expand Down Expand Up @@ -1852,6 +1861,12 @@ extension Instruction: ProtobufConvertible {
op = BeginForLoopBody(numLoopVariables: inouts.count)
case .endForLoop:
op = EndForLoop()
case .beginForAwaitOfLoop:
op = BeginForAwaitOfLoop()
case .beginForAwaitOfLoopWithDestruct(let p):
op = BeginForAwaitOfLoopWithDestruct(indices: p.indices.map({ Int64($0) }), hasRestElement: p.hasRestElement_p)
case .endForAwaitOfLoop:
op = EndForAwaitOfLoop()
case .beginForInLoop:
op = BeginForInLoop()
case .endForInLoop:
Expand Down
11 changes: 11 additions & 0 deletions Sources/Fuzzilli/FuzzIL/JSTyper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ public struct JSTyper: Analyzer {
case .endForLoop:
state.endGroupOfConditionallyExecutingBlocks(typeChanges: &typeChanges)
case .beginWhileLoopBody,
.beginForAwaitOfLoop,
.beginForAwaitOfLoopWithDestruct,
.beginForInLoop,
.beginForOfLoop,
.beginForOfLoopWithDestruct,
Expand All @@ -409,6 +411,7 @@ public struct JSTyper: Analyzer {
// Push a new state tracking the types inside the loop
state.enterConditionallyExecutingBlock(typeChanges: &typeChanges)
case .endWhileLoop,
.endForAwaitOfLoop,
.endForInLoop,
.endForOfLoop,
.endRepeatLoop,
Expand Down Expand Up @@ -926,6 +929,14 @@ public struct JSTyper: Analyzer {
assert(inputTypes.count == instr.numInnerOutputs)
zip(instr.innerOutputs, inputTypes).forEach({ set($0, $1) })

case .beginForAwaitOfLoop:
set(instr.innerOutput, .string)

case .beginForAwaitOfLoopWithDestruct:
for v in instr.innerOutputs {
set(v, .anything)
}

case .beginForInLoop:
set(instr.innerOutput, .string)

Expand Down
31 changes: 31 additions & 0 deletions Sources/Fuzzilli/FuzzIL/JsOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,37 @@ final class EndForLoop: JsOperation {
}
}

final class BeginForAwaitOfLoop: JsOperation {
override var opcode: Opcode { .beginForAwaitOfLoop(self) }

init() {
super.init(numInputs: 1, numInnerOutputs: 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.asyncFunction], contextOpened: [.javascript, .loop])
}
}

final class BeginForAwaitOfLoopWithDestruct: JsOperation {
override var opcode: Opcode { .beginForAwaitOfLoopWithDestruct(self) }

let indices: [Int64]
let hasRestElement: Bool

init(indices: [Int64], hasRestElement: Bool) {
assert(indices.count >= 1)
self.indices = indices
self.hasRestElement = hasRestElement
super.init(numInputs: 1, numInnerOutputs: indices.count, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.asyncFunction], contextOpened: [.javascript, .loop])
}
}

final class EndForAwaitOfLoop: JsOperation {
override var opcode: Opcode { .endForAwaitOfLoop(self) }

init() {
super.init(attributes: .isBlockEnd)
}
}


final class BeginForInLoop: JsOperation {
override var opcode: Opcode { .beginForInLoop(self) }

Expand Down
3 changes: 3 additions & 0 deletions Sources/Fuzzilli/FuzzIL/Opcodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ enum Opcode {
case beginForLoopAfterthought(BeginForLoopAfterthought)
case beginForLoopBody(BeginForLoopBody)
case endForLoop(EndForLoop)
case beginForAwaitOfLoop(BeginForAwaitOfLoop)
case beginForAwaitOfLoopWithDestruct(BeginForAwaitOfLoopWithDestruct)
case endForAwaitOfLoop(EndForAwaitOfLoop)
case beginForInLoop(BeginForInLoop)
case endForInLoop(EndForInLoop)
case beginForOfLoop(BeginForOfLoop)
Expand Down
3 changes: 3 additions & 0 deletions Sources/Fuzzilli/FuzzIL/Semantics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ extension Operation {
return endOp is BeginForLoopBody
case .beginForLoopBody:
return endOp is EndForLoop
case .beginForAwaitOfLoop,
.beginForAwaitOfLoopWithDestruct:
return endOp is EndForAwaitOfLoop
case .beginForInLoop:
return endOp is EndForInLoop
case .beginForOfLoop,
Expand Down
13 changes: 13 additions & 0 deletions Sources/Fuzzilli/Lifting/FuzzILLifter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,19 @@ public class FuzzILLifter: Lifter {
w.decreaseIndentionLevel()
w.emit("EndForLoop")

case .beginForAwaitOfLoop:
w.emit("BeginForAwaitOfLooop \(input(0)) -> \(innerOutput())")
w.increaseIndentionLevel()

case .beginForAwaitOfLoopWithDestruct(let op):
let outputs = instr.innerOutputs.map(lift)
w.emit("BeginForAwaitOfLoopWithDestruct \(input(0)) -> [\(liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))]")
w.increaseIndentionLevel()

case .endForAwaitOfLoop:
w.decreaseIndentionLevel()
w.emit("EndForAwaitOfLoop")

case .beginForInLoop:
w.emit("BeginForInLoop \(input(0)) -> \(innerOutput())")
w.increaseIndentionLevel()
Expand Down
19 changes: 19 additions & 0 deletions Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,25 @@ public class JavaScriptLifter: Lifter {
w.leaveCurrentBlock()
w.emit("}")

case .beginForAwaitOfLoop:
let V = w.declare(instr.innerOutput)
let LET = w.declarationKeyword(for: instr.innerOutput)
let OBJ = input(0)
w.emit("for await (\(LET) \(V) of \(OBJ)) {")
w.enterNewBlock()

case .beginForAwaitOfLoopWithDestruct(let op):
let outputs = w.declareAll(instr.innerOutputs)
let PATTERN = liftArrayDestructPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement)
let LET = w.varKeyword
let OBJ = input(0)
w.emit("for await (\(LET) [\(PATTERN)] of \(OBJ)) {")
w.enterNewBlock()

case .endForAwaitOfLoop:
w.leaveCurrentBlock()
w.emit("}")

case .beginForInLoop:
let LET = w.declarationKeyword(for: instr.innerOutput)
let V = w.declare(instr.innerOutput)
Expand Down
2 changes: 2 additions & 0 deletions Sources/Fuzzilli/Minimization/BlockReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct BlockReducer: Reducer {
case .beginWhileLoopHeader,
.beginDoWhileLoopBody,
.beginForLoopInitializer,
.beginForAwaitOfLoop,
.beginForAwaitOfLoopWithDestruct,
.beginForInLoop,
.beginForOfLoop,
.beginForOfLoopWithDestruct,
Expand Down
8 changes: 5 additions & 3 deletions Sources/Fuzzilli/Minimization/LoopSimplifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ struct LoopSimplifier: Reducer {
tryReplaceDoWhileLoopWithRepeatLoop(group, with: helper)
case .beginRepeatLoop:
tryReduceRepeatLoopIterationCount(group, with: helper)
case .beginForInLoop,
.beginForOfLoop,
.beginForOfLoopWithDestruct:
case .beginForAwaitOfLoop,
.beginForAwaitOfLoopWithDestruct,
.beginForInLoop,
.beginForOfLoop,
.beginForOfLoopWithDestruct:
// These loops are (usually) guaranteed to terminate, and should probably anyway not be replaced by repeat-loops.
break
default:
Expand Down
3 changes: 3 additions & 0 deletions Sources/Fuzzilli/Mutators/OperationMutator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,9 @@ public class OperationMutator: BaseInstructionMutator {
.beginForLoopAfterthought(_),
.beginForLoopBody(_),
.endForLoop(_),
.beginForAwaitOfLoop(_),
.beginForAwaitOfLoopWithDestruct(_),
.endForAwaitOfLoop(_),
.beginForInLoop(_),
.endForInLoop(_),
.beginForOfLoop(_),
Expand Down
Loading