From c9372f813d471b3f0f9f69072a21610921b980cd Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Fri, 30 Jul 2021 23:01:13 +0100
Subject: [PATCH 1/5] CallMethod, CallComputedMethod and Construct now support
 Spread Elements

---
 Sources/Fuzzilli/Core/CodeGenerators.swift    |  41 +++++
 Sources/Fuzzilli/Core/ProgramBuilder.swift    |  15 ++
 .../Fuzzilli/FuzzIL/AbstractInterpreter.swift |   9 +-
 Sources/Fuzzilli/FuzzIL/Instruction.swift     |  16 ++
 Sources/Fuzzilli/FuzzIL/Operations.swift      |  46 +++++
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   |  33 ++++
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   |  35 ++++
 .../Fuzzilli/Mutators/OperationMutator.swift  |  21 +++
 Sources/Fuzzilli/Protobuf/operations.pb.swift | 140 +++++++++++++++
 Sources/Fuzzilli/Protobuf/operations.proto    |  13 ++
 Sources/Fuzzilli/Protobuf/program.pb.swift    |  81 +++++++++
 Sources/Fuzzilli/Protobuf/program.proto       |   3 +
 .../FuzzilliCli/CodeGeneratorWeights.swift    | 169 +++++++++---------
 Tests/FuzzilliTests/LifterTest.swift          |  98 +++++++++-
 14 files changed, 633 insertions(+), 87 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index d71e5186d..5d541f6b9 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -255,6 +255,21 @@ public let CodeGenerators: [CodeGenerator] = [
         b.callMethod(methodName!, on: obj, withArgs: arguments)
     },
 
+    CodeGenerator("MethodCallWithSpreadGenerator", input: .object()) { b, obj in
+        var methodName = b.type(of: obj).randomMethod()
+        if methodName == nil {
+            guard b.mode != .conservative else { return }
+            methodName = b.genMethodName()
+        }
+        guard let arguments = b.randCallArguments(forMethod: methodName!, on: obj) else { return }
+
+        let spreads = arguments.map({ arg in
+            probability(0.75) && b.type(of: arg).Is(.iterable)
+        })
+
+        b.callMethod(methodName!, on: obj, withArgs: arguments, spreading: spreads)
+    },
+
     CodeGenerator("ComputedMethodCallGenerator", input: .object()) { b, obj in
         var methodName = b.type(of: obj).randomMethod()
         if methodName == nil {
@@ -266,6 +281,22 @@ public let CodeGenerators: [CodeGenerator] = [
         b.callComputedMethod(method, on: obj, withArgs: arguments)
     },
 
+    CodeGenerator("ComputedMethodCallWithSpreadGenerator", input: .object()) { b, obj in
+        var methodName = b.type(of: obj).randomMethod()
+        if methodName == nil {
+            guard b.mode != .conservative else { return }
+            methodName = b.genMethodName()
+        }
+        let method = b.loadString(methodName!)
+        guard let arguments = b.randCallArguments(forMethod: methodName!, on: obj) else { return }
+
+        let spreads = arguments.map({ arg in
+            probability(0.75) && b.type(of: arg).Is(.iterable)
+        })
+
+        b.callComputedMethod(method, on: obj, withArgs: arguments, spreading: spreads)
+    },
+
     CodeGenerator("FunctionCallGenerator", input: .function()) { b, f in
         guard let arguments = b.randCallArguments(for: f) else { return }
         b.callFunction(f, withArgs: arguments)
@@ -288,6 +319,16 @@ public let CodeGenerators: [CodeGenerator] = [
         b.callFunction(f, withArgs: arguments, spreading: spreads)
     },
 
+    CodeGenerator("ConstructorCallWithSpreadGenerator", input: .constructor()) { b, c in
+        guard let arguments = b.randCallArguments(for: c) else { return }
+
+        let spreads = arguments.map({ arg in
+            probability(0.75) && b.type(of: arg).Is(.iterable)
+        })
+
+        b.construct(c, withArgs: arguments, spreading: spreads)
+    },
+
     CodeGenerator("FunctionReturnGenerator", inContext: .function, input: .anything) { b, val in
         assert(b.context.contains(.function))
         b.doReturn(value: val)
diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index cdd1e1dc9..166e1c6f0 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -1167,11 +1167,21 @@ public class ProgramBuilder {
         return perform(CallMethod(methodName: name, numArguments: arguments.count), withInputs: [object] + arguments).output
     }
 
+    @discardableResult
+    public func callMethod(_ name: String, on object: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
+        return perform(CallMethodWithSpread(methodName: name, numArguments: arguments.count, spreads: spreads), withInputs: [object] + arguments).output
+    }
+
     @discardableResult
     public func callComputedMethod(_ name: Variable, on object: Variable, withArgs arguments: [Variable]) -> Variable {
         return perform(CallComputedMethod(numArguments: arguments.count), withInputs: [object, name] + arguments).output
     }
 
+    @discardableResult
+    public func callComputedMethod(_ name: Variable, on object: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
+        return perform(CallComputedMethodWithSpread(numArguments: arguments.count, spreads: spreads), withInputs: [object, name] + arguments).output
+    }
+
     @discardableResult
     public func callFunction(_ function: Variable, withArgs arguments: [Variable]) -> Variable {
         return perform(CallFunction(numArguments: arguments.count), withInputs: [function] + arguments).output
@@ -1187,6 +1197,11 @@ public class ProgramBuilder {
         return perform(CallFunctionWithSpread(numArguments: arguments.count, spreads: spreads), withInputs: [function] + arguments).output
     }
 
+    @discardableResult
+    public func construct(_ constructor: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
+        return perform(ConstructWithSpread(numArguments: arguments.count, spreads: spreads), withInputs: [constructor] + arguments).output
+    }
+
     @discardableResult
     public func unary(_ op: UnaryOperator, _ input: Variable) -> Variable {
         return perform(UnaryOperation(op), withInputs: [input]).output
diff --git a/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift b/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
index 2d23de5e6..2c0a339d8 100644
--- a/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
+++ b/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
@@ -388,7 +388,8 @@ public struct AbstractInterpreter {
         // For now we treat this as .unknown
         case is LoadElement,
              is LoadComputedProperty,
-             is CallComputedMethod:
+             is CallComputedMethod,
+             is CallComputedMethodWithSpread:
             set(instr.output, .unknown)
 
         case is ConditionalOperation:
@@ -402,7 +403,11 @@ public struct AbstractInterpreter {
         case let op as CallMethod:
             set(instr.output, inferMethodSignature(of: op.methodName, on: instr.input(0)).outputType)
 
-        case is Construct:
+        case let op as CallMethodWithSpread:
+            set(instr.output, inferMethodSignature(of: op.methodName, on: instr.input(0)).outputType)
+
+        case is Construct,
+             is ConstructWithSpread:
             set(instr.output, inferConstructedType(of: instr.input(0)))
 
         case let op as UnaryOperation:
diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift
index b61cb4374..e69e8e8e1 100644
--- a/Sources/Fuzzilli/FuzzIL/Instruction.swift
+++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift
@@ -352,14 +352,23 @@ extension Instruction: ProtobufConvertible {
                 $0.await = Fuzzilli_Protobuf_Await()
             case let op as CallMethod:
                 $0.callMethod = Fuzzilli_Protobuf_CallMethod.with { $0.methodName = op.methodName }
+            case let op as CallMethodWithSpread:
+                $0.callMethodWithSpread = Fuzzilli_Protobuf_CallMethodWithSpread.with { 
+                    $0.methodName = op.methodName
+                    $0.spreads = op.spreads 
+                }
             case is CallComputedMethod:
                 $0.callComputedMethod = Fuzzilli_Protobuf_CallComputedMethod()
+            case let op as CallComputedMethodWithSpread:
+                $0.callComputedMethodWithSpread = Fuzzilli_Protobuf_CallComputedMethodWithSpread.with { $0.spreads = op.spreads }
             case is CallFunction:
                 $0.callFunction = Fuzzilli_Protobuf_CallFunction()
             case is Construct:
                 $0.construct = Fuzzilli_Protobuf_Construct()
             case let op as CallFunctionWithSpread:
                 $0.callFunctionWithSpread = Fuzzilli_Protobuf_CallFunctionWithSpread.with { $0.spreads = op.spreads }
+            case let op as ConstructWithSpread:
+                $0.constructWithSpread = Fuzzilli_Protobuf_ConstructWithSpread.with { $0.spreads = op.spreads }
             case let op as UnaryOperation:
                 $0.unaryOperation = Fuzzilli_Protobuf_UnaryOperation.with { $0.op = convertEnum(op.op, allUnaryOperators) }
             case let op as BinaryOperation:
@@ -585,15 +594,22 @@ extension Instruction: ProtobufConvertible {
             op = Await()
         case .callMethod(let p):
             op = CallMethod(methodName: p.methodName, numArguments: inouts.count - 2)
+        case .callMethodWithSpread(let p):
+            op = CallMethodWithSpread(methodName: p.methodName, numArguments: inouts.count - 2, spreads: p.spreads)
         case .callComputedMethod(_):
             // We subtract 3 from the inouts count since the first two elements are the callee and method and the last element is the output variable
             op = CallComputedMethod(numArguments: inouts.count - 3)
+        case .callComputedMethodWithSpread(let p):
+            // We subtract 3 from the inouts count since the first two elements are the callee and method and the last element is the output variable
+            op = CallComputedMethodWithSpread(numArguments: inouts.count - 3, spreads: p.spreads)
         case .callFunction(_):
             op = CallFunction(numArguments: inouts.count - 2)
         case .construct(_):
             op = Construct(numArguments: inouts.count - 2)
         case .callFunctionWithSpread(let p):
             op = CallFunctionWithSpread(numArguments: inouts.count - 2, spreads: p.spreads)
+        case .constructWithSpread(let p):
+            op = ConstructWithSpread(numArguments: inouts.count - 2, spreads: p.spreads)
         case .unaryOperation(let p):
             op = UnaryOperation(try convertEnum(p.op, allUnaryOperators))
         case .binaryOperation(let p):
diff --git a/Sources/Fuzzilli/FuzzIL/Operations.swift b/Sources/Fuzzilli/FuzzIL/Operations.swift
index 0f4233591..986d49d93 100644
--- a/Sources/Fuzzilli/FuzzIL/Operations.swift
+++ b/Sources/Fuzzilli/FuzzIL/Operations.swift
@@ -444,6 +444,23 @@ class CallMethod: Operation {
     }
 }
 
+class CallMethodWithSpread: Operation {
+    let methodName: String
+    let spreads: [Bool]
+
+    var numArguments: Int {
+        return numInputs - 1
+    }
+    
+    init(methodName: String, numArguments: Int, spreads: [Bool]) {
+        assert(spreads.count == numArguments)
+        self.methodName = methodName
+        self.spreads = spreads
+        // reference object is the first input
+        super.init(numInputs: numArguments + 1, numOutputs: 1, attributes: [.isParametric, .isVarargs, .isCall])
+    }
+}
+
 class CallComputedMethod: Operation {
     var numArguments: Int {
         return numInputs - 2
@@ -455,6 +472,20 @@ class CallComputedMethod: Operation {
     }
 }
 
+class CallComputedMethodWithSpread: Operation {
+    let spreads: [Bool]
+    var numArguments: Int {
+        return numInputs - 2
+    }
+
+    init(numArguments: Int, spreads: [Bool]) {
+        assert(spreads.count == numArguments)
+        self.spreads = spreads
+        // reference object is the first input and method name is the second input
+        super.init(numInputs: numArguments + 2, numOutputs: 1, attributes: [.isVarargs, .isCall])
+    }
+}
+
 class CallFunction: Operation {
     var numArguments: Int {
         return numInputs - 1
@@ -492,6 +523,21 @@ class CallFunctionWithSpread: Operation {
     }
 }
 
+class ConstructWithSpread: Operation {
+    let spreads: [Bool]
+
+    var numArguments: Int {
+        return numInputs - 1
+    }
+    
+    init(numArguments: Int, spreads: [Bool]) {
+        assert(spreads.count == numArguments)
+        self.spreads = spreads
+        // constructor is the first input
+        super.init(numInputs: numArguments + 1, numOutputs: 1, attributes: [.isCall, .isVarargs])
+    }
+}
+
 public enum UnaryOperator: String, CaseIterable {
     case PreInc     = "++"
     case PreDec     = "--"
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index 5a9a5098b..122502f71 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -155,10 +155,32 @@ public class FuzzILLifter: Lifter {
             let arguments = instr.inputs.dropFirst().map({ $0.identifier })
             w.emit("\(instr.output) <- CallMethod \(input(0)), '\(op.methodName)', [\(arguments.joined(separator: ", "))]")
 
+        case let op as CallMethodWithSpread:
+            var arguments = [String]()
+            for (i, v) in instr.inputs.dropFirst().enumerated() {
+                if op.spreads[i] {
+                    arguments.append("...\(v.identifier)")
+                } else {
+                    arguments.append(v.identifier)
+                }
+            }
+            w.emit("\(instr.output) <- CallMethodWithSpread \(input(0)), '\(op.methodName)', [\(arguments.joined(separator: ", "))]")
+
         case is CallComputedMethod:
             let arguments = instr.inputs.dropFirst(2).map({ $0.identifier })
             w.emit("\(instr.output) <- CallComputedMethod \(input(0)), \(input(1)), [\(arguments.joined(separator: ", "))]")
 
+        case let op as CallComputedMethodWithSpread:
+            var arguments = [String]()
+            for (i, v) in instr.inputs.dropFirst(2).enumerated() {
+                if op.spreads[i] {
+                    arguments.append("...\(v.identifier)")
+                } else {
+                    arguments.append(v.identifier)
+                }
+            }
+            w.emit("\(instr.output) <- CallComputedMethodWithSpread \(input(0)), \(input(1)), [\(arguments.joined(separator: ", "))]")
+
         case is Construct:
             let arguments = instr.inputs.dropFirst().map({ $0.identifier })
             w.emit("\(instr.output) <- Construct \(input(0)), [\(arguments.joined(separator: ", "))]")
@@ -174,6 +196,17 @@ public class FuzzILLifter: Lifter {
             }
             w.emit("\(instr.output) <- CallFunctionWithSpread \(input(0)), [\(arguments.joined(separator: ", "))]")
 
+        case let op as ConstructWithSpread:
+            var arguments = [String]()
+            for (i, v) in instr.inputs.dropFirst().enumerated() {
+                if op.spreads[i] {
+                    arguments.append("...\(v.identifier)")
+                } else {
+                    arguments.append(v.identifier)
+                }
+            }
+            w.emit("\(instr.output) <- ConstructWithSpread \(input(0)), [\(arguments.joined(separator: ", "))]")
+
         case let op as UnaryOperation:
             if op.op.isPostfix {
                 w.emit("\(instr.output) <- UnaryOperation \(input(0)), '\(op.op.token)'")
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index 541d72e88..d4c833208 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -340,11 +340,35 @@ public class JavaScriptLifter: Lifter {
                 let method = MemberExpression.new() <> input(0) <> "." <> op.methodName
                 output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
 
+            case let op as CallMethodWithSpread:
+                var arguments = [String]()
+                for (i, v) in instr.inputs.dropFirst().enumerated() {
+                    if op.spreads[i] {
+                        arguments.append("..." + expr(for: v).text)
+                    } else {
+                        arguments.append(expr(for: v).text)
+                    }
+                }
+                let method = MemberExpression.new() <> input(0) <> "." <> op.methodName
+                output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
+
             case is CallComputedMethod:
                 let arguments = instr.inputs.dropFirst(2).map({ expr(for: $0).text })
                 let method = MemberExpression.new() <> input(0) <> "[" <> input(1) <> "]"
                 output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
 
+            case let op as CallComputedMethodWithSpread:
+                var arguments = [String]()
+                for (i, v) in instr.inputs.dropFirst(2).enumerated() {
+                    if op.spreads[i] {
+                        arguments.append("..." + expr(for: v).text)
+                    } else {
+                        arguments.append(expr(for: v).text)
+                    }
+                }
+                let method = MemberExpression.new() <> input(0) <> "[" <> input(1) <> "]"
+                output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
+
             case is Construct:
                 let arguments = instr.inputs.dropFirst().map({ expr(for: $0).text })
                 output = NewExpression.new() <> "new " <> input(0) <> "(" <> arguments.joined(separator: ",") <> ")"
@@ -360,6 +384,17 @@ public class JavaScriptLifter: Lifter {
                 }
                 output = CallExpression.new() <> input(0) <> "(" <> arguments.joined(separator: ",") <> ")"
 
+            case let op as ConstructWithSpread:
+                var arguments = [String]()
+                for (i, v) in instr.inputs.dropFirst().enumerated() {
+                    if op.spreads[i] {
+                        arguments.append("..." + expr(for: v).text)
+                    } else {
+                        arguments.append(expr(for: v).text)
+                    }
+                }
+                output = NewExpression.new() <> "new " <> input(0) <> "(" <> arguments.joined(separator: ",") <> ")"
+
             case let op as UnaryOperation:
                 if op.op.isPostfix {
                     output = UnaryExpression.new() <> input(0) <> op.op.token
diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift
index bef10d009..a6f52c4ca 100644
--- a/Sources/Fuzzilli/Mutators/OperationMutator.swift
+++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift
@@ -79,6 +79,20 @@ public class OperationMutator: BaseInstructionMutator {
             newOp = DeleteElement(index: b.genIndex())
         case let op as CallMethod:
             newOp = CallMethod(methodName: b.genMethodName(), numArguments: op.numArguments)
+        case let op as CallMethodWithSpread:
+            var spreads = op.spreads
+            if spreads.count > 0 {
+                let idx = Int.random(in: 0..<spreads.count)
+                spreads[idx] = !spreads[idx]
+            }
+            newOp = CallMethodWithSpread(methodName: b.genMethodName(), numArguments: op.numArguments, spreads: spreads)
+        case let op as CallComputedMethodWithSpread:
+            var spreads = op.spreads
+            if spreads.count > 0 {
+                let idx = Int.random(in: 0..<spreads.count)
+                spreads[idx] = !spreads[idx]
+            }
+            newOp = CallComputedMethodWithSpread(numArguments: op.numArguments, spreads: spreads)
         case let op as CallFunctionWithSpread:
             var spreads = op.spreads
             if spreads.count > 0 {
@@ -86,6 +100,13 @@ public class OperationMutator: BaseInstructionMutator {
                 spreads[idx] = !spreads[idx]
             }
             newOp = CallFunctionWithSpread(numArguments: op.numArguments, spreads: spreads)
+        case let op as ConstructWithSpread:
+            var spreads = op.spreads
+            if spreads.count > 0 {
+                let idx = Int.random(in: 0..<spreads.count)
+                spreads[idx] = !spreads[idx]
+            }
+            newOp = ConstructWithSpread(numArguments: op.numArguments, spreads: spreads)
         case is UnaryOperation:
             newOp = UnaryOperation(chooseUniform(from: allUnaryOperators))
         case is BinaryOperation:
diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift
index 869948e3e..b31fcb09e 100644
--- a/Sources/Fuzzilli/Protobuf/operations.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift
@@ -796,6 +796,20 @@ public struct Fuzzilli_Protobuf_CallMethod {
   public init() {}
 }
 
+public struct Fuzzilli_Protobuf_CallMethodWithSpread {
+  // 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 methodName: String = String()
+
+  public var spreads: [Bool] = []
+
+  public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  public init() {}
+}
+
 public struct Fuzzilli_Protobuf_CallComputedMethod {
   // SwiftProtobuf.Message conformance is added in an extension below. See the
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@@ -806,6 +820,18 @@ public struct Fuzzilli_Protobuf_CallComputedMethod {
   public init() {}
 }
 
+public struct Fuzzilli_Protobuf_CallComputedMethodWithSpread {
+  // 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 spreads: [Bool] = []
+
+  public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  public init() {}
+}
+
 public struct Fuzzilli_Protobuf_CallFunction {
   // SwiftProtobuf.Message conformance is added in an extension below. See the
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@@ -838,6 +864,18 @@ public struct Fuzzilli_Protobuf_CallFunctionWithSpread {
   public init() {}
 }
 
+public struct Fuzzilli_Protobuf_ConstructWithSpread {
+  // 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 spreads: [Bool] = []
+
+  public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  public init() {}
+}
+
 public struct Fuzzilli_Protobuf_UnaryOperation {
   // SwiftProtobuf.Message conformance is added in an extension below. See the
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@@ -2570,6 +2608,44 @@ extension Fuzzilli_Protobuf_CallMethod: SwiftProtobuf.Message, SwiftProtobuf._Me
   }
 }
 
+extension Fuzzilli_Protobuf_CallMethodWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  public static let protoMessageName: String = _protobuf_package + ".CallMethodWithSpread"
+  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "methodName"),
+    2: .same(proto: "spreads"),
+  ]
+
+  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeSingularStringField(value: &self.methodName) }()
+      case 2: try { try decoder.decodeRepeatedBoolField(value: &self.spreads) }()
+      default: break
+      }
+    }
+  }
+
+  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if !self.methodName.isEmpty {
+      try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1)
+    }
+    if !self.spreads.isEmpty {
+      try visitor.visitPackedBoolField(value: self.spreads, fieldNumber: 2)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  public static func ==(lhs: Fuzzilli_Protobuf_CallMethodWithSpread, rhs: Fuzzilli_Protobuf_CallMethodWithSpread) -> Bool {
+    if lhs.methodName != rhs.methodName {return false}
+    if lhs.spreads != rhs.spreads {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
 extension Fuzzilli_Protobuf_CallComputedMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".CallComputedMethod"
   public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
@@ -2589,6 +2665,38 @@ extension Fuzzilli_Protobuf_CallComputedMethod: SwiftProtobuf.Message, SwiftProt
   }
 }
 
+extension Fuzzilli_Protobuf_CallComputedMethodWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  public static let protoMessageName: String = _protobuf_package + ".CallComputedMethodWithSpread"
+  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "spreads"),
+  ]
+
+  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeRepeatedBoolField(value: &self.spreads) }()
+      default: break
+      }
+    }
+  }
+
+  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if !self.spreads.isEmpty {
+      try visitor.visitPackedBoolField(value: self.spreads, fieldNumber: 1)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  public static func ==(lhs: Fuzzilli_Protobuf_CallComputedMethodWithSpread, rhs: Fuzzilli_Protobuf_CallComputedMethodWithSpread) -> Bool {
+    if lhs.spreads != rhs.spreads {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
 extension Fuzzilli_Protobuf_CallFunction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".CallFunction"
   public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
@@ -2659,6 +2767,38 @@ extension Fuzzilli_Protobuf_CallFunctionWithSpread: SwiftProtobuf.Message, Swift
   }
 }
 
+extension Fuzzilli_Protobuf_ConstructWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  public static let protoMessageName: String = _protobuf_package + ".ConstructWithSpread"
+  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "spreads"),
+  ]
+
+  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeRepeatedBoolField(value: &self.spreads) }()
+      default: break
+      }
+    }
+  }
+
+  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if !self.spreads.isEmpty {
+      try visitor.visitPackedBoolField(value: self.spreads, fieldNumber: 1)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  public static func ==(lhs: Fuzzilli_Protobuf_ConstructWithSpread, rhs: Fuzzilli_Protobuf_ConstructWithSpread) -> Bool {
+    if lhs.spreads != rhs.spreads {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
 extension Fuzzilli_Protobuf_UnaryOperation: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".UnaryOperation"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto
index d7ba94628..868dab26f 100644
--- a/Sources/Fuzzilli/Protobuf/operations.proto
+++ b/Sources/Fuzzilli/Protobuf/operations.proto
@@ -177,9 +177,18 @@ message CallMethod {
     string methodName = 1;
 }
 
+message CallMethodWithSpread {
+    string methodName = 1;
+    repeated bool spreads = 2;
+}
+
 message CallComputedMethod {
 }
 
+message CallComputedMethodWithSpread {
+    repeated bool spreads = 1;
+}
+
 message CallFunction {
 }
 
@@ -190,6 +199,10 @@ message CallFunctionWithSpread {
     repeated bool spreads = 1;
 }
 
+message ConstructWithSpread {
+    repeated bool spreads = 1;
+}
+
 enum UnaryOperator {
     PRE_INC = 0;
     PRE_DEC = 1;
diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift
index 4a068555b..1c3707401 100644
--- a/Sources/Fuzzilli/Protobuf/program.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/program.pb.swift
@@ -486,6 +486,14 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .callMethod(newValue)}
   }
 
+  public var callMethodWithSpread: Fuzzilli_Protobuf_CallMethodWithSpread {
+    get {
+      if case .callMethodWithSpread(let v)? = operation {return v}
+      return Fuzzilli_Protobuf_CallMethodWithSpread()
+    }
+    set {operation = .callMethodWithSpread(newValue)}
+  }
+
   public var callComputedMethod: Fuzzilli_Protobuf_CallComputedMethod {
     get {
       if case .callComputedMethod(let v)? = operation {return v}
@@ -494,6 +502,14 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .callComputedMethod(newValue)}
   }
 
+  public var callComputedMethodWithSpread: Fuzzilli_Protobuf_CallComputedMethodWithSpread {
+    get {
+      if case .callComputedMethodWithSpread(let v)? = operation {return v}
+      return Fuzzilli_Protobuf_CallComputedMethodWithSpread()
+    }
+    set {operation = .callComputedMethodWithSpread(newValue)}
+  }
+
   public var callFunction: Fuzzilli_Protobuf_CallFunction {
     get {
       if case .callFunction(let v)? = operation {return v}
@@ -518,6 +534,14 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .callFunctionWithSpread(newValue)}
   }
 
+  public var constructWithSpread: Fuzzilli_Protobuf_ConstructWithSpread {
+    get {
+      if case .constructWithSpread(let v)? = operation {return v}
+      return Fuzzilli_Protobuf_ConstructWithSpread()
+    }
+    set {operation = .constructWithSpread(newValue)}
+  }
+
   public var unaryOperation: Fuzzilli_Protobuf_UnaryOperation {
     get {
       if case .unaryOperation(let v)? = operation {return v}
@@ -942,10 +966,13 @@ public struct Fuzzilli_Protobuf_Instruction {
     case yieldEach(Fuzzilli_Protobuf_YieldEach)
     case await(Fuzzilli_Protobuf_Await)
     case callMethod(Fuzzilli_Protobuf_CallMethod)
+    case callMethodWithSpread(Fuzzilli_Protobuf_CallMethodWithSpread)
     case callComputedMethod(Fuzzilli_Protobuf_CallComputedMethod)
+    case callComputedMethodWithSpread(Fuzzilli_Protobuf_CallComputedMethodWithSpread)
     case callFunction(Fuzzilli_Protobuf_CallFunction)
     case construct(Fuzzilli_Protobuf_Construct)
     case callFunctionWithSpread(Fuzzilli_Protobuf_CallFunctionWithSpread)
+    case constructWithSpread(Fuzzilli_Protobuf_ConstructWithSpread)
     case unaryOperation(Fuzzilli_Protobuf_UnaryOperation)
     case binaryOperation(Fuzzilli_Protobuf_BinaryOperation)
     case binaryOperationAndReassign(Fuzzilli_Protobuf_BinaryOperationAndReassign)
@@ -1176,10 +1203,18 @@ public struct Fuzzilli_Protobuf_Instruction {
         guard case .callMethod(let l) = lhs, case .callMethod(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
+      case (.callMethodWithSpread, .callMethodWithSpread): return {
+        guard case .callMethodWithSpread(let l) = lhs, case .callMethodWithSpread(let r) = rhs else { preconditionFailure() }
+        return l == r
+      }()
       case (.callComputedMethod, .callComputedMethod): return {
         guard case .callComputedMethod(let l) = lhs, case .callComputedMethod(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
+      case (.callComputedMethodWithSpread, .callComputedMethodWithSpread): return {
+        guard case .callComputedMethodWithSpread(let l) = lhs, case .callComputedMethodWithSpread(let r) = rhs else { preconditionFailure() }
+        return l == r
+      }()
       case (.callFunction, .callFunction): return {
         guard case .callFunction(let l) = lhs, case .callFunction(let r) = rhs else { preconditionFailure() }
         return l == r
@@ -1192,6 +1227,10 @@ public struct Fuzzilli_Protobuf_Instruction {
         guard case .callFunctionWithSpread(let l) = lhs, case .callFunctionWithSpread(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
+      case (.constructWithSpread, .constructWithSpread): return {
+        guard case .constructWithSpread(let l) = lhs, case .constructWithSpread(let r) = rhs else { preconditionFailure() }
+        return l == r
+      }()
       case (.unaryOperation, .unaryOperation): return {
         guard case .unaryOperation(let l) = lhs, case .unaryOperation(let r) = rhs else { preconditionFailure() }
         return l == r
@@ -1538,10 +1577,13 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     74: .same(proto: "yieldEach"),
     75: .same(proto: "await"),
     31: .same(proto: "callMethod"),
+    103: .same(proto: "callMethodWithSpread"),
     96: .same(proto: "callComputedMethod"),
+    104: .same(proto: "callComputedMethodWithSpread"),
     32: .same(proto: "callFunction"),
     33: .same(proto: "construct"),
     34: .same(proto: "callFunctionWithSpread"),
+    105: .same(proto: "constructWithSpread"),
     35: .same(proto: "unaryOperation"),
     36: .same(proto: "binaryOperation"),
     97: .same(proto: "binaryOperationAndReassign"),
@@ -2828,6 +2870,33 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .createTemplateString(v)
         }
       }()
+      case 103: try {
+        var v: Fuzzilli_Protobuf_CallMethodWithSpread?
+        if let current = self.operation {
+          try decoder.handleConflictingOneOf()
+          if case .callMethodWithSpread(let m) = current {v = m}
+        }
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {self.operation = .callMethodWithSpread(v)}
+      }()
+      case 104: try {
+        var v: Fuzzilli_Protobuf_CallComputedMethodWithSpread?
+        if let current = self.operation {
+          try decoder.handleConflictingOneOf()
+          if case .callComputedMethodWithSpread(let m) = current {v = m}
+        }
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {self.operation = .callComputedMethodWithSpread(v)}
+      }()
+      case 105: try {
+        var v: Fuzzilli_Protobuf_ConstructWithSpread?
+        if let current = self.operation {
+          try decoder.handleConflictingOneOf()
+          if case .constructWithSpread(let m) = current {v = m}
+        }
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {self.operation = .constructWithSpread(v)}
+      }()
       default: break
       }
     }
@@ -3221,6 +3290,18 @@ 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 .callMethodWithSpread?: try {
+      guard case .callMethodWithSpread(let v)? = self.operation else { preconditionFailure() }
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 103)
+    }()
+    case .callComputedMethodWithSpread?: try {
+      guard case .callComputedMethodWithSpread(let v)? = self.operation else { preconditionFailure() }
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 104)
+    }()
+    case .constructWithSpread?: try {
+      guard case .constructWithSpread(let v)? = self.operation else { preconditionFailure() }
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 105)
+    }()
     case nil: break
     }
     try unknownFields.traverse(visitor: &visitor)
diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto
index 661e45664..8dd215d1f 100644
--- a/Sources/Fuzzilli/Protobuf/program.proto
+++ b/Sources/Fuzzilli/Protobuf/program.proto
@@ -71,10 +71,13 @@ message Instruction {
         YieldEach yieldEach = 74;
         Await await = 75;
         CallMethod callMethod = 31;
+        CallMethodWithSpread callMethodWithSpread = 103;
         CallComputedMethod callComputedMethod = 96;
+        CallComputedMethodWithSpread callComputedMethodWithSpread = 104;
         CallFunction callFunction = 32;
         Construct construct = 33;
         CallFunctionWithSpread callFunctionWithSpread = 34;
+        ConstructWithSpread constructWithSpread = 105;
         UnaryOperation unaryOperation = 35;
         BinaryOperation binaryOperation = 36;
         BinaryOperationAndReassign binaryOperationAndReassign = 97;
diff --git a/Sources/FuzzilliCli/CodeGeneratorWeights.swift b/Sources/FuzzilliCli/CodeGeneratorWeights.swift
index baf1bd8c0..738be80ba 100644
--- a/Sources/FuzzilliCli/CodeGeneratorWeights.swift
+++ b/Sources/FuzzilliCli/CodeGeneratorWeights.swift
@@ -17,88 +17,91 @@ import Fuzzilli
 /// Assigned weights for the builtin code generators.
 let codeGeneratorWeights = [
     // Base generators
-    "IntegerGenerator":                     2,
-    "RegExpGenerator":                      1,
-    "BigIntGenerator":                      2,
-    "FloatGenerator":                       1,
-    "StringGenerator":                      1,
-    "BooleanGenerator":                     1,
-    "UndefinedGenerator":                   1,
-    "NullGenerator":                        1,
-    "BuiltinGenerator":                     5,
-    "ObjectGenerator":                      10,
-    "ArrayGenerator":                       10,
-    "ObjectWithSpreadGenerator":            5,
-    "ArrayWithSpreadGenerator":             5,
-    "TemplateStringGenerator":              1,
-    "PlainFunctionGenerator":               15,
-    "ArrowFunctionGenerator":               3,
-    "GeneratorFunctionGenerator":           3,
-    "AsyncFunctionGenerator":               3,
-    "AsyncArrowFunctionGenerator":          1,
-    "AsyncGeneratorFunctionGenerator":      1,
-    "FunctionReturnGenerator":              3,
-    "YieldGenerator":                       2,
-    "AwaitGenerator":                       2,
-    "PropertyRetrievalGenerator":           20,
-    "PropertyAssignmentGenerator":          40,
-    "PropertyRemovalGenerator":             5,
-    "ElementRetrievalGenerator":            20,
-    "ElementAssignmentGenerator":           20,
-    "ElementRemovalGenerator":              5,
-    "TypeTestGenerator":                    5,
-    "InstanceOfGenerator":                  5,
-    "InGenerator":                          3,
-    "ComputedPropertyRetrievalGenerator":   20,
-    "ComputedPropertyAssignmentGenerator":  20,
-    "ComputedPropertyRemovalGenerator":     5,
-    "FunctionCallGenerator":                30,
-    "FunctionCallWithSpreadGenerator":      10,
-    "MethodCallGenerator":                  40,
-    "ComputedMethodCallGenerator":          10,
-    "ConstructorCallGenerator":             25,
-    "UnaryOperationGenerator":              25,
-    "BinaryOperationGenerator":             40,
-    "BinaryOperationAndReassignGenerator":  25,
-    "DupGenerator":                         2,
-    "ReassignmentGenerator":                25,
-    "WithStatementGenerator":               5,
-    "LoadFromScopeGenerator":               4,
-    "StoreToScopeGenerator":                4,
-    "ComparisonGenerator":                  10,
-    "ConditionalOperationGenerator":        5,
-    "ClassGenerator":                       20,
-    "SuperMethodCallGenerator":             20,
-    "SuperPropertyOperationGenerator":      10,
-    "IfElseGenerator":                      25,
-    "SwitchCaseGenerator":                  5,
-    "WhileLoopGenerator":                   20,
-    "DoWhileLoopGenerator":                 20,
-    "ForLoopGenerator":                     20,
-    "ForInLoopGenerator":                   10,
-    "ForOfLoopGenerator":                   10,
-    "BreakGenerator":                       5,
-    "ContinueGenerator":                    5,
-    "TryCatchGenerator":                    5,
-    "ThrowGenerator":                       1,
-    "BlockStatementGenerator":              1,
-    
+    "IntegerGenerator":                      2,
+    "RegExpGenerator":                       1,
+    "BigIntGenerator":                       2,
+    "FloatGenerator":                        1,
+    "StringGenerator":                       1,
+    "BooleanGenerator":                      1,
+    "UndefinedGenerator":                    1,
+    "NullGenerator":                         1,
+    "BuiltinGenerator":                      5,
+    "ObjectGenerator":                       10,
+    "ArrayGenerator":                        10,
+    "ObjectWithSpreadGenerator":             5,
+    "ArrayWithSpreadGenerator":              5,
+    "TemplateStringGenerator":               1,
+    "PlainFunctionGenerator":                15,
+    "ArrowFunctionGenerator":                3,
+    "GeneratorFunctionGenerator":            3,
+    "AsyncFunctionGenerator":                3,
+    "AsyncArrowFunctionGenerator":           1,
+    "AsyncGeneratorFunctionGenerator":       1,
+    "FunctionReturnGenerator":               3,
+    "YieldGenerator":                        2,
+    "AwaitGenerator":                        2,
+    "PropertyRetrievalGenerator":            20,
+    "PropertyAssignmentGenerator":           40,
+    "PropertyRemovalGenerator":              5,
+    "ElementRetrievalGenerator":             20,
+    "ElementAssignmentGenerator":            20,
+    "ElementRemovalGenerator":               5,
+    "TypeTestGenerator":                     5,
+    "InstanceOfGenerator":                   5,
+    "InGenerator":                           3,
+    "ComputedPropertyRetrievalGenerator":    20,
+    "ComputedPropertyAssignmentGenerator":   20,
+    "ComputedPropertyRemovalGenerator":      5,
+    "FunctionCallGenerator":                 30,
+    "FunctionCallWithSpreadGenerator":       10,
+    "MethodCallGenerator":                   40,
+    "MethodCallWithSpreadGenerator":         10,
+    "ComputedMethodCallGenerator":           10,
+    "ComputedMethodCallWithSpreadGenerator": 4,
+    "ConstructorCallGenerator":              25,
+    "ConstructorCallWithSpreadGenerator":    10,
+    "UnaryOperationGenerator":               25,
+    "BinaryOperationGenerator":              40,
+    "BinaryOperationAndReassignGenerator":   25,
+    "DupGenerator":                          2,
+    "ReassignmentGenerator":                 25,
+    "WithStatementGenerator":                5,
+    "LoadFromScopeGenerator":                4,
+    "StoreToScopeGenerator":                 4,
+    "ComparisonGenerator":                   10,
+    "ConditionalOperationGenerator":         5,
+    "ClassGenerator":                        20,
+    "SuperMethodCallGenerator":              20,
+    "SuperPropertyOperationGenerator":       10,
+    "IfElseGenerator":                       25,
+    "SwitchCaseGenerator":                   5,
+    "WhileLoopGenerator":                    20,
+    "DoWhileLoopGenerator":                  20,
+    "ForLoopGenerator":                      20,
+    "ForInLoopGenerator":                    10,
+    "ForOfLoopGenerator":                    10,
+    "BreakGenerator":                        5,
+    "ContinueGenerator":                     5,
+    "TryCatchGenerator":                     5,
+    "ThrowGenerator":                        1,
+    "BlockStatementGenerator":               1,
+
     // Special generators
-    "WellKnownPropertyLoadGenerator":       5,
-    "WellKnownPropertyStoreGenerator":      5,
-    "TypedArrayGenerator":                  10,
-    "FloatArrayGenerator":                  5,
-    "IntArrayGenerator":                    5,
-    "ObjectArrayGenerator":                 5,
-    "PrototypeAccessGenerator":             15,
-    "PrototypeOverwriteGenerator":          15,
-    "CallbackPropertyGenerator":            15,
-    "PropertyAccessorGenerator":            15,
-    "MethodCallWithDifferentThisGenerator": 5,
-    "ProxyGenerator":                       15,
-    "LengthChangeGenerator":                5,
-    "ElementKindChangeGenerator":           5,
-    "PromiseGenerator":                     3,
-    "EvalGenerator":                        3,
-    "MathOperationGenerator":               10,
+    "WellKnownPropertyLoadGenerator":        5,
+    "WellKnownPropertyStoreGenerator":       5,
+    "TypedArrayGenerator":                   10,
+    "FloatArrayGenerator":                   5,
+    "IntArrayGenerator":                     5,
+    "ObjectArrayGenerator":                  5,
+    "PrototypeAccessGenerator":              15,
+    "PrototypeOverwriteGenerator":           15,
+    "CallbackPropertyGenerator":             15,
+    "PropertyAccessorGenerator":             15,
+    "MethodCallWithDifferentThisGenerator":  5,
+    "ProxyGenerator":                        15,
+    "LengthChangeGenerator":                 5,
+    "ElementKindChangeGenerator":            5,
+    "PromiseGenerator":                      3,
+    "EvalGenerator":                         3,
+    "MathOperationGenerator":                10,
 ]
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index 5d431aa5e..9ff365f55 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -733,8 +733,8 @@ class LifterTests: XCTestCase {
         let _ = b.deleteElement(3, of: v10)
 
         let program = b.finalize()
-
         let lifted_program = fuzzer.lifter.lift(program)
+
         let expected_program = """
         const v3 = {"bar":13.37,"foo":1337};
         const v4 = delete v3.foo;
@@ -813,7 +813,6 @@ class LifterTests: XCTestCase {
         let v1 = b.loadRegExp("b", RegExpFlags())
         b.compare(v0,v1, with: .equal);
 
-
         let program = b.finalize()
 
         let lifted_program = fuzzer.lifter.lift(program)
@@ -822,6 +821,98 @@ class LifterTests: XCTestCase {
         const v1 = v0 == v0;
         const v2 = /b/;
         const v3 = v0 == v2;
+
+        """
+
+        XCTAssertEqual(lifted_program,expected_program)
+    }
+
+    
+    func testCallMethodWithSpreadLifting(){
+        let fuzzer = makeMockFuzzer()
+        let b = fuzzer.makeBuilder()
+
+        var initialValues = [Variable]()
+        initialValues.append(b.loadInt(1))
+        initialValues.append(b.loadInt(3))
+        initialValues.append(b.loadInt(9))
+        initialValues.append(b.loadInt(10))
+        initialValues.append(b.loadInt(2))
+        initialValues.append(b.loadInt(6))
+        let v6 = b.createArray(with: initialValues)
+
+        let v7 = b.loadInt(0)
+        let v8 = b.loadInt(4)
+
+        let v9 = b.loadBuiltin("Math")
+        let _ = b.callMethod("max", on: v9, withArgs: [v7,v6,v8], spreading: [false, true, false])
+
+        let program = b.finalize()
+
+        let lifted_program = fuzzer.lifter.lift(program)
+        let expected_program = """
+        const v6 = [1,3,9,10,2,6];
+        const v10 = Math.max(0,...v6,4);
+
+        """
+
+        XCTAssertEqual(lifted_program,expected_program)
+    }
+
+    func testCallComputedMethodWithSpreadLifting(){
+        let fuzzer = makeMockFuzzer()
+        let b = fuzzer.makeBuilder()
+
+        let v0 = b.definePlainFunction(withSignature: FunctionSignature(withParameterCount: 2)) { args in
+            let v3 = b.binary(args[0], args[1], with: .Add)
+            b.doReturn(value: v3)
+        }
+        let v4 = b.createObject(with: ["add" : v0])
+        let v5 = b.loadString("add")
+        var initialValues = [Variable]()
+        initialValues.append(b.loadInt(15))
+        initialValues.append(b.loadInt(30))
+        let v8 = b.createArray(with: initialValues)
+        let _ = b.callComputedMethod(v5, on: v4, withArgs: [v8], spreading: [true])
+
+        let program = b.finalize()
+
+        let lifted_program = fuzzer.lifter.lift(program)
+        let expected_program = """
+        function v0(v1,v2) {
+            const v3 = v1 + v2;
+            return v3;
+        }
+        const v4 = {"add":v0};
+        const v8 = [15,30];
+        const v9 = v4["add"](...v8);
+
+        """
+
+        XCTAssertEqual(lifted_program,expected_program)
+    }
+
+    func testConstructWithSpreadLifting(){
+        let fuzzer = makeMockFuzzer()
+        let b = fuzzer.makeBuilder()
+
+        var initialValues = [Variable]()
+        initialValues.append(b.loadInt(15))
+        initialValues.append(b.loadInt(30))
+        initialValues.append(b.loadString("Hello"))
+        initialValues.append(b.loadString("World"))
+        let v4 = b.createArray(with: initialValues)
+        let v5 = b.loadFloat(13.37)
+
+        let v6 = b.loadBuiltin("Array")
+        let _ = b.construct(v6, withArgs: [v4,v5], spreading: [true,false])
+
+        let program = b.finalize()
+
+        let lifted_program = fuzzer.lifter.lift(program)
+        let expected_program = """
+        const v4 = [15,30,"Hello","World"];
+        const v7 = new Array(...v4,13.37);
         
         """
 
@@ -853,6 +944,9 @@ extension LifterTests {
             ("testDeleteOpsLifting", testDeleteOpsLifting),
             ("testRegExpInline",testRegExpInline),
             ("testStrictFunctionLifting", testStrictFunctionLifting),
+            ("testCallMethodWithSpreadLifting", testCallMethodWithSpreadLifting),
+            ("testCallComputedMethodWithSpreadLifting", testCallComputedMethodWithSpreadLifting),
+            ("testConstructWithSpreadLifting", testConstructWithSpreadLifting),
         ]
     }
 }

From e50cef1332232c8299c05bd9cfb796ab20b476d4 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Sat, 31 Jul 2021 09:42:57 +0100
Subject: [PATCH 2/5] Add missing spread ops

---
 Sources/Fuzzilli/FuzzIL/Semantics.swift | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Sources/Fuzzilli/FuzzIL/Semantics.swift b/Sources/Fuzzilli/FuzzIL/Semantics.swift
index 6a632cf02..a74029bcd 100644
--- a/Sources/Fuzzilli/FuzzIL/Semantics.swift
+++ b/Sources/Fuzzilli/FuzzIL/Semantics.swift
@@ -26,7 +26,10 @@ extension Operation {
         switch self {
         case is CallFunction,
              is CallMethod,
-             is CallComputedMethod:
+             is CallComputedMethod,
+             is CallFunctionWithSpread,
+             is CallMethodWithSpread,
+             is CallComputedMethodWithSpread:
             // We assume that a constructor doesn't modify its arguments when called
             return true
         case is StoreProperty,

From 506d49a22ce73a3c73447789522f29faa58c7eba Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Fri, 3 Sep 2021 18:12:11 +0100
Subject: [PATCH 3/5] remove Call*WithSpread operations

---
 Sources/Fuzzilli/Core/ProgramBuilder.swift    |  16 +-
 .../Fuzzilli/FuzzIL/AbstractInterpreter.swift |  12 +-
 Sources/Fuzzilli/FuzzIL/Instruction.swift     |  45 ++---
 Sources/Fuzzilli/FuzzIL/Operations.swift      |  48 +-----
 Sources/Fuzzilli/FuzzIL/Semantics.swift       |   7 +-
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   |  26 +--
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   |  20 +--
 .../Minimization/CallArgumentReducer.swift    |   8 +-
 .../Minimization/ReplaceReducer.swift         |   2 +-
 .../Fuzzilli/Mutators/OperationMutator.swift  |  16 +-
 Sources/Fuzzilli/Protobuf/operations.pb.swift | 145 +---------------
 Sources/Fuzzilli/Protobuf/operations.proto    |  15 +-
 Sources/Fuzzilli/Protobuf/program.pb.swift    | 160 +++---------------
 Sources/Fuzzilli/Protobuf/program.proto       |  18 +-
 14 files changed, 89 insertions(+), 449 deletions(-)

diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index 166e1c6f0..8ed8f3724 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -1164,42 +1164,42 @@ public class ProgramBuilder {
 
     @discardableResult
     public func callMethod(_ name: String, on object: Variable, withArgs arguments: [Variable]) -> Variable {
-        return perform(CallMethod(methodName: name, numArguments: arguments.count), withInputs: [object] + arguments).output
+        return perform(CallMethod(methodName: name, numArguments: arguments.count, spreads: [Bool](repeating: false, count: arguments.count)), withInputs: [object] + arguments).output
     }
 
     @discardableResult
     public func callMethod(_ name: String, on object: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
-        return perform(CallMethodWithSpread(methodName: name, numArguments: arguments.count, spreads: spreads), withInputs: [object] + arguments).output
+        return perform(CallMethod(methodName: name, numArguments: arguments.count, spreads: spreads), withInputs: [object] + arguments).output
     }
 
     @discardableResult
     public func callComputedMethod(_ name: Variable, on object: Variable, withArgs arguments: [Variable]) -> Variable {
-        return perform(CallComputedMethod(numArguments: arguments.count), withInputs: [object, name] + arguments).output
+        return perform(CallComputedMethod(numArguments: arguments.count, spreads: [Bool](repeating: false, count: arguments.count)), withInputs: [object, name] + arguments).output
     }
 
     @discardableResult
     public func callComputedMethod(_ name: Variable, on object: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
-        return perform(CallComputedMethodWithSpread(numArguments: arguments.count, spreads: spreads), withInputs: [object, name] + arguments).output
+        return perform(CallComputedMethod(numArguments: arguments.count, spreads: spreads), withInputs: [object, name] + arguments).output
     }
 
     @discardableResult
     public func callFunction(_ function: Variable, withArgs arguments: [Variable]) -> Variable {
-        return perform(CallFunction(numArguments: arguments.count), withInputs: [function] + arguments).output
+        return perform(CallFunction(numArguments: arguments.count, spreads: [Bool](repeating: false, count: arguments.count)), withInputs: [function] + arguments).output
     }
 
     @discardableResult
     public func construct(_ constructor: Variable, withArgs arguments: [Variable]) -> Variable {
-        return perform(Construct(numArguments: arguments.count), withInputs: [constructor] + arguments).output
+        return perform(Construct(numArguments: arguments.count, spreads: [Bool](repeating: false, count: arguments.count)), withInputs: [constructor] + arguments).output
     }
 
     @discardableResult
     public func callFunction(_ function: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
-        return perform(CallFunctionWithSpread(numArguments: arguments.count, spreads: spreads), withInputs: [function] + arguments).output
+        return perform(CallFunction(numArguments: arguments.count, spreads: spreads), withInputs: [function] + arguments).output
     }
 
     @discardableResult
     public func construct(_ constructor: Variable, withArgs arguments: [Variable], spreading spreads: [Bool]) -> Variable {
-        return perform(ConstructWithSpread(numArguments: arguments.count, spreads: spreads), withInputs: [constructor] + arguments).output
+        return perform(Construct(numArguments: arguments.count, spreads: spreads), withInputs: [constructor] + arguments).output
     }
 
     @discardableResult
diff --git a/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift b/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
index 2c0a339d8..cc6e2daee 100644
--- a/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
+++ b/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
@@ -388,26 +388,20 @@ public struct AbstractInterpreter {
         // For now we treat this as .unknown
         case is LoadElement,
              is LoadComputedProperty,
-             is CallComputedMethod,
-             is CallComputedMethodWithSpread:
+             is CallComputedMethod:
             set(instr.output, .unknown)
 
         case is ConditionalOperation:
             let outputType = type(ofInput: 1) | type(ofInput: 2)
             set(instr.output, outputType)
 
-        case is CallFunction,
-             is CallFunctionWithSpread:
+        case is CallFunction:
             set(instr.output, inferCallResultType(of: instr.input(0)))
 
         case let op as CallMethod:
             set(instr.output, inferMethodSignature(of: op.methodName, on: instr.input(0)).outputType)
 
-        case let op as CallMethodWithSpread:
-            set(instr.output, inferMethodSignature(of: op.methodName, on: instr.input(0)).outputType)
-
-        case is Construct,
-             is ConstructWithSpread:
+        case is Construct:
             set(instr.output, inferConstructedType(of: instr.input(0)))
 
         case let op as UnaryOperation:
diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift
index e69e8e8e1..e49d3afc1 100644
--- a/Sources/Fuzzilli/FuzzIL/Instruction.swift
+++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift
@@ -351,24 +351,16 @@ extension Instruction: ProtobufConvertible {
             case is Await:
                 $0.await = Fuzzilli_Protobuf_Await()
             case let op as CallMethod:
-                $0.callMethod = Fuzzilli_Protobuf_CallMethod.with { $0.methodName = op.methodName }
-            case let op as CallMethodWithSpread:
-                $0.callMethodWithSpread = Fuzzilli_Protobuf_CallMethodWithSpread.with { 
+                $0.callMethod = Fuzzilli_Protobuf_CallMethod.with { 
                     $0.methodName = op.methodName
                     $0.spreads = op.spreads 
                 }
-            case is CallComputedMethod:
-                $0.callComputedMethod = Fuzzilli_Protobuf_CallComputedMethod()
-            case let op as CallComputedMethodWithSpread:
-                $0.callComputedMethodWithSpread = Fuzzilli_Protobuf_CallComputedMethodWithSpread.with { $0.spreads = op.spreads }
-            case is CallFunction:
-                $0.callFunction = Fuzzilli_Protobuf_CallFunction()
-            case is Construct:
-                $0.construct = Fuzzilli_Protobuf_Construct()
-            case let op as CallFunctionWithSpread:
-                $0.callFunctionWithSpread = Fuzzilli_Protobuf_CallFunctionWithSpread.with { $0.spreads = op.spreads }
-            case let op as ConstructWithSpread:
-                $0.constructWithSpread = Fuzzilli_Protobuf_ConstructWithSpread.with { $0.spreads = op.spreads }
+            case let op as CallComputedMethod:
+                $0.callComputedMethod = Fuzzilli_Protobuf_CallComputedMethod.with { $0.spreads = op.spreads }
+            case let op as CallFunction:
+                $0.callFunction = Fuzzilli_Protobuf_CallFunction.with { $0.spreads = op.spreads }
+            case let op as Construct:
+                $0.construct = Fuzzilli_Protobuf_Construct.with { $0.spreads = op.spreads }
             case let op as UnaryOperation:
                 $0.unaryOperation = Fuzzilli_Protobuf_UnaryOperation.with { $0.op = convertEnum(op.op, allUnaryOperators) }
             case let op as BinaryOperation:
@@ -593,23 +585,14 @@ extension Instruction: ProtobufConvertible {
         case .await(_):
             op = Await()
         case .callMethod(let p):
-            op = CallMethod(methodName: p.methodName, numArguments: inouts.count - 2)
-        case .callMethodWithSpread(let p):
-            op = CallMethodWithSpread(methodName: p.methodName, numArguments: inouts.count - 2, spreads: p.spreads)
-        case .callComputedMethod(_):
+            op = CallMethod(methodName: p.methodName, numArguments: inouts.count - 2, spreads: p.spreads)
+        case .callComputedMethod(let p):
             // We subtract 3 from the inouts count since the first two elements are the callee and method and the last element is the output variable
-            op = CallComputedMethod(numArguments: inouts.count - 3)
-        case .callComputedMethodWithSpread(let p):
-            // We subtract 3 from the inouts count since the first two elements are the callee and method and the last element is the output variable
-            op = CallComputedMethodWithSpread(numArguments: inouts.count - 3, spreads: p.spreads)
-        case .callFunction(_):
-            op = CallFunction(numArguments: inouts.count - 2)
-        case .construct(_):
-            op = Construct(numArguments: inouts.count - 2)
-        case .callFunctionWithSpread(let p):
-            op = CallFunctionWithSpread(numArguments: inouts.count - 2, spreads: p.spreads)
-        case .constructWithSpread(let p):
-            op = ConstructWithSpread(numArguments: inouts.count - 2, spreads: p.spreads)
+            op = CallComputedMethod(numArguments: inouts.count - 3, spreads: p.spreads)
+        case .callFunction(let p):
+            op = CallFunction(numArguments: inouts.count - 2, spreads: p.spreads)
+        case .construct(let p):
+            op = Construct(numArguments: inouts.count - 2, spreads: p.spreads)
         case .unaryOperation(let p):
             op = UnaryOperation(try convertEnum(p.op, allUnaryOperators))
         case .binaryOperation(let p):
diff --git a/Sources/Fuzzilli/FuzzIL/Operations.swift b/Sources/Fuzzilli/FuzzIL/Operations.swift
index 986d49d93..2f8fde7ec 100644
--- a/Sources/Fuzzilli/FuzzIL/Operations.swift
+++ b/Sources/Fuzzilli/FuzzIL/Operations.swift
@@ -432,19 +432,6 @@ class Await: Operation {
 }
 
 class CallMethod: Operation {
-    let methodName: String
-    var numArguments: Int {
-        return numInputs - 1
-    }
-    
-    init(methodName: String, numArguments: Int) {
-        self.methodName = methodName
-        // reference object is the first input
-        super.init(numInputs: numArguments + 1, numOutputs: 1, attributes: [.isParametric, .isVarargs, .isCall])
-    }
-}
-
-class CallMethodWithSpread: Operation {
     let methodName: String
     let spreads: [Bool]
 
@@ -462,17 +449,6 @@ class CallMethodWithSpread: Operation {
 }
 
 class CallComputedMethod: Operation {
-    var numArguments: Int {
-        return numInputs - 2
-    }
-
-    init(numArguments: Int) {
-        // reference object is the first input and method name is the second input
-        super.init(numInputs: numArguments + 2, numOutputs: 1, attributes: [.isVarargs, .isCall])
-    }
-}
-
-class CallComputedMethodWithSpread: Operation {
     let spreads: [Bool]
     var numArguments: Int {
         return numInputs - 2
@@ -487,28 +463,6 @@ class CallComputedMethodWithSpread: Operation {
 }
 
 class CallFunction: Operation {
-    var numArguments: Int {
-        return numInputs - 1
-    }
-    
-    init(numArguments: Int) {
-        // function object is the first input
-        super.init(numInputs: numArguments + 1, numOutputs: 1, attributes: [.isCall, .isVarargs])
-    }
-}
-
-class Construct: Operation {
-    var numArguments: Int {
-        return numInputs - 1
-    }
-    
-    init(numArguments: Int) {
-        // constructor is the first input
-        super.init(numInputs: numArguments + 1, numOutputs: 1, attributes: [.isCall, .isVarargs])
-    }
-}
-
-class CallFunctionWithSpread: Operation {
     // Which inputs to spread
     let spreads: [Bool]
     
@@ -523,7 +477,7 @@ class CallFunctionWithSpread: Operation {
     }
 }
 
-class ConstructWithSpread: Operation {
+class Construct: Operation {
     let spreads: [Bool]
 
     var numArguments: Int {
diff --git a/Sources/Fuzzilli/FuzzIL/Semantics.swift b/Sources/Fuzzilli/FuzzIL/Semantics.swift
index a74029bcd..d8ffa68e9 100644
--- a/Sources/Fuzzilli/FuzzIL/Semantics.swift
+++ b/Sources/Fuzzilli/FuzzIL/Semantics.swift
@@ -26,11 +26,8 @@ extension Operation {
         switch self {
         case is CallFunction,
              is CallMethod,
-             is CallComputedMethod,
-             is CallFunctionWithSpread,
-             is CallMethodWithSpread,
-             is CallComputedMethodWithSpread:
-            // We assume that a constructor doesn't modify its arguments when called
+             is CallComputedMethod:
+             // We assume that a constructor doesn't modify its arguments when called
             return true
         case is StoreProperty,
              is StoreElement,
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index 122502f71..8b56854fe 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -152,10 +152,6 @@ public class FuzzILLifter: Lifter {
             w.emit("\(instr.output) <- CallFunction \(input(0)), [\(arguments.joined(separator: ", "))]")
 
         case let op as CallMethod:
-            let arguments = instr.inputs.dropFirst().map({ $0.identifier })
-            w.emit("\(instr.output) <- CallMethod \(input(0)), '\(op.methodName)', [\(arguments.joined(separator: ", "))]")
-
-        case let op as CallMethodWithSpread:
             var arguments = [String]()
             for (i, v) in instr.inputs.dropFirst().enumerated() {
                 if op.spreads[i] {
@@ -164,13 +160,9 @@ public class FuzzILLifter: Lifter {
                     arguments.append(v.identifier)
                 }
             }
-            w.emit("\(instr.output) <- CallMethodWithSpread \(input(0)), '\(op.methodName)', [\(arguments.joined(separator: ", "))]")
-
-        case is CallComputedMethod:
-            let arguments = instr.inputs.dropFirst(2).map({ $0.identifier })
-            w.emit("\(instr.output) <- CallComputedMethod \(input(0)), \(input(1)), [\(arguments.joined(separator: ", "))]")
+            w.emit("\(instr.output) <- CallMethod \(input(0)), '\(op.methodName)', [\(arguments.joined(separator: ", "))]")
 
-        case let op as CallComputedMethodWithSpread:
+        case let op as CallComputedMethod:
             var arguments = [String]()
             for (i, v) in instr.inputs.dropFirst(2).enumerated() {
                 if op.spreads[i] {
@@ -179,13 +171,9 @@ public class FuzzILLifter: Lifter {
                     arguments.append(v.identifier)
                 }
             }
-            w.emit("\(instr.output) <- CallComputedMethodWithSpread \(input(0)), \(input(1)), [\(arguments.joined(separator: ", "))]")
-
-        case is Construct:
-            let arguments = instr.inputs.dropFirst().map({ $0.identifier })
-            w.emit("\(instr.output) <- Construct \(input(0)), [\(arguments.joined(separator: ", "))]")
+            w.emit("\(instr.output) <- CallComputedMethod \(input(0)), \(input(1)), [\(arguments.joined(separator: ", "))]")
 
-        case let op as CallFunctionWithSpread:
+        case let op as CallFunction:
             var arguments = [String]()
             for (i, v) in instr.inputs.dropFirst().enumerated() {
                 if op.spreads[i] {
@@ -194,9 +182,9 @@ public class FuzzILLifter: Lifter {
                     arguments.append(v.identifier)
                 }
             }
-            w.emit("\(instr.output) <- CallFunctionWithSpread \(input(0)), [\(arguments.joined(separator: ", "))]")
+            w.emit("\(instr.output) <- CallFunction \(input(0)), [\(arguments.joined(separator: ", "))]")
 
-        case let op as ConstructWithSpread:
+        case let op as Construct:
             var arguments = [String]()
             for (i, v) in instr.inputs.dropFirst().enumerated() {
                 if op.spreads[i] {
@@ -205,7 +193,7 @@ public class FuzzILLifter: Lifter {
                     arguments.append(v.identifier)
                 }
             }
-            w.emit("\(instr.output) <- ConstructWithSpread \(input(0)), [\(arguments.joined(separator: ", "))]")
+            w.emit("\(instr.output) <- Construct \(input(0)), [\(arguments.joined(separator: ", "))]")
 
         case let op as UnaryOperation:
             if op.op.isPostfix {
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index d4c833208..c63c3ca9e 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -336,11 +336,6 @@ public class JavaScriptLifter: Lifter {
                 output = CallExpression.new() <> input(0) <> "(" <> arguments.joined(separator: ",") <> ")"
 
             case let op as CallMethod:
-                let arguments = instr.inputs.dropFirst().map({ expr(for: $0).text })
-                let method = MemberExpression.new() <> input(0) <> "." <> op.methodName
-                output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
-
-            case let op as CallMethodWithSpread:
                 var arguments = [String]()
                 for (i, v) in instr.inputs.dropFirst().enumerated() {
                     if op.spreads[i] {
@@ -352,12 +347,7 @@ public class JavaScriptLifter: Lifter {
                 let method = MemberExpression.new() <> input(0) <> "." <> op.methodName
                 output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
 
-            case is CallComputedMethod:
-                let arguments = instr.inputs.dropFirst(2).map({ expr(for: $0).text })
-                let method = MemberExpression.new() <> input(0) <> "[" <> input(1) <> "]"
-                output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
-
-            case let op as CallComputedMethodWithSpread:
+            case let op as CallComputedMethod:
                 var arguments = [String]()
                 for (i, v) in instr.inputs.dropFirst(2).enumerated() {
                     if op.spreads[i] {
@@ -369,11 +359,7 @@ public class JavaScriptLifter: Lifter {
                 let method = MemberExpression.new() <> input(0) <> "[" <> input(1) <> "]"
                 output = CallExpression.new() <> method <> "(" <> arguments.joined(separator: ",") <> ")"
 
-            case is Construct:
-                let arguments = instr.inputs.dropFirst().map({ expr(for: $0).text })
-                output = NewExpression.new() <> "new " <> input(0) <> "(" <> arguments.joined(separator: ",") <> ")"
-
-            case let op as CallFunctionWithSpread:
+            case let op as CallFunction:
                 var arguments = [String]()
                 for (i, v) in instr.inputs.dropFirst().enumerated() {
                     if op.spreads[i] {
@@ -384,7 +370,7 @@ public class JavaScriptLifter: Lifter {
                 }
                 output = CallExpression.new() <> input(0) <> "(" <> arguments.joined(separator: ",") <> ")"
 
-            case let op as ConstructWithSpread:
+            case let op as Construct:
                 var arguments = [String]()
                 for (i, v) in instr.inputs.dropFirst().enumerated() {
                     if op.spreads[i] {
diff --git a/Sources/Fuzzilli/Minimization/CallArgumentReducer.swift b/Sources/Fuzzilli/Minimization/CallArgumentReducer.swift
index 4a3126d3b..6a33ce72f 100644
--- a/Sources/Fuzzilli/Minimization/CallArgumentReducer.swift
+++ b/Sources/Fuzzilli/Minimization/CallArgumentReducer.swift
@@ -29,7 +29,7 @@ struct CallArgumentReducer: Reducer {
                     break
                 }
                 
-                let newOp = CallFunction(numArguments: op.numArguments - 1)
+                let newOp = CallFunction(numArguments: op.numArguments - 1, spreads: op.spreads.dropLast())
                 let newInstr = Instruction(newOp, output: instr.output, inputs: Array(instr.inputs.dropLast()))
                 verifier.tryReplacing(instructionAt: instr.index, with: newInstr, in: &code)
                 
@@ -38,7 +38,7 @@ struct CallArgumentReducer: Reducer {
                     break
                 }
                 
-                let newOp = CallMethod(methodName: op.methodName, numArguments: op.numArguments - 1)
+                let newOp = CallMethod(methodName: op.methodName, numArguments: op.numArguments - 1, spreads: op.spreads.dropLast())
                 let newInstr = Instruction(newOp, output: instr.output, inputs: Array(instr.inputs.dropLast()))
                 verifier.tryReplacing(instructionAt: instr.index, with: newInstr, in: &code)
 
@@ -47,7 +47,7 @@ struct CallArgumentReducer: Reducer {
                     break
                 }
 
-                let newOp = CallComputedMethod(numArguments: op.numArguments - 1)
+                let newOp = CallComputedMethod(numArguments: op.numArguments - 1, spreads: op.spreads.dropLast())
                 let newInstr = Instruction(newOp, output: instr.output, inputs: Array(instr.inputs.dropLast()))
                 verifier.tryReplacing(instructionAt: instr.index, with: newInstr, in: &code)
                 
@@ -56,7 +56,7 @@ struct CallArgumentReducer: Reducer {
                     break
                 }
                 
-                let newOp = Construct(numArguments: op.numArguments - 1)
+                let newOp = Construct(numArguments: op.numArguments - 1, spreads: op.spreads.dropLast())
                 let newInstr = Instruction(newOp, output: instr.output, inputs: Array(instr.inputs.dropLast()))
                 verifier.tryReplacing(instructionAt: instr.index, with: newInstr, in: &code)
                 
diff --git a/Sources/Fuzzilli/Minimization/ReplaceReducer.swift b/Sources/Fuzzilli/Minimization/ReplaceReducer.swift
index 6ad3d69b5..6bb132ab1 100644
--- a/Sources/Fuzzilli/Minimization/ReplaceReducer.swift
+++ b/Sources/Fuzzilli/Minimization/ReplaceReducer.swift
@@ -19,7 +19,7 @@ struct ReplaceReducer: Reducer {
             switch instr.op {
             case let op as Construct:
                 // Try replacing with a simple call
-                let newOp = CallFunction(numArguments: op.numArguments)
+                let newOp = CallFunction(numArguments: op.numArguments, spreads: [Bool](repeating: false, count: op.numArguments))
                 verifier.tryReplacing(instructionAt: instr.index, with: Instruction(newOp, inouts: instr.inouts), in: &code)
             default:
                 break
diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift
index a6f52c4ca..a2d9e8725 100644
--- a/Sources/Fuzzilli/Mutators/OperationMutator.swift
+++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift
@@ -78,35 +78,33 @@ public class OperationMutator: BaseInstructionMutator {
         case is DeleteElement:
             newOp = DeleteElement(index: b.genIndex())
         case let op as CallMethod:
-            newOp = CallMethod(methodName: b.genMethodName(), numArguments: op.numArguments)
-        case let op as CallMethodWithSpread:
             var spreads = op.spreads
             if spreads.count > 0 {
                 let idx = Int.random(in: 0..<spreads.count)
                 spreads[idx] = !spreads[idx]
             }
-            newOp = CallMethodWithSpread(methodName: b.genMethodName(), numArguments: op.numArguments, spreads: spreads)
-        case let op as CallComputedMethodWithSpread:
+            newOp = CallMethod(methodName: b.genMethodName(), numArguments: op.numArguments, spreads: spreads)
+        case let op as CallComputedMethod:
             var spreads = op.spreads
             if spreads.count > 0 {
                 let idx = Int.random(in: 0..<spreads.count)
                 spreads[idx] = !spreads[idx]
             }
-            newOp = CallComputedMethodWithSpread(numArguments: op.numArguments, spreads: spreads)
-        case let op as CallFunctionWithSpread:
+            newOp = CallComputedMethod(numArguments: op.numArguments, spreads: spreads)
+        case let op as CallFunction:
             var spreads = op.spreads
             if spreads.count > 0 {
                 let idx = Int.random(in: 0..<spreads.count)
                 spreads[idx] = !spreads[idx]
             }
-            newOp = CallFunctionWithSpread(numArguments: op.numArguments, spreads: spreads)
-        case let op as ConstructWithSpread:
+            newOp = CallFunction(numArguments: op.numArguments, spreads: spreads)
+        case let op as Construct:
             var spreads = op.spreads
             if spreads.count > 0 {
                 let idx = Int.random(in: 0..<spreads.count)
                 spreads[idx] = !spreads[idx]
             }
-            newOp = ConstructWithSpread(numArguments: op.numArguments, spreads: spreads)
+            newOp = Construct(numArguments: op.numArguments, spreads: spreads)
         case is UnaryOperation:
             newOp = UnaryOperation(chooseUniform(from: allUnaryOperators))
         case is BinaryOperation:
diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift
index b31fcb09e..e13dc091f 100644
--- a/Sources/Fuzzilli/Protobuf/operations.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift
@@ -791,18 +791,6 @@ public struct Fuzzilli_Protobuf_CallMethod {
 
   public var methodName: String = String()
 
-  public var unknownFields = SwiftProtobuf.UnknownStorage()
-
-  public init() {}
-}
-
-public struct Fuzzilli_Protobuf_CallMethodWithSpread {
-  // 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 methodName: String = String()
-
   public var spreads: [Bool] = []
 
   public var unknownFields = SwiftProtobuf.UnknownStorage()
@@ -815,16 +803,6 @@ public struct Fuzzilli_Protobuf_CallComputedMethod {
   // `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_CallComputedMethodWithSpread {
-  // 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 spreads: [Bool] = []
 
   public var unknownFields = SwiftProtobuf.UnknownStorage()
@@ -837,26 +815,6 @@ public struct Fuzzilli_Protobuf_CallFunction {
   // `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_Construct {
-  // 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_CallFunctionWithSpread {
-  // 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 spreads: [Bool] = []
 
   public var unknownFields = SwiftProtobuf.UnknownStorage()
@@ -864,7 +822,7 @@ public struct Fuzzilli_Protobuf_CallFunctionWithSpread {
   public init() {}
 }
 
-public struct Fuzzilli_Protobuf_ConstructWithSpread {
+public struct Fuzzilli_Protobuf_Construct {
   // 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.
@@ -2578,38 +2536,6 @@ extension Fuzzilli_Protobuf_Await: SwiftProtobuf.Message, SwiftProtobuf._Message
 
 extension Fuzzilli_Protobuf_CallMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".CallMethod"
-  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
-    1: .same(proto: "methodName"),
-  ]
-
-  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeSingularStringField(value: &self.methodName) }()
-      default: break
-      }
-    }
-  }
-
-  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
-    if !self.methodName.isEmpty {
-      try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1)
-    }
-    try unknownFields.traverse(visitor: &visitor)
-  }
-
-  public static func ==(lhs: Fuzzilli_Protobuf_CallMethod, rhs: Fuzzilli_Protobuf_CallMethod) -> Bool {
-    if lhs.methodName != rhs.methodName {return false}
-    if lhs.unknownFields != rhs.unknownFields {return false}
-    return true
-  }
-}
-
-extension Fuzzilli_Protobuf_CallMethodWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
-  public static let protoMessageName: String = _protobuf_package + ".CallMethodWithSpread"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
     1: .same(proto: "methodName"),
     2: .same(proto: "spreads"),
@@ -2638,7 +2564,7 @@ extension Fuzzilli_Protobuf_CallMethodWithSpread: SwiftProtobuf.Message, SwiftPr
     try unknownFields.traverse(visitor: &visitor)
   }
 
-  public static func ==(lhs: Fuzzilli_Protobuf_CallMethodWithSpread, rhs: Fuzzilli_Protobuf_CallMethodWithSpread) -> Bool {
+  public static func ==(lhs: Fuzzilli_Protobuf_CallMethod, rhs: Fuzzilli_Protobuf_CallMethod) -> Bool {
     if lhs.methodName != rhs.methodName {return false}
     if lhs.spreads != rhs.spreads {return false}
     if lhs.unknownFields != rhs.unknownFields {return false}
@@ -2648,25 +2574,6 @@ extension Fuzzilli_Protobuf_CallMethodWithSpread: SwiftProtobuf.Message, SwiftPr
 
 extension Fuzzilli_Protobuf_CallComputedMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".CallComputedMethod"
-  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
-
-  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
-    while let _ = try decoder.nextFieldNumber() {
-    }
-  }
-
-  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
-    try unknownFields.traverse(visitor: &visitor)
-  }
-
-  public static func ==(lhs: Fuzzilli_Protobuf_CallComputedMethod, rhs: Fuzzilli_Protobuf_CallComputedMethod) -> Bool {
-    if lhs.unknownFields != rhs.unknownFields {return false}
-    return true
-  }
-}
-
-extension Fuzzilli_Protobuf_CallComputedMethodWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
-  public static let protoMessageName: String = _protobuf_package + ".CallComputedMethodWithSpread"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
     1: .same(proto: "spreads"),
   ]
@@ -2690,7 +2597,7 @@ extension Fuzzilli_Protobuf_CallComputedMethodWithSpread: SwiftProtobuf.Message,
     try unknownFields.traverse(visitor: &visitor)
   }
 
-  public static func ==(lhs: Fuzzilli_Protobuf_CallComputedMethodWithSpread, rhs: Fuzzilli_Protobuf_CallComputedMethodWithSpread) -> Bool {
+  public static func ==(lhs: Fuzzilli_Protobuf_CallComputedMethod, rhs: Fuzzilli_Protobuf_CallComputedMethod) -> Bool {
     if lhs.spreads != rhs.spreads {return false}
     if lhs.unknownFields != rhs.unknownFields {return false}
     return true
@@ -2699,44 +2606,6 @@ extension Fuzzilli_Protobuf_CallComputedMethodWithSpread: SwiftProtobuf.Message,
 
 extension Fuzzilli_Protobuf_CallFunction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".CallFunction"
-  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
-
-  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
-    while let _ = try decoder.nextFieldNumber() {
-    }
-  }
-
-  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
-    try unknownFields.traverse(visitor: &visitor)
-  }
-
-  public static func ==(lhs: Fuzzilli_Protobuf_CallFunction, rhs: Fuzzilli_Protobuf_CallFunction) -> Bool {
-    if lhs.unknownFields != rhs.unknownFields {return false}
-    return true
-  }
-}
-
-extension Fuzzilli_Protobuf_Construct: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
-  public static let protoMessageName: String = _protobuf_package + ".Construct"
-  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
-
-  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
-    while let _ = try decoder.nextFieldNumber() {
-    }
-  }
-
-  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
-    try unknownFields.traverse(visitor: &visitor)
-  }
-
-  public static func ==(lhs: Fuzzilli_Protobuf_Construct, rhs: Fuzzilli_Protobuf_Construct) -> Bool {
-    if lhs.unknownFields != rhs.unknownFields {return false}
-    return true
-  }
-}
-
-extension Fuzzilli_Protobuf_CallFunctionWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
-  public static let protoMessageName: String = _protobuf_package + ".CallFunctionWithSpread"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
     1: .same(proto: "spreads"),
   ]
@@ -2760,15 +2629,15 @@ extension Fuzzilli_Protobuf_CallFunctionWithSpread: SwiftProtobuf.Message, Swift
     try unknownFields.traverse(visitor: &visitor)
   }
 
-  public static func ==(lhs: Fuzzilli_Protobuf_CallFunctionWithSpread, rhs: Fuzzilli_Protobuf_CallFunctionWithSpread) -> Bool {
+  public static func ==(lhs: Fuzzilli_Protobuf_CallFunction, rhs: Fuzzilli_Protobuf_CallFunction) -> Bool {
     if lhs.spreads != rhs.spreads {return false}
     if lhs.unknownFields != rhs.unknownFields {return false}
     return true
   }
 }
 
-extension Fuzzilli_Protobuf_ConstructWithSpread: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
-  public static let protoMessageName: String = _protobuf_package + ".ConstructWithSpread"
+extension Fuzzilli_Protobuf_Construct: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  public static let protoMessageName: String = _protobuf_package + ".Construct"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
     1: .same(proto: "spreads"),
   ]
@@ -2792,7 +2661,7 @@ extension Fuzzilli_Protobuf_ConstructWithSpread: SwiftProtobuf.Message, SwiftPro
     try unknownFields.traverse(visitor: &visitor)
   }
 
-  public static func ==(lhs: Fuzzilli_Protobuf_ConstructWithSpread, rhs: Fuzzilli_Protobuf_ConstructWithSpread) -> Bool {
+  public static func ==(lhs: Fuzzilli_Protobuf_Construct, rhs: Fuzzilli_Protobuf_Construct) -> Bool {
     if lhs.spreads != rhs.spreads {return false}
     if lhs.unknownFields != rhs.unknownFields {return false}
     return true
diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto
index 868dab26f..2611d6c75 100644
--- a/Sources/Fuzzilli/Protobuf/operations.proto
+++ b/Sources/Fuzzilli/Protobuf/operations.proto
@@ -175,31 +175,18 @@ message Await {
 
 message CallMethod {
     string methodName = 1;
-}
-
-message CallMethodWithSpread {
-    string methodName = 1;
     repeated bool spreads = 2;
 }
 
 message CallComputedMethod {
-}
-
-message CallComputedMethodWithSpread {
     repeated bool spreads = 1;
 }
 
 message CallFunction {
-}
-
-message Construct {
-}
-
-message CallFunctionWithSpread {
     repeated bool spreads = 1;
 }
 
-message ConstructWithSpread {
+message Construct {
     repeated bool spreads = 1;
 }
 
diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift
index 1c3707401..f1417440c 100644
--- a/Sources/Fuzzilli/Protobuf/program.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/program.pb.swift
@@ -486,14 +486,6 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .callMethod(newValue)}
   }
 
-  public var callMethodWithSpread: Fuzzilli_Protobuf_CallMethodWithSpread {
-    get {
-      if case .callMethodWithSpread(let v)? = operation {return v}
-      return Fuzzilli_Protobuf_CallMethodWithSpread()
-    }
-    set {operation = .callMethodWithSpread(newValue)}
-  }
-
   public var callComputedMethod: Fuzzilli_Protobuf_CallComputedMethod {
     get {
       if case .callComputedMethod(let v)? = operation {return v}
@@ -502,14 +494,6 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .callComputedMethod(newValue)}
   }
 
-  public var callComputedMethodWithSpread: Fuzzilli_Protobuf_CallComputedMethodWithSpread {
-    get {
-      if case .callComputedMethodWithSpread(let v)? = operation {return v}
-      return Fuzzilli_Protobuf_CallComputedMethodWithSpread()
-    }
-    set {operation = .callComputedMethodWithSpread(newValue)}
-  }
-
   public var callFunction: Fuzzilli_Protobuf_CallFunction {
     get {
       if case .callFunction(let v)? = operation {return v}
@@ -526,22 +510,6 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .construct(newValue)}
   }
 
-  public var callFunctionWithSpread: Fuzzilli_Protobuf_CallFunctionWithSpread {
-    get {
-      if case .callFunctionWithSpread(let v)? = operation {return v}
-      return Fuzzilli_Protobuf_CallFunctionWithSpread()
-    }
-    set {operation = .callFunctionWithSpread(newValue)}
-  }
-
-  public var constructWithSpread: Fuzzilli_Protobuf_ConstructWithSpread {
-    get {
-      if case .constructWithSpread(let v)? = operation {return v}
-      return Fuzzilli_Protobuf_ConstructWithSpread()
-    }
-    set {operation = .constructWithSpread(newValue)}
-  }
-
   public var unaryOperation: Fuzzilli_Protobuf_UnaryOperation {
     get {
       if case .unaryOperation(let v)? = operation {return v}
@@ -966,13 +934,9 @@ public struct Fuzzilli_Protobuf_Instruction {
     case yieldEach(Fuzzilli_Protobuf_YieldEach)
     case await(Fuzzilli_Protobuf_Await)
     case callMethod(Fuzzilli_Protobuf_CallMethod)
-    case callMethodWithSpread(Fuzzilli_Protobuf_CallMethodWithSpread)
     case callComputedMethod(Fuzzilli_Protobuf_CallComputedMethod)
-    case callComputedMethodWithSpread(Fuzzilli_Protobuf_CallComputedMethodWithSpread)
     case callFunction(Fuzzilli_Protobuf_CallFunction)
     case construct(Fuzzilli_Protobuf_Construct)
-    case callFunctionWithSpread(Fuzzilli_Protobuf_CallFunctionWithSpread)
-    case constructWithSpread(Fuzzilli_Protobuf_ConstructWithSpread)
     case unaryOperation(Fuzzilli_Protobuf_UnaryOperation)
     case binaryOperation(Fuzzilli_Protobuf_BinaryOperation)
     case binaryOperationAndReassign(Fuzzilli_Protobuf_BinaryOperationAndReassign)
@@ -1203,18 +1167,10 @@ public struct Fuzzilli_Protobuf_Instruction {
         guard case .callMethod(let l) = lhs, case .callMethod(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
-      case (.callMethodWithSpread, .callMethodWithSpread): return {
-        guard case .callMethodWithSpread(let l) = lhs, case .callMethodWithSpread(let r) = rhs else { preconditionFailure() }
-        return l == r
-      }()
       case (.callComputedMethod, .callComputedMethod): return {
         guard case .callComputedMethod(let l) = lhs, case .callComputedMethod(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
-      case (.callComputedMethodWithSpread, .callComputedMethodWithSpread): return {
-        guard case .callComputedMethodWithSpread(let l) = lhs, case .callComputedMethodWithSpread(let r) = rhs else { preconditionFailure() }
-        return l == r
-      }()
       case (.callFunction, .callFunction): return {
         guard case .callFunction(let l) = lhs, case .callFunction(let r) = rhs else { preconditionFailure() }
         return l == r
@@ -1223,14 +1179,6 @@ public struct Fuzzilli_Protobuf_Instruction {
         guard case .construct(let l) = lhs, case .construct(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
-      case (.callFunctionWithSpread, .callFunctionWithSpread): return {
-        guard case .callFunctionWithSpread(let l) = lhs, case .callFunctionWithSpread(let r) = rhs else { preconditionFailure() }
-        return l == r
-      }()
-      case (.constructWithSpread, .constructWithSpread): return {
-        guard case .constructWithSpread(let l) = lhs, case .constructWithSpread(let r) = rhs else { preconditionFailure() }
-        return l == r
-      }()
       case (.unaryOperation, .unaryOperation): return {
         guard case .unaryOperation(let l) = lhs, case .unaryOperation(let r) = rhs else { preconditionFailure() }
         return l == r
@@ -1577,20 +1525,16 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     74: .same(proto: "yieldEach"),
     75: .same(proto: "await"),
     31: .same(proto: "callMethod"),
-    103: .same(proto: "callMethodWithSpread"),
-    96: .same(proto: "callComputedMethod"),
-    104: .same(proto: "callComputedMethodWithSpread"),
+    34: .same(proto: "callComputedMethod"),
     32: .same(proto: "callFunction"),
     33: .same(proto: "construct"),
-    34: .same(proto: "callFunctionWithSpread"),
-    105: .same(proto: "constructWithSpread"),
     35: .same(proto: "unaryOperation"),
     36: .same(proto: "binaryOperation"),
-    97: .same(proto: "binaryOperationAndReassign"),
+    95: .same(proto: "binaryOperationAndReassign"),
     37: .same(proto: "dup"),
     38: .same(proto: "reassign"),
     39: .same(proto: "compare"),
-    98: .same(proto: "conditionalOperation"),
+    96: .same(proto: "conditionalOperation"),
     40: .same(proto: "eval"),
     87: .same(proto: "beginClassDefinition"),
     88: .same(proto: "beginMethodDefinition"),
@@ -1606,9 +1550,9 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     45: .same(proto: "beginIf"),
     46: .same(proto: "beginElse"),
     47: .same(proto: "endIf"),
-    99: .same(proto: "beginSwitch"),
-    100: .same(proto: "beginSwitchCase"),
-    101: .same(proto: "endSwitch"),
+    97: .same(proto: "beginSwitch"),
+    98: .same(proto: "beginSwitchCase"),
+    99: .same(proto: "endSwitch"),
     48: .same(proto: "beginWhile"),
     49: .same(proto: "endWhile"),
     50: .same(proto: "beginDoWhile"),
@@ -1623,7 +1567,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     59: .same(proto: "continue"),
     60: .same(proto: "beginTry"),
     61: .same(proto: "beginCatch"),
-    95: .same(proto: "beginFinally"),
+    94: .same(proto: "beginFinally"),
     62: .same(proto: "endTryCatch"),
     63: .same(proto: "throwException"),
     81: .same(proto: "beginCodeString"),
@@ -2026,16 +1970,16 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
         }
       }()
       case 34: try {
-        var v: Fuzzilli_Protobuf_CallFunctionWithSpread?
+        var v: Fuzzilli_Protobuf_CallComputedMethod?
         var hadOneofValue = false
         if let current = self.operation {
           hadOneofValue = true
-          if case .callFunctionWithSpread(let m) = current {v = m}
+          if case .callComputedMethod(let m) = current {v = m}
         }
         try decoder.decodeSingularMessageField(value: &v)
         if let v = v {
           if hadOneofValue {try decoder.handleConflictingOneOf()}
-          self.operation = .callFunctionWithSpread(v)
+          self.operation = .callComputedMethod(v)
         }
       }()
       case 35: try {
@@ -2766,7 +2710,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .storeSuperProperty(v)
         }
       }()
-      case 95: try {
+      case 94: try {
         var v: Fuzzilli_Protobuf_BeginFinally?
         var hadOneofValue = false
         if let current = self.operation {
@@ -2779,20 +2723,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .beginFinally(v)
         }
       }()
-      case 96: try {
-        var v: Fuzzilli_Protobuf_CallComputedMethod?
-        var hadOneofValue = false
-        if let current = self.operation {
-          hadOneofValue = true
-          if case .callComputedMethod(let m) = current {v = m}
-        }
-        try decoder.decodeSingularMessageField(value: &v)
-        if let v = v {
-          if hadOneofValue {try decoder.handleConflictingOneOf()}
-          self.operation = .callComputedMethod(v)
-        }
-      }()
-      case 97: try {
+      case 95: try {
         var v: Fuzzilli_Protobuf_BinaryOperationAndReassign?
         var hadOneofValue = false
         if let current = self.operation {
@@ -2805,7 +2736,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .binaryOperationAndReassign(v)
         }
       }()
-      case 98: try {
+      case 96: try {
         var v: Fuzzilli_Protobuf_ConditionalOperation?
         var hadOneofValue = false
         if let current = self.operation {
@@ -2818,7 +2749,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .conditionalOperation(v)
         }
       }()
-      case 99: try {
+      case 97: try {
         var v: Fuzzilli_Protobuf_BeginSwitch?
         var hadOneofValue = false
         if let current = self.operation {
@@ -2831,7 +2762,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .beginSwitch(v)
         }
       }()
-      case 100: try {
+      case 98: try {
         var v: Fuzzilli_Protobuf_BeginSwitchCase?
         var hadOneofValue = false
         if let current = self.operation {
@@ -2844,7 +2775,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .beginSwitchCase(v)
         }
       }()
-      case 101: try {
+      case 99: try {
         var v: Fuzzilli_Protobuf_EndSwitch?
         var hadOneofValue = false
         if let current = self.operation {
@@ -2870,33 +2801,6 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .createTemplateString(v)
         }
       }()
-      case 103: try {
-        var v: Fuzzilli_Protobuf_CallMethodWithSpread?
-        if let current = self.operation {
-          try decoder.handleConflictingOneOf()
-          if case .callMethodWithSpread(let m) = current {v = m}
-        }
-        try decoder.decodeSingularMessageField(value: &v)
-        if let v = v {self.operation = .callMethodWithSpread(v)}
-      }()
-      case 104: try {
-        var v: Fuzzilli_Protobuf_CallComputedMethodWithSpread?
-        if let current = self.operation {
-          try decoder.handleConflictingOneOf()
-          if case .callComputedMethodWithSpread(let m) = current {v = m}
-        }
-        try decoder.decodeSingularMessageField(value: &v)
-        if let v = v {self.operation = .callComputedMethodWithSpread(v)}
-      }()
-      case 105: try {
-        var v: Fuzzilli_Protobuf_ConstructWithSpread?
-        if let current = self.operation {
-          try decoder.handleConflictingOneOf()
-          if case .constructWithSpread(let m) = current {v = m}
-        }
-        try decoder.decodeSingularMessageField(value: &v)
-        if let v = v {self.operation = .constructWithSpread(v)}
-      }()
       default: break
       }
     }
@@ -3030,8 +2934,8 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
       guard case .construct(let v)? = self.operation else { preconditionFailure() }
       try visitor.visitSingularMessageField(value: v, fieldNumber: 33)
     }()
-    case .callFunctionWithSpread?: try {
-      guard case .callFunctionWithSpread(let v)? = self.operation else { preconditionFailure() }
+    case .callComputedMethod?: try {
+      guard case .callComputedMethod(let v)? = self.operation else { preconditionFailure() }
       try visitor.visitSingularMessageField(value: v, fieldNumber: 34)
     }()
     case .unaryOperation?: try {
@@ -3260,48 +3164,32 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     }()
     case .beginFinally?: try {
       guard case .beginFinally(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 95)
-    }()
-    case .callComputedMethod?: try {
-      guard case .callComputedMethod(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 96)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 94)
     }()
     case .binaryOperationAndReassign?: try {
       guard case .binaryOperationAndReassign(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 97)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 95)
     }()
     case .conditionalOperation?: try {
       guard case .conditionalOperation(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 98)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 96)
     }()
     case .beginSwitch?: try {
       guard case .beginSwitch(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 99)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 97)
     }()
     case .beginSwitchCase?: try {
       guard case .beginSwitchCase(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 100)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 98)
     }()
     case .endSwitch?: try {
       guard case .endSwitch(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 101)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 99)
     }()
     case .createTemplateString?: try {
       guard case .createTemplateString(let v)? = self.operation else { preconditionFailure() }
       try visitor.visitSingularMessageField(value: v, fieldNumber: 102)
     }()
-    case .callMethodWithSpread?: try {
-      guard case .callMethodWithSpread(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 103)
-    }()
-    case .callComputedMethodWithSpread?: try {
-      guard case .callComputedMethodWithSpread(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 104)
-    }()
-    case .constructWithSpread?: try {
-      guard case .constructWithSpread(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 105)
-    }()
     case nil: break
     }
     try unknownFields.traverse(visitor: &visitor)
diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto
index 8dd215d1f..b4e35d0f1 100644
--- a/Sources/Fuzzilli/Protobuf/program.proto
+++ b/Sources/Fuzzilli/Protobuf/program.proto
@@ -71,20 +71,16 @@ message Instruction {
         YieldEach yieldEach = 74;
         Await await = 75;
         CallMethod callMethod = 31;
-        CallMethodWithSpread callMethodWithSpread = 103;
-        CallComputedMethod callComputedMethod = 96;
-        CallComputedMethodWithSpread callComputedMethodWithSpread = 104;
+        CallComputedMethod callComputedMethod = 34;
         CallFunction callFunction = 32;
         Construct construct = 33;
-        CallFunctionWithSpread callFunctionWithSpread = 34;
-        ConstructWithSpread constructWithSpread = 105;
         UnaryOperation unaryOperation = 35;
         BinaryOperation binaryOperation = 36;
-        BinaryOperationAndReassign binaryOperationAndReassign = 97;
+        BinaryOperationAndReassign binaryOperationAndReassign = 95;
         Dup dup = 37;
         Reassign reassign = 38;
         Compare compare = 39;
-        ConditionalOperation conditionalOperation = 98;
+        ConditionalOperation conditionalOperation = 96;
         Eval eval = 40;
         BeginClassDefinition beginClassDefinition = 87;
         BeginMethodDefinition beginMethodDefinition = 88;
@@ -100,9 +96,9 @@ message Instruction {
         BeginIf beginIf = 45;
         BeginElse beginElse = 46;
         EndIf endIf = 47;
-        BeginSwitch beginSwitch = 99;
-        BeginSwitchCase beginSwitchCase = 100;
-        EndSwitch endSwitch = 101;
+        BeginSwitch beginSwitch = 97;
+        BeginSwitchCase beginSwitchCase = 98;
+        EndSwitch endSwitch = 99;
         BeginWhile beginWhile = 48;
         EndWhile endWhile = 49;
         BeginDoWhile beginDoWhile = 50;
@@ -117,7 +113,7 @@ message Instruction {
         Continue continue = 59;
         BeginTry beginTry = 60;
         BeginCatch beginCatch = 61;
-        BeginFinally beginFinally = 95;
+        BeginFinally beginFinally = 94;
         EndTryCatch endTryCatch = 62;
         ThrowException throwException = 63;
         BeginCodeString beginCodeString = 81;

From b03767cb74101fd8549ac92e136e509d1ec7ec50 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Thu, 9 Sep 2021 17:13:05 +0100
Subject: [PATCH 4/5] remove randCallArguments from XWithSpread operations

---
 Sources/Fuzzilli/Core/CodeGenerators.swift | 42 ++++++++++++++++------
 1 file changed, 31 insertions(+), 11 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index 5d541f6b9..9b0d00134 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -256,15 +256,21 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("MethodCallWithSpreadGenerator", input: .object()) { b, obj in
+        guard b.mode != .conservative else { return }
+
         var methodName = b.type(of: obj).randomMethod()
         if methodName == nil {
             guard b.mode != .conservative else { return }
             methodName = b.genMethodName()
         }
-        guard let arguments = b.randCallArguments(forMethod: methodName!, on: obj) else { return }
+
+        var arguments: [Variable] = []
+        for _ in 0...Int.random(in: 3...5){
+            arguments.append(b.randVar())
+        }
 
         let spreads = arguments.map({ arg in
-            probability(0.75) && b.type(of: arg).Is(.iterable)
+            probability(0.25)
         })
 
         b.callMethod(methodName!, on: obj, withArgs: arguments, spreading: spreads)
@@ -282,16 +288,22 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("ComputedMethodCallWithSpreadGenerator", input: .object()) { b, obj in
+        guard b.mode != .conservative else { return }
+
         var methodName = b.type(of: obj).randomMethod()
         if methodName == nil {
             guard b.mode != .conservative else { return }
             methodName = b.genMethodName()
         }
         let method = b.loadString(methodName!)
-        guard let arguments = b.randCallArguments(forMethod: methodName!, on: obj) else { return }
+
+        var arguments: [Variable] = []
+        for _ in 0...Int.random(in: 3...5){
+            arguments.append(b.randVar())
+        }
 
         let spreads = arguments.map({ arg in
-            probability(0.75) && b.type(of: arg).Is(.iterable)
+            probability(0.25)
         })
 
         b.callComputedMethod(method, on: obj, withArgs: arguments, spreading: spreads)
@@ -308,22 +320,30 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("FunctionCallWithSpreadGenerator", input: .function()) { b, f in
-        // Since we are spreading, the signature doesn't actually help, so ignore it completely
-        guard let arguments = b.randCallArguments(for: FunctionSignature.forUnknownFunction) else { return }
-        
-        // Pick some random arguments to spread.
+        guard b.mode != .conservative else { return }
+
+        var arguments: [Variable] = []
+        for _ in 0...Int.random(in: 3...5){
+            arguments.append(b.randVar())
+        }
+
         let spreads = arguments.map({ arg in
-            probability(0.75) && b.type(of: arg).Is(.iterable)
+            probability(0.25)
         })
         
         b.callFunction(f, withArgs: arguments, spreading: spreads)
     },
 
     CodeGenerator("ConstructorCallWithSpreadGenerator", input: .constructor()) { b, c in
-        guard let arguments = b.randCallArguments(for: c) else { return }
+        guard b.mode != .conservative else { return }
+
+        var arguments: [Variable] = []
+        for _ in 0...Int.random(in: 3...5){
+            arguments.append(b.randVar())
+        }
 
         let spreads = arguments.map({ arg in
-            probability(0.75) && b.type(of: arg).Is(.iterable)
+            probability(0.25)
         })
 
         b.construct(c, withArgs: arguments, spreading: spreads)

From 3bdf33fc3540833e61faa4253ad23e03b2026de4 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Fri, 10 Sep 2021 10:43:13 +0100
Subject: [PATCH 5/5] Add comments and randCallArgumentsWithSpreading

---
 Sources/Fuzzilli/Core/CodeGenerators.swift | 60 ++++++----------------
 Sources/Fuzzilli/Core/ProgramBuilder.swift | 17 ++++++
 2 files changed, 33 insertions(+), 44 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index 9b0d00134..0b45ee637 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -256,24 +256,15 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("MethodCallWithSpreadGenerator", input: .object()) { b, obj in
+        // We cannot currently track element types of Arrays and other Iterable objects and so cannot properly determine argument types when spreading.
+        // For that reason, we don't run this CodeGenerator in conservative mode
         guard b.mode != .conservative else { return }
 
-        var methodName = b.type(of: obj).randomMethod()
-        if methodName == nil {
-            guard b.mode != .conservative else { return }
-            methodName = b.genMethodName()
-        }
-
-        var arguments: [Variable] = []
-        for _ in 0...Int.random(in: 3...5){
-            arguments.append(b.randVar())
-        }
+        var methodName = b.type(of: obj).randomMethod() ?? b.genMethodName()
 
-        let spreads = arguments.map({ arg in
-            probability(0.25)
-        })
+        let (arguments, spreads) = b.randCallArgumentsWithSpreading(n: Int.random(in: 3...5))
 
-        b.callMethod(methodName!, on: obj, withArgs: arguments, spreading: spreads)
+        b.callMethod(methodName, on: obj, withArgs: arguments, spreading: spreads)
     },
 
     CodeGenerator("ComputedMethodCallGenerator", input: .object()) { b, obj in
@@ -288,23 +279,14 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("ComputedMethodCallWithSpreadGenerator", input: .object()) { b, obj in
+        // We cannot currently track element types of Arrays and other Iterable objects and so cannot properly determine argument types when spreading.
+        // For that reason, we don't run this CodeGenerator in conservative mode
         guard b.mode != .conservative else { return }
 
-        var methodName = b.type(of: obj).randomMethod()
-        if methodName == nil {
-            guard b.mode != .conservative else { return }
-            methodName = b.genMethodName()
-        }
-        let method = b.loadString(methodName!)
-
-        var arguments: [Variable] = []
-        for _ in 0...Int.random(in: 3...5){
-            arguments.append(b.randVar())
-        }
+        var methodName = b.type(of: obj).randomMethod() ?? b.genMethodName()
+        let method = b.loadString(methodName)
 
-        let spreads = arguments.map({ arg in
-            probability(0.25)
-        })
+        let (arguments, spreads) = b.randCallArgumentsWithSpreading(n: Int.random(in: 3...5))
 
         b.callComputedMethod(method, on: obj, withArgs: arguments, spreading: spreads)
     },
@@ -320,31 +302,21 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("FunctionCallWithSpreadGenerator", input: .function()) { b, f in
+        // We cannot currently track element types of Arrays and other Iterable objects and so cannot properly determine argument types when spreading.
+        // For that reason, we don't run this CodeGenerator in conservative mode
         guard b.mode != .conservative else { return }
 
-        var arguments: [Variable] = []
-        for _ in 0...Int.random(in: 3...5){
-            arguments.append(b.randVar())
-        }
-
-        let spreads = arguments.map({ arg in
-            probability(0.25)
-        })
+        let (arguments, spreads) = b.randCallArgumentsWithSpreading(n: Int.random(in: 3...5))
         
         b.callFunction(f, withArgs: arguments, spreading: spreads)
     },
 
     CodeGenerator("ConstructorCallWithSpreadGenerator", input: .constructor()) { b, c in
+        // We cannot currently track element types of Arrays and other Iterable objects and so cannot properly determine argument types when spreading.
+        // For that reason, we don't run this CodeGenerator in conservative mode
         guard b.mode != .conservative else { return }
 
-        var arguments: [Variable] = []
-        for _ in 0...Int.random(in: 3...5){
-            arguments.append(b.randVar())
-        }
-
-        let spreads = arguments.map({ arg in
-            probability(0.25)
-        })
+        let (arguments, spreads) = b.randCallArgumentsWithSpreading(n: Int.random(in: 3...5))
 
         b.construct(c, withArgs: arguments, spreading: spreads)
     },
diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index 8ed8f3724..604535e0b 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -455,6 +455,23 @@ public class ProgramBuilder {
         return randCallArguments(for: signature)
     }
 
+    public func randCallArgumentsWithSpreading(n: Int) -> (arguments: [Variable], spreads: [Bool]) {
+        var arguments: [Variable] = []
+        var spreads: [Bool] = []
+        for _ in 0...n {
+            let val = randVar()
+            arguments.append(val)
+            // Prefer to spread values that we know are iterable, as non-iterable values will lead to exceptions ("TypeError: Found non-callable @@iterator")
+            if type(of: val).Is(.iterable) {
+                spreads.append(probability(0.9))
+            } else {
+                spreads.append(probability(0.1))
+            }
+        }
+
+        return (arguments, spreads)
+    }
+
     public func generateCallArguments(forMethod methodName: String, on object: Variable) -> [Variable] {
         let signature = methodSignature(of: methodName, on: object)
         return generateCallArguments(for: signature)
