From 7a66735451771723ae3093bda763bbccd3b2fb5a Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Sun, 14 Nov 2021 17:23:04 +0000
Subject: [PATCH 1/9] Add DestructArray and DestructArrayAndReassign operations

---
 Sources/Fuzzilli/Core/CodeGenerators.swift    | 13 ++++
 Sources/Fuzzilli/Core/ProgramBuilder.swift    | 10 +++
 .../Fuzzilli/FuzzIL/AbstractInterpreter.swift |  6 ++
 Sources/Fuzzilli/FuzzIL/Instruction.swift     |  8 +++
 Sources/Fuzzilli/FuzzIL/Operations.swift      | 15 +++++
 Sources/Fuzzilli/FuzzIL/Semantics.swift       |  2 +
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   |  8 +++
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   |  8 +++
 Sources/Fuzzilli/Protobuf/operations.pb.swift | 58 +++++++++++++++++
 Sources/Fuzzilli/Protobuf/operations.proto    |  6 ++
 Sources/Fuzzilli/Protobuf/program.pb.swift    | 62 +++++++++++++++++++
 Sources/Fuzzilli/Protobuf/program.proto       |  2 +
 Tests/FuzzilliTests/LifterTest.swift          | 58 ++++++++++++++++-
 13 files changed, 255 insertions(+), 1 deletion(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index e3150aa47..1eeda9c3e 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -393,6 +393,19 @@ public let CodeGenerators: [CodeGenerator] = [
         b.reassign(target, to: val)
     },
 
+    CodeGenerator("DestructArrayGenerator", input: .iterable) { b, arr in
+        // TODO: We need a way to expose the length of arr so that element selection is accurate
+        b.destruct(arr, selectFirst: Int.random(in: 0...5))
+    },
+
+    CodeGenerator("DestructArrayAndReassignGenerator", input: .iterable) {b, arr in
+        var candidates: [Variable] = []
+        for _ in 0..<Int.random(in: 0..<5) {
+            candidates.append(b.randVar())
+        }
+        b.destructAndReassign(candidates, to: arr)
+    },
+
     CodeGenerator("ComparisonGenerator", inputs: (.anything, .anything)) { b, lhs, rhs in
         b.compare(lhs, rhs, with: chooseUniform(from: allComparators))
     },
diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index 978c15629..585221cac 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -1309,6 +1309,16 @@ public class ProgramBuilder {
         perform(Reassign(), withInputs: [output, input])
     }
 
+    @discardableResult
+    public func destruct(_ input: Variable, selectFirst numOutputs: Int) -> [Variable] {
+        let outputs = perform(DestructArray(numOutputs: numOutputs), withInputs: [input]).outputs
+        return Array(outputs)
+    }
+
+    public func destructAndReassign(_ candidates: [Variable], to input: Variable) {
+        perform(DestructArrayAndReassign(numInputs: candidates.count), withInputs: [input] + candidates)
+    }
+
     @discardableResult
     public func compare(_ lhs: Variable, _ rhs: Variable, with comparator: Comparator) -> Variable {
         return perform(Compare(comparator), withInputs: [lhs, rhs]).output
diff --git a/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift b/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
index b1702b818..04dce8bff 100644
--- a/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
+++ b/Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
@@ -476,6 +476,12 @@ public struct AbstractInterpreter {
         case is Reassign:
             set(instr.input(0), type(ofInput: 1))
 
+        case is DestructArray:
+            instr.outputs.forEach{set($0, .unknown)}
+
+        case is DestructArrayAndReassign:
+            instr.inputs.dropFirst().forEach{set($0, .unknown)}
+
         case is Compare:
             set(instr.output, .boolean)
 
diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift
index d462ff9fa..0c494dfde 100644
--- a/Sources/Fuzzilli/FuzzIL/Instruction.swift
+++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift
@@ -411,6 +411,10 @@ extension Instruction: ProtobufConvertible {
                 $0.dup = Fuzzilli_Protobuf_Dup()
             case is Reassign:
                 $0.reassign = Fuzzilli_Protobuf_Reassign()
+            case is DestructArray:
+                $0.destructArray = Fuzzilli_Protobuf_DestructArray()
+            case is DestructArrayAndReassign:
+                $0.destructArrayAndReassign = Fuzzilli_Protobuf_DestructArrayAndReassign()
             case let op as Compare:
                 $0.compare = Fuzzilli_Protobuf_Compare.with { $0.op = convertEnum(op.op, allComparators) }
             case is ConditionalOperation:
@@ -658,6 +662,10 @@ extension Instruction: ProtobufConvertible {
             op = Dup()
         case .reassign(_):
             op = Reassign()
+        case .destructArray(_):
+            op = DestructArray(numOutputs: inouts.count - 1)
+        case .destructArrayAndReassign(_):
+            op = DestructArrayAndReassign(numInputs: inouts.count - 1)
         case .compare(let p):
             op = Compare(try convertEnum(p.op, allComparators))
         case .conditionalOperation(_):
diff --git a/Sources/Fuzzilli/FuzzIL/Operations.swift b/Sources/Fuzzilli/FuzzIL/Operations.swift
index f4fedf93c..e469d4c51 100644
--- a/Sources/Fuzzilli/FuzzIL/Operations.swift
+++ b/Sources/Fuzzilli/FuzzIL/Operations.swift
@@ -653,6 +653,21 @@ class Reassign: Operation {
     }
 }
 
+/// Destructs an array into n output variables
+class DestructArray: Operation {
+    init(numOutputs: Int) {
+        super.init(numInputs: 1, numOutputs: numOutputs)
+    }
+}
+
+/// Destructs an array and reassigns the output to n existing variables
+class DestructArrayAndReassign: Operation {
+    init(numInputs: Int) {
+        // The first input is the array being destructed
+        super.init(numInputs: 1 + numInputs, numOutputs: 0)
+    }
+}
+
 // This array must be kept in sync with the Comparator Enum in operations.proto
 public enum Comparator: String {
     case equal              = "=="
diff --git a/Sources/Fuzzilli/FuzzIL/Semantics.swift b/Sources/Fuzzilli/FuzzIL/Semantics.swift
index 0e7de20a7..5e9c11af3 100644
--- a/Sources/Fuzzilli/FuzzIL/Semantics.swift
+++ b/Sources/Fuzzilli/FuzzIL/Semantics.swift
@@ -49,6 +49,8 @@ extension Operation {
             return inputIdx == 0
         case let op as UnaryOperation:
             return op.op.reassignsInput
+        case is DestructArrayAndReassign:
+            return inputIdx != 0
         default:
             return false
         }
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index 17f68dcbc..9647002b8 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -229,6 +229,14 @@ public class FuzzILLifter: Lifter {
         case is Reassign:
             w.emit("Reassign \(input(0)), \(input(1))")
 
+        case is DestructArray:
+            let outputs = instr.outputs.map({ $0.identifier }).joined(separator: ", ")
+            w.emit("[\(outputs)] <- DestructArray \(input(0))")
+
+        case is DestructArrayAndReassign:
+            let outputs = instr.outputs.map({ $0.identifier }).joined(separator: ", ")
+            w.emit("[\(outputs)] <- DestructArrayAndReassign \(input(0))")
+
         case let op as Compare:
             w.emit("\(instr.output) <- Compare \(input(0)), '\(op.op.token)', \(input(1))")
 
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index 5fb5300ec..b07ad9e00 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -419,6 +419,14 @@ public class JavaScriptLifter: Lifter {
                 let expr = AssignmentExpression.new() <> input(0) <> " = " <> input(1)
                 w.emit(expr)
 
+            case is DestructArray:
+                let outputs = instr.outputs.map({ $0.identifier }).joined(separator: ", ")
+                w.emit("\(varDecl) [\(outputs)] = \(input(0));")
+
+            case is DestructArrayAndReassign:
+                let outputs = instr.inputs.dropFirst().map({ $0.identifier }).joined(separator: ", ")
+                w.emit("[\(outputs)] = \(input(0));")
+
             case let op as Compare:
                 output = BinaryExpression.new() <> input(0) <> " " <> op.op.token <> " " <> input(1)
 
diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift
index 3ea2c2839..1cc21f89b 100644
--- a/Sources/Fuzzilli/Protobuf/operations.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift
@@ -950,6 +950,26 @@ public struct Fuzzilli_Protobuf_Reassign {
   public init() {}
 }
 
+public struct Fuzzilli_Protobuf_DestructArray {
+  // 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_DestructArrayAndReassign {
+  // 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_Compare {
   // SwiftProtobuf.Message conformance is added in an extension below. See the
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@@ -3046,6 +3066,44 @@ extension Fuzzilli_Protobuf_Reassign: SwiftProtobuf.Message, SwiftProtobuf._Mess
   }
 }
 
+extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  public static let protoMessageName: String = _protobuf_package + ".DestructArray"
+  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_DestructArray, rhs: Fuzzilli_Protobuf_DestructArray) -> Bool {
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
+extension Fuzzilli_Protobuf_DestructArrayAndReassign: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  public static let protoMessageName: String = _protobuf_package + ".DestructArrayAndReassign"
+  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_DestructArrayAndReassign, rhs: Fuzzilli_Protobuf_DestructArrayAndReassign) -> Bool {
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
 extension Fuzzilli_Protobuf_Compare: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".Compare"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto
index bac259548..d21524541 100644
--- a/Sources/Fuzzilli/Protobuf/operations.proto
+++ b/Sources/Fuzzilli/Protobuf/operations.proto
@@ -256,6 +256,12 @@ message Dup {
 message Reassign {
 }
 
+message DestructArray {
+}
+
+message DestructArrayAndReassign {
+}
+
 enum Comparator {
     EQUAL = 0;
     STRICT_EQUAL = 1;
diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift
index 57d12ce45..d3d9d735e 100644
--- a/Sources/Fuzzilli/Protobuf/program.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/program.pb.swift
@@ -593,6 +593,22 @@ public struct Fuzzilli_Protobuf_Instruction {
     set {operation = .reassign(newValue)}
   }
 
+  public var destructArray: Fuzzilli_Protobuf_DestructArray {
+    get {
+      if case .destructArray(let v)? = operation {return v}
+      return Fuzzilli_Protobuf_DestructArray()
+    }
+    set {operation = .destructArray(newValue)}
+  }
+
+  public var destructArrayAndReassign: Fuzzilli_Protobuf_DestructArrayAndReassign {
+    get {
+      if case .destructArrayAndReassign(let v)? = operation {return v}
+      return Fuzzilli_Protobuf_DestructArrayAndReassign()
+    }
+    set {operation = .destructArrayAndReassign(newValue)}
+  }
+
   public var compare: Fuzzilli_Protobuf_Compare {
     get {
       if case .compare(let v)? = operation {return v}
@@ -998,6 +1014,8 @@ public struct Fuzzilli_Protobuf_Instruction {
     case reassignWithBinop(Fuzzilli_Protobuf_ReassignWithBinop)
     case dup(Fuzzilli_Protobuf_Dup)
     case reassign(Fuzzilli_Protobuf_Reassign)
+    case destructArray(Fuzzilli_Protobuf_DestructArray)
+    case destructArrayAndReassign(Fuzzilli_Protobuf_DestructArrayAndReassign)
     case compare(Fuzzilli_Protobuf_Compare)
     case conditionalOperation(Fuzzilli_Protobuf_ConditionalOperation)
     case eval(Fuzzilli_Protobuf_Eval)
@@ -1276,6 +1294,14 @@ public struct Fuzzilli_Protobuf_Instruction {
         guard case .reassign(let l) = lhs, case .reassign(let r) = rhs else { preconditionFailure() }
         return l == r
       }()
+      case (.destructArray, .destructArray): return {
+        guard case .destructArray(let l) = lhs, case .destructArray(let r) = rhs else { preconditionFailure() }
+        return l == r
+      }()
+      case (.destructArrayAndReassign, .destructArrayAndReassign): return {
+        guard case .destructArrayAndReassign(let l) = lhs, case .destructArrayAndReassign(let r) = rhs else { preconditionFailure() }
+        return l == r
+      }()
       case (.compare, .compare): return {
         guard case .compare(let l) = lhs, case .compare(let r) = rhs else { preconditionFailure() }
         return l == r
@@ -1619,6 +1645,8 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     95: .same(proto: "reassignWithBinop"),
     37: .same(proto: "dup"),
     38: .same(proto: "reassign"),
+    117: .same(proto: "destructArray"),
+    118: .same(proto: "destructArrayAndReassign"),
     39: .same(proto: "compare"),
     96: .same(proto: "conditionalOperation"),
     40: .same(proto: "eval"),
@@ -2966,6 +2994,32 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .storeSuperPropertyWithBinop(v)
         }
       }()
+      case 117: try {
+        var v: Fuzzilli_Protobuf_DestructArray?
+        var hadOneofValue = false
+        if let current = self.operation {
+          hadOneofValue = true
+          if case .destructArray(let m) = current {v = m}
+        }
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {
+          if hadOneofValue {try decoder.handleConflictingOneOf()}
+          self.operation = .destructArray(v)
+        }
+      }()
+      case 118: try {
+        var v: Fuzzilli_Protobuf_DestructArrayAndReassign?
+        var hadOneofValue = false
+        if let current = self.operation {
+          hadOneofValue = true
+          if case .destructArrayAndReassign(let m) = current {v = m}
+        }
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {
+          if hadOneofValue {try decoder.handleConflictingOneOf()}
+          self.operation = .destructArrayAndReassign(v)
+        }
+      }()
       default: break
       }
     }
@@ -3380,6 +3434,14 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
       guard case .storeSuperPropertyWithBinop(let v)? = self.operation else { preconditionFailure() }
       try visitor.visitSingularMessageField(value: v, fieldNumber: 115)
     }()
+    case .destructArray?: try {
+      guard case .destructArray(let v)? = self.operation else { preconditionFailure() }
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 117)
+    }()
+    case .destructArrayAndReassign?: try {
+      guard case .destructArrayAndReassign(let v)? = self.operation else { preconditionFailure() }
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 118)
+    }()
     case nil: break
     }
     try unknownFields.traverse(visitor: &visitor)
diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto
index 044ec5d76..2d44bd98a 100644
--- a/Sources/Fuzzilli/Protobuf/program.proto
+++ b/Sources/Fuzzilli/Protobuf/program.proto
@@ -82,6 +82,8 @@ message Instruction {
         ReassignWithBinop reassignWithBinop = 95;
         Dup dup = 37;
         Reassign reassign = 38;
+        DestructArray destructArray = 117;
+        DestructArrayAndReassign destructArrayAndReassign = 118;
         Compare compare = 39;
         ConditionalOperation conditionalOperation = 96;
         Eval eval = 40;
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index 94a2ccd30..c2e0c1125 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -1034,6 +1034,60 @@ class LifterTests: XCTestCase {
 
         XCTAssertEqual(lifted_program,expected_program)
     }
+
+    func testArrayDestructLifting() {
+        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)
+
+        b.destruct(v4, selectFirst: 3)
+        b.destruct(v4, selectFirst: 5)
+
+        let program = b.finalize()
+        let lifted_program = fuzzer.lifter.lift(program)
+        let expected_program = """
+        const v4 = [15,30,"Hello","World"];
+        let [v5, v6, v7] = v4;
+        let [v8, v9, v10, v11, v12] = v4;
+
+        """
+
+        XCTAssertEqual(lifted_program,expected_program)
+    }
+
+    func testArrayDestructAndReassignLifting() {
+        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 v8 = b.loadInt(1000)
+        let v9 = b.loadBuiltin("JSON")
+
+        b.destructAndReassign([v8, v9], to: v4)
+
+        let program = b.finalize()
+        let lifted_program = fuzzer.lifter.lift(program)
+        let expected_program = """
+        const v4 = [15,30,"Hello","World"];
+        let v5 = 1000;
+        let v6 = JSON;
+        [v5, v6] = v4;
+
+        """
+
+        XCTAssertEqual(lifted_program,expected_program)
+    }
 }
 
 extension LifterTests {
@@ -1065,7 +1119,9 @@ extension LifterTests {
             ("testConstructWithSpreadLifting", testConstructWithSpreadLifting),
             ("testCallWithSpreadLifting", testCallWithSpreadLifting),
             ("testPropertyAndElementWithBinopLifting", testPropertyAndElementWithBinopLifting),
-            ("testSuperPropertyWithBinopLifting", testSuperPropertyWithBinopLifting)
+            ("testSuperPropertyWithBinopLifting", testSuperPropertyWithBinopLifting),
+            ("testArrayDestructLifting",testArrayDestructLifting),
+            ("testArrayDestructAndReassignLifting", testArrayDestructAndReassignLifting),
         ]
     }
 }

From 1a31082f37d7e12014bba2d5122000d8928cf244 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Mon, 15 Nov 2021 12:53:26 +0000
Subject: [PATCH 2/9] fix bug in FuzzIL lifting

---
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index 9647002b8..debff3d28 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -234,7 +234,7 @@ public class FuzzILLifter: Lifter {
             w.emit("[\(outputs)] <- DestructArray \(input(0))")
 
         case is DestructArrayAndReassign:
-            let outputs = instr.outputs.map({ $0.identifier }).joined(separator: ", ")
+            let outputs = instr.inputs.dropFirst().map({ $0.identifier }).joined(separator: ", ")
             w.emit("[\(outputs)] <- DestructArrayAndReassign \(input(0))")
 
         case let op as Compare:

From 663377fcac1eca44218776bfc9dc86fa23b3580c Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Wed, 17 Nov 2021 10:38:42 +0000
Subject: [PATCH 3/9] add support for rest element

---
 Sources/Fuzzilli/Core/CodeGenerators.swift    |  4 +--
 Sources/Fuzzilli/Core/ProgramBuilder.swift    |  8 ++---
 Sources/Fuzzilli/FuzzIL/Instruction.swift     | 16 +++++-----
 Sources/Fuzzilli/FuzzIL/Operations.swift      | 10 ++++--
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   | 18 +++++++----
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   | 18 +++++++----
 Sources/Fuzzilli/Protobuf/operations.pb.swift | 32 ++++++++++++++++---
 Sources/Fuzzilli/Protobuf/operations.proto    |  2 ++
 Tests/FuzzilliTests/LifterTest.swift          |  9 ++++--
 9 files changed, 82 insertions(+), 35 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index 1eeda9c3e..c051c2f70 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -395,7 +395,7 @@ public let CodeGenerators: [CodeGenerator] = [
 
     CodeGenerator("DestructArrayGenerator", input: .iterable) { b, arr in
         // TODO: We need a way to expose the length of arr so that element selection is accurate
-        b.destruct(arr, selectFirst: Int.random(in: 0...5))
+        b.destruct(arr, selectFirst: Int.random(in: 0...5), hasRestElement: probability(0.2))
     },
 
     CodeGenerator("DestructArrayAndReassignGenerator", input: .iterable) {b, arr in
@@ -403,7 +403,7 @@ public let CodeGenerators: [CodeGenerator] = [
         for _ in 0..<Int.random(in: 0..<5) {
             candidates.append(b.randVar())
         }
-        b.destructAndReassign(candidates, to: arr)
+        b.destructAndReassign(candidates, to: arr, hasRestElement: probability(0.2))
     },
 
     CodeGenerator("ComparisonGenerator", inputs: (.anything, .anything)) { b, lhs, rhs in
diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index 585221cac..ac0aa402f 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -1310,13 +1310,13 @@ public class ProgramBuilder {
     }
 
     @discardableResult
-    public func destruct(_ input: Variable, selectFirst numOutputs: Int) -> [Variable] {
-        let outputs = perform(DestructArray(numOutputs: numOutputs), withInputs: [input]).outputs
+    public func destruct(_ input: Variable, selectFirst numOutputs: Int, hasRestElement: Bool = false) -> [Variable] {
+        let outputs = perform(DestructArray(numOutputs: numOutputs, hasRestElement: hasRestElement), withInputs: [input]).outputs
         return Array(outputs)
     }
 
-    public func destructAndReassign(_ candidates: [Variable], to input: Variable) {
-        perform(DestructArrayAndReassign(numInputs: candidates.count), withInputs: [input] + candidates)
+    public func destructAndReassign(_ candidates: [Variable], to input: Variable, hasRestElement: Bool = false) {
+        perform(DestructArrayAndReassign(numInputs: candidates.count, hasRestElement: hasRestElement), withInputs: [input] + candidates)
     }
 
     @discardableResult
diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift
index 0c494dfde..0196d41aa 100644
--- a/Sources/Fuzzilli/FuzzIL/Instruction.swift
+++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift
@@ -411,10 +411,10 @@ extension Instruction: ProtobufConvertible {
                 $0.dup = Fuzzilli_Protobuf_Dup()
             case is Reassign:
                 $0.reassign = Fuzzilli_Protobuf_Reassign()
-            case is DestructArray:
-                $0.destructArray = Fuzzilli_Protobuf_DestructArray()
-            case is DestructArrayAndReassign:
-                $0.destructArrayAndReassign = Fuzzilli_Protobuf_DestructArrayAndReassign()
+            case let op as DestructArray:
+                $0.destructArray = Fuzzilli_Protobuf_DestructArray.with { $0.hasRestElement_p = op.hasRestElement }
+            case let op as DestructArrayAndReassign:
+                $0.destructArrayAndReassign = Fuzzilli_Protobuf_DestructArrayAndReassign.with { $0.hasRestElement_p = op.hasRestElement }
             case let op as Compare:
                 $0.compare = Fuzzilli_Protobuf_Compare.with { $0.op = convertEnum(op.op, allComparators) }
             case is ConditionalOperation:
@@ -662,10 +662,10 @@ extension Instruction: ProtobufConvertible {
             op = Dup()
         case .reassign(_):
             op = Reassign()
-        case .destructArray(_):
-            op = DestructArray(numOutputs: inouts.count - 1)
-        case .destructArrayAndReassign(_):
-            op = DestructArrayAndReassign(numInputs: inouts.count - 1)
+        case .destructArray(let p):
+            op = DestructArray(numOutputs: inouts.count - 1, hasRestElement: p.hasRestElement_p)
+        case .destructArrayAndReassign(let p):
+            op = DestructArrayAndReassign(numInputs: inouts.count - 1, hasRestElement: p.hasRestElement_p)
         case .compare(let p):
             op = Compare(try convertEnum(p.op, allComparators))
         case .conditionalOperation(_):
diff --git a/Sources/Fuzzilli/FuzzIL/Operations.swift b/Sources/Fuzzilli/FuzzIL/Operations.swift
index e469d4c51..1426c7a33 100644
--- a/Sources/Fuzzilli/FuzzIL/Operations.swift
+++ b/Sources/Fuzzilli/FuzzIL/Operations.swift
@@ -655,14 +655,20 @@ class Reassign: Operation {
 
 /// Destructs an array into n output variables
 class DestructArray: Operation {
-    init(numOutputs: Int) {
+    public var hasRestElement: Bool
+    
+    init(numOutputs: Int, hasRestElement: Bool) {
+        self.hasRestElement = hasRestElement
         super.init(numInputs: 1, numOutputs: numOutputs)
     }
 }
 
 /// Destructs an array and reassigns the output to n existing variables
 class DestructArrayAndReassign: Operation {
-    init(numInputs: Int) {
+    public var hasRestElement: Bool
+
+    init(numInputs: Int, hasRestElement:Bool) {
+        self.hasRestElement = hasRestElement
         // The first input is the array being destructed
         super.init(numInputs: 1 + numInputs, numOutputs: 0)
     }
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index debff3d28..ad2316975 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -229,13 +229,19 @@ public class FuzzILLifter: Lifter {
         case is Reassign:
             w.emit("Reassign \(input(0)), \(input(1))")
 
-        case is DestructArray:
-            let outputs = instr.outputs.map({ $0.identifier }).joined(separator: ", ")
-            w.emit("[\(outputs)] <- DestructArray \(input(0))")
+        case let op as DestructArray:
+            var outputs = instr.outputs.map({ $0.identifier })
+            if outputs.count > 0 && op.hasRestElement {
+                outputs.append("...\(outputs.popLast()!)")
+            }
+            w.emit("[\(outputs.joined(separator: ", "))] <- DestructArray \(input(0))")
 
-        case is DestructArrayAndReassign:
-            let outputs = instr.inputs.dropFirst().map({ $0.identifier }).joined(separator: ", ")
-            w.emit("[\(outputs)] <- DestructArrayAndReassign \(input(0))")
+        case let op as DestructArrayAndReassign:
+            var outputs = instr.outputs.map({ $0.identifier })
+            if outputs.count > 0 && op.hasRestElement {
+                outputs.append("...\(outputs.popLast()!)")
+            }
+            w.emit("[\(outputs.joined(separator: ", "))] <- DestructArrayAndReassign \(input(0))")
 
         case let op as Compare:
             w.emit("\(instr.output) <- Compare \(input(0)), '\(op.op.token)', \(input(1))")
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index b07ad9e00..e8a6dd981 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -419,13 +419,19 @@ public class JavaScriptLifter: Lifter {
                 let expr = AssignmentExpression.new() <> input(0) <> " = " <> input(1)
                 w.emit(expr)
 
-            case is DestructArray:
-                let outputs = instr.outputs.map({ $0.identifier }).joined(separator: ", ")
-                w.emit("\(varDecl) [\(outputs)] = \(input(0));")
+            case let op as DestructArray:
+                var outputs = instr.outputs.map({ $0.identifier })
+                if outputs.count > 0 && op.hasRestElement {
+                    outputs.append("...\(outputs.popLast()!)")
+                }
+                w.emit("\(varDecl) [\(outputs.joined(separator: ", "))] = \(input(0));")
 
-            case is DestructArrayAndReassign:
-                let outputs = instr.inputs.dropFirst().map({ $0.identifier }).joined(separator: ", ")
-                w.emit("[\(outputs)] = \(input(0));")
+            case let op as DestructArrayAndReassign:
+                var outputs = instr.inputs.dropFirst().map({ $0.identifier })
+                if outputs.count > 0 && op.hasRestElement {
+                    outputs.append("...\(outputs.popLast()!)")
+                }
+                w.emit("[\(outputs.joined(separator: ", "))] = \(input(0));")
 
             case let op as Compare:
                 output = BinaryExpression.new() <> input(0) <> " " <> op.op.token <> " " <> input(1)
diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift
index 1cc21f89b..b645b478d 100644
--- a/Sources/Fuzzilli/Protobuf/operations.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift
@@ -955,6 +955,8 @@ public struct Fuzzilli_Protobuf_DestructArray {
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
   // methods supported on all messages.
 
+  public var hasRestElement_p: Bool = false
+
   public var unknownFields = SwiftProtobuf.UnknownStorage()
 
   public init() {}
@@ -965,6 +967,8 @@ public struct Fuzzilli_Protobuf_DestructArrayAndReassign {
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
   // methods supported on all messages.
 
+  public var hasRestElement_p: Bool = false
+
   public var unknownFields = SwiftProtobuf.UnknownStorage()
 
   public init() {}
@@ -3068,18 +3072,28 @@ extension Fuzzilli_Protobuf_Reassign: SwiftProtobuf.Message, SwiftProtobuf._Mess
 
 extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".DestructArray"
-  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
+  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "hasRestElement"),
+  ]
 
   public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
-    while let _ = try decoder.nextFieldNumber() {
+    while let fieldNumber = try decoder.nextFieldNumber() {
+      switch fieldNumber {
+      case 1: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
+      default: break
+      }
     }
   }
 
   public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if self.hasRestElement_p != false {
+      try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 1)
+    }
     try unknownFields.traverse(visitor: &visitor)
   }
 
   public static func ==(lhs: Fuzzilli_Protobuf_DestructArray, rhs: Fuzzilli_Protobuf_DestructArray) -> Bool {
+    if lhs.hasRestElement_p != rhs.hasRestElement_p {return false}
     if lhs.unknownFields != rhs.unknownFields {return false}
     return true
   }
@@ -3087,18 +3101,28 @@ extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf.
 
 extension Fuzzilli_Protobuf_DestructArrayAndReassign: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".DestructArrayAndReassign"
-  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
+  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "hasRestElement"),
+  ]
 
   public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
-    while let _ = try decoder.nextFieldNumber() {
+    while let fieldNumber = try decoder.nextFieldNumber() {
+      switch fieldNumber {
+      case 1: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
+      default: break
+      }
     }
   }
 
   public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if self.hasRestElement_p != false {
+      try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 1)
+    }
     try unknownFields.traverse(visitor: &visitor)
   }
 
   public static func ==(lhs: Fuzzilli_Protobuf_DestructArrayAndReassign, rhs: Fuzzilli_Protobuf_DestructArrayAndReassign) -> Bool {
+    if lhs.hasRestElement_p != rhs.hasRestElement_p {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 d21524541..83ec3a0bf 100644
--- a/Sources/Fuzzilli/Protobuf/operations.proto
+++ b/Sources/Fuzzilli/Protobuf/operations.proto
@@ -257,9 +257,11 @@ message Reassign {
 }
 
 message DestructArray {
+    bool hasRestElement = 1;
 }
 
 message DestructArrayAndReassign {
+    bool hasRestElement = 1;
 }
 
 enum Comparator {
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index c2e0c1125..a9a60fef3 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -1049,13 +1049,16 @@ class LifterTests: XCTestCase {
         b.destruct(v4, selectFirst: 3)
         b.destruct(v4, selectFirst: 5)
 
+        b.destruct(v4, selectFirst: 2, hasRestElement: true)
+
         let program = b.finalize()
         let lifted_program = fuzzer.lifter.lift(program)
         let expected_program = """
         const v4 = [15,30,"Hello","World"];
         let [v5, v6, v7] = v4;
         let [v8, v9, v10, v11, v12] = v4;
-
+        let [v13, ...v14] = v4;
+        
         """
 
         XCTAssertEqual(lifted_program,expected_program)
@@ -1074,7 +1077,7 @@ class LifterTests: XCTestCase {
         let v8 = b.loadInt(1000)
         let v9 = b.loadBuiltin("JSON")
 
-        b.destructAndReassign([v8, v9], to: v4)
+        b.destructAndReassign([v8, v9], to: v4, hasRestElement: true)
 
         let program = b.finalize()
         let lifted_program = fuzzer.lifter.lift(program)
@@ -1082,7 +1085,7 @@ class LifterTests: XCTestCase {
         const v4 = [15,30,"Hello","World"];
         let v5 = 1000;
         let v6 = JSON;
-        [v5, v6] = v4;
+        [v5, ...v6] = v4;
 
         """
 

From 600407a6238a10814fd60c413cccec9c51d4e984 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Wed, 17 Nov 2021 13:54:25 +0000
Subject: [PATCH 4/9] add support for holes

---
 Sources/Fuzzilli/Core/CodeGenerators.swift    | 20 ++++++--
 Sources/Fuzzilli/Core/ProgramBuilder.swift    |  8 ++--
 Sources/Fuzzilli/FuzzIL/Instruction.swift     | 14 ++++--
 Sources/Fuzzilli/FuzzIL/Operations.swift      | 16 ++++---
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   | 46 +++++++++++++++----
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   | 46 +++++++++++++++----
 Sources/Fuzzilli/Protobuf/operations.pb.swift | 28 ++++++++---
 Sources/Fuzzilli/Protobuf/operations.proto    |  6 ++-
 Tests/FuzzilliTests/LifterTest.swift          | 18 ++++----
 9 files changed, 151 insertions(+), 51 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index c051c2f70..7054eb9c2 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -395,15 +395,27 @@ public let CodeGenerators: [CodeGenerator] = [
 
     CodeGenerator("DestructArrayGenerator", input: .iterable) { b, arr in
         // TODO: We need a way to expose the length of arr so that element selection is accurate
-        b.destruct(arr, selectFirst: Int.random(in: 0...5), hasRestElement: probability(0.2))
+        // Pick random indices
+        var indices: [Int] = []
+        for idx in 0..<Int.random(in: 0..<5) {
+            withProbability(0.7) {
+                indices.append(idx)
+            }
+        }
+
+        b.destruct(arr, selecting: indices, hasRestElement: probability(0.2))
     },
 
     CodeGenerator("DestructArrayAndReassignGenerator", input: .iterable) {b, arr in
         var candidates: [Variable] = []
-        for _ in 0..<Int.random(in: 0..<5) {
-            candidates.append(b.randVar())
+        var indices: [Int] = []
+        for idx in 0..<Int.random(in: 0..<5) {
+            withProbability(0.7) {
+                indices.append(idx)
+                candidates.append(b.randVar())
+            }
         }
-        b.destructAndReassign(candidates, to: arr, hasRestElement: probability(0.2))
+        b.destructAndReassign(candidates, atIndices: indices, from: arr, hasRestElement: probability(0.2))
     },
 
     CodeGenerator("ComparisonGenerator", inputs: (.anything, .anything)) { b, lhs, rhs in
diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index ac0aa402f..231ce3dce 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -1310,13 +1310,13 @@ public class ProgramBuilder {
     }
 
     @discardableResult
-    public func destruct(_ input: Variable, selectFirst numOutputs: Int, hasRestElement: Bool = false) -> [Variable] {
-        let outputs = perform(DestructArray(numOutputs: numOutputs, hasRestElement: hasRestElement), withInputs: [input]).outputs
+    public func destruct(_ input: Variable, selecting indices: [Int], hasRestElement: Bool = false) -> [Variable] {
+        let outputs = perform(DestructArray(indices: indices, hasRestElement: hasRestElement), withInputs: [input]).outputs
         return Array(outputs)
     }
 
-    public func destructAndReassign(_ candidates: [Variable], to input: Variable, hasRestElement: Bool = false) {
-        perform(DestructArrayAndReassign(numInputs: candidates.count, hasRestElement: hasRestElement), withInputs: [input] + candidates)
+    public func destructAndReassign(_ candidates: [Variable], atIndices indices: [Int], from input: Variable, hasRestElement: Bool = false) {
+        perform(DestructArrayAndReassign(indices: indices, hasRestElement: hasRestElement), withInputs: [input] + candidates)
     }
 
     @discardableResult
diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift
index 0196d41aa..b40413164 100644
--- a/Sources/Fuzzilli/FuzzIL/Instruction.swift
+++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift
@@ -412,9 +412,15 @@ extension Instruction: ProtobufConvertible {
             case is Reassign:
                 $0.reassign = Fuzzilli_Protobuf_Reassign()
             case let op as DestructArray:
-                $0.destructArray = Fuzzilli_Protobuf_DestructArray.with { $0.hasRestElement_p = op.hasRestElement }
+                $0.destructArray = Fuzzilli_Protobuf_DestructArray.with { 
+                    $0.indices = op.indices.map({ Int32($0) })
+                    $0.hasRestElement_p = op.hasRestElement
+                }
             case let op as DestructArrayAndReassign:
-                $0.destructArrayAndReassign = Fuzzilli_Protobuf_DestructArrayAndReassign.with { $0.hasRestElement_p = op.hasRestElement }
+                $0.destructArrayAndReassign = Fuzzilli_Protobuf_DestructArrayAndReassign.with {
+                    $0.indices = op.indices.map({ Int32($0) })
+                    $0.hasRestElement_p = op.hasRestElement 
+                }
             case let op as Compare:
                 $0.compare = Fuzzilli_Protobuf_Compare.with { $0.op = convertEnum(op.op, allComparators) }
             case is ConditionalOperation:
@@ -663,9 +669,9 @@ extension Instruction: ProtobufConvertible {
         case .reassign(_):
             op = Reassign()
         case .destructArray(let p):
-            op = DestructArray(numOutputs: inouts.count - 1, hasRestElement: p.hasRestElement_p)
+            op = DestructArray(indices: p.indices.map({ Int($0) }), hasRestElement: p.hasRestElement_p)
         case .destructArrayAndReassign(let p):
-            op = DestructArrayAndReassign(numInputs: inouts.count - 1, hasRestElement: p.hasRestElement_p)
+            op = DestructArrayAndReassign(indices: p.indices.map({ Int($0) }), hasRestElement: p.hasRestElement_p)
         case .compare(let p):
             op = Compare(try convertEnum(p.op, allComparators))
         case .conditionalOperation(_):
diff --git a/Sources/Fuzzilli/FuzzIL/Operations.swift b/Sources/Fuzzilli/FuzzIL/Operations.swift
index 1426c7a33..9516223bd 100644
--- a/Sources/Fuzzilli/FuzzIL/Operations.swift
+++ b/Sources/Fuzzilli/FuzzIL/Operations.swift
@@ -655,22 +655,26 @@ class Reassign: Operation {
 
 /// Destructs an array into n output variables
 class DestructArray: Operation {
-    public var hasRestElement: Bool
+    let indices: [Int]
+    let hasRestElement: Bool
     
-    init(numOutputs: Int, hasRestElement: Bool) {
+    init(indices: [Int], hasRestElement: Bool) {
         self.hasRestElement = hasRestElement
-        super.init(numInputs: 1, numOutputs: numOutputs)
+        self.indices = indices
+        super.init(numInputs: 1, numOutputs: indices.count)
     }
 }
 
 /// Destructs an array and reassigns the output to n existing variables
 class DestructArrayAndReassign: Operation {
-    public var hasRestElement: Bool
+    let indices: [Int]
+    let hasRestElement: Bool
 
-    init(numInputs: Int, hasRestElement:Bool) {
+    init(indices: [Int], hasRestElement:Bool) {
+        self.indices = indices
         self.hasRestElement = hasRestElement
         // The first input is the array being destructed
-        super.init(numInputs: 1 + numInputs, numOutputs: 0)
+        super.init(numInputs: 1 + indices.count, numOutputs: 0)
     }
 }
 
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index ad2316975..320e622fb 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -230,18 +230,48 @@ public class FuzzILLifter: Lifter {
             w.emit("Reassign \(input(0)), \(input(1))")
 
         case let op as DestructArray:
-            var outputs = instr.outputs.map({ $0.identifier })
-            if outputs.count > 0 && op.hasRestElement {
-                outputs.append("...\(outputs.popLast()!)")
+            var arrayPattern: String = ""
+            if op.indices.count > 0 {
+                let outputs = instr.outputs.map({ $0.identifier })
+                var outputIndex = 0
+                assert(op.indices.count == outputs.count)
+                
+                for index in 0...op.indices.last! {
+                    if op.indices.contains(index) {
+                        if index == op.indices.last! {
+                            op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
+                        } else {
+                            arrayPattern.append(" \(outputs[Int(index)]),")
+                        }
+                        outputIndex += 1
+                    } else {
+                        arrayPattern.append(",")
+                    }
+                }
             }
-            w.emit("[\(outputs.joined(separator: ", "))] <- DestructArray \(input(0))")
+            w.emit("[\(arrayPattern)] <- DestructArray \(input(0))")
 
         case let op as DestructArrayAndReassign:
-            var outputs = instr.outputs.map({ $0.identifier })
-            if outputs.count > 0 && op.hasRestElement {
-                outputs.append("...\(outputs.popLast()!)")
+            var arrayPattern: String = ""
+            if op.indices.count > 0 {
+                let outputs = instr.inputs.dropFirst().map({ $0.identifier })
+                var outputIndex = 0
+                assert(op.indices.count == outputs.count)
+                
+                for index in 0...op.indices.last! {
+                    if op.indices.contains(index) {
+                        if index == op.indices.last! {
+                            op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
+                        } else {
+                            arrayPattern.append(" \(outputs[Int(index)]),")
+                        }
+                        outputIndex += 1
+                    } else {
+                        arrayPattern.append(",")
+                    }
+                }
             }
-            w.emit("[\(outputs.joined(separator: ", "))] <- DestructArrayAndReassign \(input(0))")
+            w.emit("[\(arrayPattern)] <- DestructArrayAndReassign \(input(0))")
 
         case let op as Compare:
             w.emit("\(instr.output) <- Compare \(input(0)), '\(op.op.token)', \(input(1))")
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index e8a6dd981..714f6efdf 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -420,18 +420,48 @@ public class JavaScriptLifter: Lifter {
                 w.emit(expr)
 
             case let op as DestructArray:
-                var outputs = instr.outputs.map({ $0.identifier })
-                if outputs.count > 0 && op.hasRestElement {
-                    outputs.append("...\(outputs.popLast()!)")
+                var arrayPattern: String = ""
+                if op.indices.count > 0 {
+                    let outputs = instr.outputs.map({ $0.identifier })
+                    var outputIndex = 0;
+                    assert(op.indices.count == outputs.count)
+                
+                    for index in 0...op.indices.last! {
+                        if op.indices.contains(index) {
+                            if index == op.indices.last! {
+                                op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
+                            } else {
+                                arrayPattern.append("\(outputs[Int(outputIndex)]),")
+                            }
+                            outputIndex += 1
+                        } else {
+                            arrayPattern.append(" ,")
+                        }
+                    }
                 }
-                w.emit("\(varDecl) [\(outputs.joined(separator: ", "))] = \(input(0));")
+                w.emit("\(varDecl) [\(arrayPattern)] = \(input(0));")
 
             case let op as DestructArrayAndReassign:
-                var outputs = instr.inputs.dropFirst().map({ $0.identifier })
-                if outputs.count > 0 && op.hasRestElement {
-                    outputs.append("...\(outputs.popLast()!)")
+                var arrayPattern: String = ""
+                if op.indices.count > 0 {
+                    let outputs = instr.inputs.dropFirst().map({ $0.identifier })
+                    var outputIndex = 0;
+                    assert(op.indices.count == outputs.count)
+                
+                    for index in 0...op.indices.last! {
+                        if op.indices.contains(index) {
+                            if index == op.indices.last! {
+                                op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
+                            } else {
+                                arrayPattern.append("\(outputs[Int(outputIndex)]),")
+                            }
+                            outputIndex += 1
+                        } else {
+                            arrayPattern.append(" ,")
+                        }
+                    }
                 }
-                w.emit("[\(outputs.joined(separator: ", "))] = \(input(0));")
+                w.emit("[\(arrayPattern)] = \(input(0));")
 
             case let op as Compare:
                 output = BinaryExpression.new() <> input(0) <> " " <> op.op.token <> " " <> input(1)
diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift
index b645b478d..ced3ff06d 100644
--- a/Sources/Fuzzilli/Protobuf/operations.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift
@@ -955,6 +955,8 @@ public struct Fuzzilli_Protobuf_DestructArray {
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
   // methods supported on all messages.
 
+  public var indices: [Int32] = []
+
   public var hasRestElement_p: Bool = false
 
   public var unknownFields = SwiftProtobuf.UnknownStorage()
@@ -967,6 +969,8 @@ public struct Fuzzilli_Protobuf_DestructArrayAndReassign {
   // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
   // methods supported on all messages.
 
+  public var indices: [Int32] = []
+
   public var hasRestElement_p: Bool = false
 
   public var unknownFields = SwiftProtobuf.UnknownStorage()
@@ -3073,26 +3077,32 @@ extension Fuzzilli_Protobuf_Reassign: SwiftProtobuf.Message, SwiftProtobuf._Mess
 extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".DestructArray"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
-    1: .same(proto: "hasRestElement"),
+    1: .same(proto: "indices"),
+    2: .same(proto: "hasRestElement"),
   ]
 
   public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
     while let fieldNumber = try decoder.nextFieldNumber() {
       switch fieldNumber {
-      case 1: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
+      case 1: try decoder.decodeRepeatedInt32Field(value: &self.indices)
+      case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
       default: break
       }
     }
   }
 
   public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if !self.indices.isEmpty {
+      try visitor.visitPackedInt32Field(value: self.indices, fieldNumber: 1)
+    }
     if self.hasRestElement_p != false {
-      try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 1)
+      try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 2)
     }
     try unknownFields.traverse(visitor: &visitor)
   }
 
   public static func ==(lhs: Fuzzilli_Protobuf_DestructArray, rhs: Fuzzilli_Protobuf_DestructArray) -> Bool {
+    if lhs.indices != rhs.indices {return false}
     if lhs.hasRestElement_p != rhs.hasRestElement_p {return false}
     if lhs.unknownFields != rhs.unknownFields {return false}
     return true
@@ -3102,26 +3112,32 @@ extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf.
 extension Fuzzilli_Protobuf_DestructArrayAndReassign: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
   public static let protoMessageName: String = _protobuf_package + ".DestructArrayAndReassign"
   public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
-    1: .same(proto: "hasRestElement"),
+    1: .same(proto: "indices"),
+    2: .same(proto: "hasRestElement"),
   ]
 
   public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
     while let fieldNumber = try decoder.nextFieldNumber() {
       switch fieldNumber {
-      case 1: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
+      case 1: try decoder.decodeRepeatedInt32Field(value: &self.indices)
+      case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
       default: break
       }
     }
   }
 
   public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if !self.indices.isEmpty {
+      try visitor.visitPackedInt32Field(value: self.indices, fieldNumber: 1)
+    }
     if self.hasRestElement_p != false {
-      try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 1)
+      try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 2)
     }
     try unknownFields.traverse(visitor: &visitor)
   }
 
   public static func ==(lhs: Fuzzilli_Protobuf_DestructArrayAndReassign, rhs: Fuzzilli_Protobuf_DestructArrayAndReassign) -> Bool {
+    if lhs.indices != rhs.indices {return false}
     if lhs.hasRestElement_p != rhs.hasRestElement_p {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 83ec3a0bf..9d18e187b 100644
--- a/Sources/Fuzzilli/Protobuf/operations.proto
+++ b/Sources/Fuzzilli/Protobuf/operations.proto
@@ -257,11 +257,13 @@ message Reassign {
 }
 
 message DestructArray {
-    bool hasRestElement = 1;
+    repeated int32 indices = 1;
+    bool hasRestElement = 2;
 }
 
 message DestructArrayAndReassign {
-    bool hasRestElement = 1;
+    repeated int32 indices = 1;
+    bool hasRestElement = 2;
 }
 
 enum Comparator {
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index a9a60fef3..c5421c977 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -1046,19 +1046,19 @@ class LifterTests: XCTestCase {
         initialValues.append(b.loadString("World"))
         let v4 = b.createArray(with: initialValues)
 
-        b.destruct(v4, selectFirst: 3)
-        b.destruct(v4, selectFirst: 5)
+        b.destruct(v4, selecting: [0,1])
+        b.destruct(v4, selecting: [0,2,5])
 
-        b.destruct(v4, selectFirst: 2, hasRestElement: true)
+        b.destruct(v4, selecting: [0,2], hasRestElement: true)
 
         let program = b.finalize()
         let lifted_program = fuzzer.lifter.lift(program)
         let expected_program = """
         const v4 = [15,30,"Hello","World"];
-        let [v5, v6, v7] = v4;
-        let [v8, v9, v10, v11, v12] = v4;
-        let [v13, ...v14] = v4;
-        
+        let [v5, v6] = v4;
+        let [v7, ,v8, , , v9] = v4;
+        let [v10, , ...v11] = v4;
+
         """
 
         XCTAssertEqual(lifted_program,expected_program)
@@ -1077,7 +1077,7 @@ class LifterTests: XCTestCase {
         let v8 = b.loadInt(1000)
         let v9 = b.loadBuiltin("JSON")
 
-        b.destructAndReassign([v8, v9], to: v4, hasRestElement: true)
+        b.destructAndReassign([v8, v9], atIndices: [0,2], from: v4, hasRestElement: true)
 
         let program = b.finalize()
         let lifted_program = fuzzer.lifter.lift(program)
@@ -1085,7 +1085,7 @@ class LifterTests: XCTestCase {
         const v4 = [15,30,"Hello","World"];
         let v5 = 1000;
         let v6 = JSON;
-        [v5, ...v6] = v4;
+        [v5, , ...v6] = v4;
 
         """
 

From 1682f99a462c9d46bb09ba8c52ca4b9c88c6170a Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Wed, 17 Nov 2021 14:51:43 +0000
Subject: [PATCH 5/9] add helper function for destruct array operations

---
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   | 80 +++++++++----------
 1 file changed, 40 insertions(+), 40 deletions(-)

diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index 714f6efdf..4d86bf13c 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -115,6 +115,44 @@ public class JavaScriptLifter: Lifter {
                 return expr(for: instr.input(idx))
             }
 
+            // Helper function to lift destruct array operations
+            func liftArrayPattern(operation: Operation) -> String {
+                var arrayPattern = ""
+                var outputs: [String] = []
+                var outputIndex = 0
+                var indices: [Int] = []
+                var hasRestElement: Bool = false
+                switch operation {
+                    case let op as DestructArray:
+                        outputs = instr.outputs.map({ $0.identifier })
+                        indices = op.indices
+                        hasRestElement = op.hasRestElement
+                    case let op as DestructArrayAndReassign:
+                        outputs = instr.inputs.dropFirst().map({ $0.identifier })
+                        indices = op.indices
+                        hasRestElement = op.hasRestElement
+                    default:
+                        return arrayPattern
+                }
+                assert(indices.count == outputs.count)
+                if indices.count > 0 {
+                    for index in 0...indices.last! {
+                        if indices.contains(index) {
+                            if index == indices.last! {
+                                hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
+                            } else {
+                                arrayPattern.append("\(outputs[Int(outputIndex)]),")
+                            }
+                            outputIndex += 1
+                        } else {
+                            arrayPattern.append(" ,")
+                        }
+                    }
+                }
+
+                return arrayPattern
+            }
+
             // Helper functions to lift a function definition
             func liftFunctionDefinitionParameters(_ op: BeginAnyFunctionDefinition) -> String {
                 assert(instr.op === op)
@@ -420,48 +458,10 @@ public class JavaScriptLifter: Lifter {
                 w.emit(expr)
 
             case let op as DestructArray:
-                var arrayPattern: String = ""
-                if op.indices.count > 0 {
-                    let outputs = instr.outputs.map({ $0.identifier })
-                    var outputIndex = 0;
-                    assert(op.indices.count == outputs.count)
-                
-                    for index in 0...op.indices.last! {
-                        if op.indices.contains(index) {
-                            if index == op.indices.last! {
-                                op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
-                            } else {
-                                arrayPattern.append("\(outputs[Int(outputIndex)]),")
-                            }
-                            outputIndex += 1
-                        } else {
-                            arrayPattern.append(" ,")
-                        }
-                    }
-                }
-                w.emit("\(varDecl) [\(arrayPattern)] = \(input(0));")
+                w.emit("\(varDecl) [\(liftArrayPattern(operation: op))] = \(input(0));")
 
             case let op as DestructArrayAndReassign:
-                var arrayPattern: String = ""
-                if op.indices.count > 0 {
-                    let outputs = instr.inputs.dropFirst().map({ $0.identifier })
-                    var outputIndex = 0;
-                    assert(op.indices.count == outputs.count)
-                
-                    for index in 0...op.indices.last! {
-                        if op.indices.contains(index) {
-                            if index == op.indices.last! {
-                                op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
-                            } else {
-                                arrayPattern.append("\(outputs[Int(outputIndex)]),")
-                            }
-                            outputIndex += 1
-                        } else {
-                            arrayPattern.append(" ,")
-                        }
-                    }
-                }
-                w.emit("[\(arrayPattern)] = \(input(0));")
+                w.emit("[\(liftArrayPattern(operation: op))] = \(input(0));")
 
             case let op as Compare:
                 output = BinaryExpression.new() <> input(0) <> " " <> op.op.token <> " " <> input(1)

From 6b3323d60bbfedeba14e688335d3fcc609344172 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Wed, 17 Nov 2021 15:48:18 +0000
Subject: [PATCH 6/9] fix bug in FuzzIL lifting

---
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 ++--
 Tests/FuzzilliTests/LifterTest.swift        | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index 320e622fb..c3a7b185f 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -241,7 +241,7 @@ public class FuzzILLifter: Lifter {
                         if index == op.indices.last! {
                             op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
                         } else {
-                            arrayPattern.append(" \(outputs[Int(index)]),")
+                            arrayPattern.append(" \(outputs[outputIndex]),")
                         }
                         outputIndex += 1
                     } else {
@@ -263,7 +263,7 @@ public class FuzzILLifter: Lifter {
                         if index == op.indices.last! {
                             op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
                         } else {
-                            arrayPattern.append(" \(outputs[Int(index)]),")
+                            arrayPattern.append(" \(outputs[outputIndex]),")
                         }
                         outputIndex += 1
                     } else {
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index c5421c977..394584d25 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -38,7 +38,7 @@ class LifterTests: XCTestCase {
         let b = fuzzer.makeBuilder()
         let lifter = FuzzILLifter()
 
-        for _ in 0..<10 {
+        for _ in 0..<100 {
             b.generate(n: 100)
             let program = b.finalize()
 

From 1fb20d1ff6af2928fe3fc3b49b1bd4fdd88bc34e Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Fri, 19 Nov 2021 21:15:13 +0000
Subject: [PATCH 7/9] address review comments

---
 Sources/Fuzzilli/Core/CodeGenerators.swift    |  5 +-
 Sources/Fuzzilli/FuzzIL/Operations.swift      |  4 ++
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   | 72 +++++++++----------
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   | 20 ++----
 .../Fuzzilli/Mutators/OperationMutator.swift  | 16 +++++
 Sources/Fuzzilli/Protobuf/operations.pb.swift | 14 ++--
 .../FuzzilliCli/CodeGeneratorWeights.swift    |  2 +
 Tests/FuzzilliTests/LifterTest.swift          |  8 +--
 8 files changed, 77 insertions(+), 64 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index 7054eb9c2..6a0def583 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -394,8 +394,9 @@ public let CodeGenerators: [CodeGenerator] = [
     },
 
     CodeGenerator("DestructArrayGenerator", input: .iterable) { b, arr in
-        // TODO: We need a way to expose the length of arr so that element selection is accurate
-        // Pick random indices
+        // Fuzzilli generated arrays can have a length ranging from 0 to 10 elements,
+        // We want to ensure that 1) when destructing arrays we are within this length range
+        // and 2) The probability with which we select indices allows defining atleast 2-3 variables.
         var indices: [Int] = []
         for idx in 0..<Int.random(in: 0..<5) {
             withProbability(0.7) {
diff --git a/Sources/Fuzzilli/FuzzIL/Operations.swift b/Sources/Fuzzilli/FuzzIL/Operations.swift
index 9516223bd..0558ec28e 100644
--- a/Sources/Fuzzilli/FuzzIL/Operations.swift
+++ b/Sources/Fuzzilli/FuzzIL/Operations.swift
@@ -659,6 +659,8 @@ class DestructArray: Operation {
     let hasRestElement: Bool
     
     init(indices: [Int], hasRestElement: Bool) {
+        assert(indices == indices.sorted(), "Indices must be sorted in ascending order")
+        assert(indices.count == Set(indices).count, "Indices must not have duplicates")
         self.hasRestElement = hasRestElement
         self.indices = indices
         super.init(numInputs: 1, numOutputs: indices.count)
@@ -671,6 +673,8 @@ class DestructArrayAndReassign: Operation {
     let hasRestElement: Bool
 
     init(indices: [Int], hasRestElement:Bool) {
+        assert(indices == indices.sorted(), "Indices must be sorted in ascending order")
+        assert(indices.count == Set(indices).count, "Indices must not have duplicates")
         self.indices = indices
         self.hasRestElement = hasRestElement
         // The first input is the array being destructed
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index c3a7b185f..38d263c04 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -24,6 +24,36 @@ public class FuzzILLifter: Lifter {
             return instr.input(n)
         }
 
+        // Helper function to lift destruct array operations
+        func liftArrayPattern(operation: Operation) -> String {
+            var arrayPattern = ""
+            var outputs: [String] = []
+            var indices: [Int] = []
+            var hasRestElement: Bool = false
+            switch operation {
+                case let op as DestructArray:
+                    outputs = instr.outputs.map({ $0.identifier })
+                    indices = op.indices
+                    hasRestElement = op.hasRestElement
+                case let op as DestructArrayAndReassign:
+                    outputs = instr.inputs.dropFirst().map({ $0.identifier })
+                    indices = op.indices
+                    hasRestElement = op.hasRestElement
+                default:
+                    return arrayPattern
+            }
+            assert(indices.count == outputs.count)
+            var lastIndex = 0
+            for (index, output) in zip(indices, outputs) {
+                let skipped = index - lastIndex
+                lastIndex = index
+                let dots = index == indices.last! && hasRestElement ? "..." : ""
+                arrayPattern += String(repeating: ",", count: skipped) + dots + output
+            }
+
+            return arrayPattern
+        }
+
         switch instr.op {
         case let op as LoadInteger:
             w.emit("\(instr.output) <- LoadInteger '\(op.value)'")
@@ -230,48 +260,10 @@ public class FuzzILLifter: Lifter {
             w.emit("Reassign \(input(0)), \(input(1))")
 
         case let op as DestructArray:
-            var arrayPattern: String = ""
-            if op.indices.count > 0 {
-                let outputs = instr.outputs.map({ $0.identifier })
-                var outputIndex = 0
-                assert(op.indices.count == outputs.count)
-                
-                for index in 0...op.indices.last! {
-                    if op.indices.contains(index) {
-                        if index == op.indices.last! {
-                            op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
-                        } else {
-                            arrayPattern.append(" \(outputs[outputIndex]),")
-                        }
-                        outputIndex += 1
-                    } else {
-                        arrayPattern.append(",")
-                    }
-                }
-            }
-            w.emit("[\(arrayPattern)] <- DestructArray \(input(0))")
+            w.emit("[\(liftArrayPattern(operation: op))] <- DestructArray \(input(0))")
 
         case let op as DestructArrayAndReassign:
-            var arrayPattern: String = ""
-            if op.indices.count > 0 {
-                let outputs = instr.inputs.dropFirst().map({ $0.identifier })
-                var outputIndex = 0
-                assert(op.indices.count == outputs.count)
-                
-                for index in 0...op.indices.last! {
-                    if op.indices.contains(index) {
-                        if index == op.indices.last! {
-                            op.hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
-                        } else {
-                            arrayPattern.append(" \(outputs[outputIndex]),")
-                        }
-                        outputIndex += 1
-                    } else {
-                        arrayPattern.append(",")
-                    }
-                }
-            }
-            w.emit("[\(arrayPattern)] <- DestructArrayAndReassign \(input(0))")
+            w.emit("[\(liftArrayPattern(operation: op))] <- DestructArrayAndReassign \(input(0))")
 
         case let op as Compare:
             w.emit("\(instr.output) <- Compare \(input(0)), '\(op.op.token)', \(input(1))")
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index 4d86bf13c..170f94bb2 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -119,7 +119,6 @@ public class JavaScriptLifter: Lifter {
             func liftArrayPattern(operation: Operation) -> String {
                 var arrayPattern = ""
                 var outputs: [String] = []
-                var outputIndex = 0
                 var indices: [Int] = []
                 var hasRestElement: Bool = false
                 switch operation {
@@ -135,19 +134,12 @@ public class JavaScriptLifter: Lifter {
                         return arrayPattern
                 }
                 assert(indices.count == outputs.count)
-                if indices.count > 0 {
-                    for index in 0...indices.last! {
-                        if indices.contains(index) {
-                            if index == indices.last! {
-                                hasRestElement ? arrayPattern.append(" ...\(outputs[outputIndex])") : arrayPattern.append(" \(outputs[outputIndex])")
-                            } else {
-                                arrayPattern.append("\(outputs[Int(outputIndex)]),")
-                            }
-                            outputIndex += 1
-                        } else {
-                            arrayPattern.append(" ,")
-                        }
-                    }
+                var lastIndex = 0
+                for (index, output) in zip(indices, outputs) {
+                    let skipped = index - lastIndex
+                    lastIndex = index
+                    let dots = index == indices.last! && hasRestElement ? "..." : ""
+                    arrayPattern += String(repeating: ",", count: skipped) + dots + output
                 }
 
                 return arrayPattern
diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift
index f1c8923bb..7fc5fbce3 100644
--- a/Sources/Fuzzilli/Mutators/OperationMutator.swift
+++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift
@@ -117,6 +117,22 @@ public class OperationMutator: BaseInstructionMutator {
             newOp = BinaryOperation(chooseUniform(from: allBinaryOperators))
         case is ReassignWithBinop:
             newOp = ReassignWithBinop(chooseUniform(from: allBinaryOperators))
+        case let op as DestructArray:
+            var newIndices: [Int] = []
+            for idx in 0..<Int.random(in: 0..<5) {
+                withProbability(0.7) {
+                    newIndices.append(idx)
+                }
+            }
+            newOp = DestructArray(indices: newIndices, hasRestElement: !op.hasRestElement)
+        case let op as DestructArrayAndReassign:
+            var newIndices: [Int] = []
+            for idx in 0..<Int.random(in: 0..<5) {
+                withProbability(0.7) {
+                    newIndices.append(idx)
+                }
+            }
+            newOp = DestructArrayAndReassign(indices: newIndices, hasRestElement: !op.hasRestElement)
         case is Compare:
             newOp = Compare(chooseUniform(from: allComparators))
         case is LoadFromScope:
diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift
index ced3ff06d..359623730 100644
--- a/Sources/Fuzzilli/Protobuf/operations.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift
@@ -3083,9 +3083,12 @@ extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf.
 
   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 decoder.decodeRepeatedInt32Field(value: &self.indices)
-      case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
+      case 1: try { try decoder.decodeRepeatedInt32Field(value: &self.indices) }()
+      case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) }()
       default: break
       }
     }
@@ -3118,9 +3121,12 @@ extension Fuzzilli_Protobuf_DestructArrayAndReassign: SwiftProtobuf.Message, Swi
 
   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 decoder.decodeRepeatedInt32Field(value: &self.indices)
-      case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p)
+      case 1: try { try decoder.decodeRepeatedInt32Field(value: &self.indices) }()
+      case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) }()
       default: break
       }
     }
diff --git a/Sources/FuzzilliCli/CodeGeneratorWeights.swift b/Sources/FuzzilliCli/CodeGeneratorWeights.swift
index d5b2bf166..9ce7f9b27 100644
--- a/Sources/FuzzilliCli/CodeGeneratorWeights.swift
+++ b/Sources/FuzzilliCli/CodeGeneratorWeights.swift
@@ -70,6 +70,8 @@ let codeGeneratorWeights = [
     "ReassignWithBinopGenerator":               25,
     "DupGenerator":                             2,
     "ReassignmentGenerator":                    25,
+    "DestructArrayGenerator":                   2,
+    "DestructArrayAndReassignGenerator":        7,
     "WithStatementGenerator":                   5,
     "LoadFromScopeGenerator":                   4,
     "StoreToScopeGenerator":                    4,
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index 394584d25..7cbb64244 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -1055,9 +1055,9 @@ class LifterTests: XCTestCase {
         let lifted_program = fuzzer.lifter.lift(program)
         let expected_program = """
         const v4 = [15,30,"Hello","World"];
-        let [v5, v6] = v4;
-        let [v7, ,v8, , , v9] = v4;
-        let [v10, , ...v11] = v4;
+        let [v5,v6] = v4;
+        let [v7,,v8,,,v9] = v4;
+        let [v10,,...v11] = v4;
 
         """
 
@@ -1085,7 +1085,7 @@ class LifterTests: XCTestCase {
         const v4 = [15,30,"Hello","World"];
         let v5 = 1000;
         let v6 = JSON;
-        [v5, , ...v6] = v4;
+        [v5,,...v6] = v4;
 
         """
 

From c7340ae0d37951a008d4a31ae08ba673f15c4153 Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Fri, 19 Nov 2021 22:02:51 +0000
Subject: [PATCH 8/9] update proto fields

---
 Sources/Fuzzilli/Protobuf/program.pb.swift | 12 ++++++------
 Sources/Fuzzilli/Protobuf/program.proto    |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift
index d3d9d735e..e75ab6b50 100644
--- a/Sources/Fuzzilli/Protobuf/program.pb.swift
+++ b/Sources/Fuzzilli/Protobuf/program.pb.swift
@@ -1645,8 +1645,8 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     95: .same(proto: "reassignWithBinop"),
     37: .same(proto: "dup"),
     38: .same(proto: "reassign"),
-    117: .same(proto: "destructArray"),
-    118: .same(proto: "destructArrayAndReassign"),
+    116: .same(proto: "destructArray"),
+    117: .same(proto: "destructArrayAndReassign"),
     39: .same(proto: "compare"),
     96: .same(proto: "conditionalOperation"),
     40: .same(proto: "eval"),
@@ -2994,7 +2994,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .storeSuperPropertyWithBinop(v)
         }
       }()
-      case 117: try {
+      case 116: try {
         var v: Fuzzilli_Protobuf_DestructArray?
         var hadOneofValue = false
         if let current = self.operation {
@@ -3007,7 +3007,7 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
           self.operation = .destructArray(v)
         }
       }()
-      case 118: try {
+      case 117: try {
         var v: Fuzzilli_Protobuf_DestructArrayAndReassign?
         var hadOneofValue = false
         if let current = self.operation {
@@ -3436,11 +3436,11 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M
     }()
     case .destructArray?: try {
       guard case .destructArray(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 117)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 116)
     }()
     case .destructArrayAndReassign?: try {
       guard case .destructArrayAndReassign(let v)? = self.operation else { preconditionFailure() }
-      try visitor.visitSingularMessageField(value: v, fieldNumber: 118)
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 117)
     }()
     case nil: break
     }
diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto
index 2d44bd98a..2fc52e69d 100644
--- a/Sources/Fuzzilli/Protobuf/program.proto
+++ b/Sources/Fuzzilli/Protobuf/program.proto
@@ -82,8 +82,8 @@ message Instruction {
         ReassignWithBinop reassignWithBinop = 95;
         Dup dup = 37;
         Reassign reassign = 38;
-        DestructArray destructArray = 117;
-        DestructArrayAndReassign destructArrayAndReassign = 118;
+        DestructArray destructArray = 116;
+        DestructArrayAndReassign destructArrayAndReassign = 117;
         Compare compare = 39;
         ConditionalOperation conditionalOperation = 96;
         Eval eval = 40;

From 4ec8305d97bc9600c09aa69b264dbb905d5952fa Mon Sep 17 00:00:00 2001
From: amarekano <menezes.amar@gmail.com>
Date: Sun, 21 Nov 2021 17:00:45 +0000
Subject: [PATCH 9/9] address review comments 2

---
 Sources/Fuzzilli/Core/CodeGenerators.swift    |  4 +--
 Sources/Fuzzilli/Core/ProgramBuilder.swift    |  4 +--
 Sources/Fuzzilli/Lifting/FuzzILLifter.swift   | 26 +++++--------------
 .../Fuzzilli/Lifting/JavaScriptLifter.swift   | 26 +++++--------------
 .../Fuzzilli/Mutators/OperationMutator.swift  | 22 +++++++++-------
 .../FuzzilliCli/CodeGeneratorWeights.swift    |  2 +-
 Tests/FuzzilliTests/LifterTest.swift          |  2 +-
 7 files changed, 32 insertions(+), 54 deletions(-)

diff --git a/Sources/Fuzzilli/Core/CodeGenerators.swift b/Sources/Fuzzilli/Core/CodeGenerators.swift
index 6a0def583..f1074b303 100644
--- a/Sources/Fuzzilli/Core/CodeGenerators.swift
+++ b/Sources/Fuzzilli/Core/CodeGenerators.swift
@@ -395,7 +395,7 @@ public let CodeGenerators: [CodeGenerator] = [
 
     CodeGenerator("DestructArrayGenerator", input: .iterable) { b, arr in
         // Fuzzilli generated arrays can have a length ranging from 0 to 10 elements,
-        // We want to ensure that 1) when destructing arrays we are within this length range
+        // We want to ensure that 1) when destructing arrays we are usually within this length range
         // and 2) The probability with which we select indices allows defining atleast 2-3 variables.
         var indices: [Int] = []
         for idx in 0..<Int.random(in: 0..<5) {
@@ -416,7 +416,7 @@ public let CodeGenerators: [CodeGenerator] = [
                 candidates.append(b.randVar())
             }
         }
-        b.destructAndReassign(candidates, atIndices: indices, from: arr, hasRestElement: probability(0.2))
+        b.destruct(arr, selecting: indices, into: candidates, hasRestElement: probability(0.2))
     },
 
     CodeGenerator("ComparisonGenerator", inputs: (.anything, .anything)) { b, lhs, rhs in
diff --git a/Sources/Fuzzilli/Core/ProgramBuilder.swift b/Sources/Fuzzilli/Core/ProgramBuilder.swift
index 231ce3dce..96f62d3d5 100644
--- a/Sources/Fuzzilli/Core/ProgramBuilder.swift
+++ b/Sources/Fuzzilli/Core/ProgramBuilder.swift
@@ -1315,8 +1315,8 @@ public class ProgramBuilder {
         return Array(outputs)
     }
 
-    public func destructAndReassign(_ candidates: [Variable], atIndices indices: [Int], from input: Variable, hasRestElement: Bool = false) {
-        perform(DestructArrayAndReassign(indices: indices, hasRestElement: hasRestElement), withInputs: [input] + candidates)
+    public func destruct(_ input: Variable, selecting indices: [Int], into outputs: [Variable], hasRestElement: Bool) {
+        perform(DestructArrayAndReassign(indices: indices, hasRestElement: hasRestElement), withInputs: [input] + outputs)
     }
 
     @discardableResult
diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
index 38d263c04..c3966e73f 100644
--- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
+++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift
@@ -25,24 +25,10 @@ public class FuzzILLifter: Lifter {
         }
 
         // Helper function to lift destruct array operations
-        func liftArrayPattern(operation: Operation) -> String {
-            var arrayPattern = ""
-            var outputs: [String] = []
-            var indices: [Int] = []
-            var hasRestElement: Bool = false
-            switch operation {
-                case let op as DestructArray:
-                    outputs = instr.outputs.map({ $0.identifier })
-                    indices = op.indices
-                    hasRestElement = op.hasRestElement
-                case let op as DestructArrayAndReassign:
-                    outputs = instr.inputs.dropFirst().map({ $0.identifier })
-                    indices = op.indices
-                    hasRestElement = op.hasRestElement
-                default:
-                    return arrayPattern
-            }
+        func liftArrayPattern(indices: [Int], outputs: [String], hasRestElement: Bool) -> String {
             assert(indices.count == outputs.count)
+
+            var arrayPattern = ""
             var lastIndex = 0
             for (index, output) in zip(indices, outputs) {
                 let skipped = index - lastIndex
@@ -260,10 +246,12 @@ public class FuzzILLifter: Lifter {
             w.emit("Reassign \(input(0)), \(input(1))")
 
         case let op as DestructArray:
-            w.emit("[\(liftArrayPattern(operation: op))] <- DestructArray \(input(0))")
+            let outputs = instr.outputs.map({ $0.identifier })
+            w.emit("[\(liftArrayPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))] <- DestructArray \(input(0))")
 
         case let op as DestructArrayAndReassign:
-            w.emit("[\(liftArrayPattern(operation: op))] <- DestructArrayAndReassign \(input(0))")
+            let outputs = instr.inputs.dropFirst().map({ $0.identifier })        
+            w.emit("[\(liftArrayPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))] <- DestructArrayAndReassign \(input(0))")
 
         case let op as Compare:
             w.emit("\(instr.output) <- Compare \(input(0)), '\(op.op.token)', \(input(1))")
diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
index 170f94bb2..e45b170c1 100644
--- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
+++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
@@ -116,24 +116,10 @@ public class JavaScriptLifter: Lifter {
             }
 
             // Helper function to lift destruct array operations
-            func liftArrayPattern(operation: Operation) -> String {
-                var arrayPattern = ""
-                var outputs: [String] = []
-                var indices: [Int] = []
-                var hasRestElement: Bool = false
-                switch operation {
-                    case let op as DestructArray:
-                        outputs = instr.outputs.map({ $0.identifier })
-                        indices = op.indices
-                        hasRestElement = op.hasRestElement
-                    case let op as DestructArrayAndReassign:
-                        outputs = instr.inputs.dropFirst().map({ $0.identifier })
-                        indices = op.indices
-                        hasRestElement = op.hasRestElement
-                    default:
-                        return arrayPattern
-                }
+            func liftArrayPattern(indices: [Int], outputs: [String], hasRestElement: Bool) -> String {
                 assert(indices.count == outputs.count)
+
+                var arrayPattern = ""
                 var lastIndex = 0
                 for (index, output) in zip(indices, outputs) {
                     let skipped = index - lastIndex
@@ -450,10 +436,12 @@ public class JavaScriptLifter: Lifter {
                 w.emit(expr)
 
             case let op as DestructArray:
-                w.emit("\(varDecl) [\(liftArrayPattern(operation: op))] = \(input(0));")
+                let outputs = instr.outputs.map({ $0.identifier })
+                w.emit("\(varDecl) [\(liftArrayPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))] = \(input(0));")
 
             case let op as DestructArrayAndReassign:
-                w.emit("[\(liftArrayPattern(operation: op))] = \(input(0));")
+                let outputs = instr.inputs.dropFirst().map({ $0.identifier })
+                w.emit("[\(liftArrayPattern(indices: op.indices, outputs: outputs, hasRestElement: op.hasRestElement))] = \(input(0));")
 
             case let op as Compare:
                 output = BinaryExpression.new() <> input(0) <> " " <> op.op.token <> " " <> input(1)
diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift
index 7fc5fbce3..c89bbca96 100644
--- a/Sources/Fuzzilli/Mutators/OperationMutator.swift
+++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift
@@ -118,21 +118,23 @@ public class OperationMutator: BaseInstructionMutator {
         case is ReassignWithBinop:
             newOp = ReassignWithBinop(chooseUniform(from: allBinaryOperators))
         case let op as DestructArray:
-            var newIndices: [Int] = []
-            for idx in 0..<Int.random(in: 0..<5) {
-                withProbability(0.7) {
-                    newIndices.append(idx)
+            var newIndices = Set(op.indices)
+            if newIndices.count > 0 {
+                newIndices.remove(newIndices.randomElement()!)
+                while newIndices.count != op.indices.count {
+                    newIndices.insert(Int.random(in: 0..<10))
                 }
             }
-            newOp = DestructArray(indices: newIndices, hasRestElement: !op.hasRestElement)
+            newOp = DestructArray(indices: newIndices.sorted(), hasRestElement: !op.hasRestElement)
         case let op as DestructArrayAndReassign:
-            var newIndices: [Int] = []
-            for idx in 0..<Int.random(in: 0..<5) {
-                withProbability(0.7) {
-                    newIndices.append(idx)
+            var newIndices = Set(op.indices)
+            if newIndices.count > 0 {
+                newIndices.remove(newIndices.randomElement()!)
+                while newIndices.count != op.indices.count {
+                    newIndices.insert(Int.random(in: 0..<10))
                 }
             }
-            newOp = DestructArrayAndReassign(indices: newIndices, hasRestElement: !op.hasRestElement)
+            newOp = DestructArrayAndReassign(indices: newIndices.sorted(), hasRestElement: !op.hasRestElement)
         case is Compare:
             newOp = Compare(chooseUniform(from: allComparators))
         case is LoadFromScope:
diff --git a/Sources/FuzzilliCli/CodeGeneratorWeights.swift b/Sources/FuzzilliCli/CodeGeneratorWeights.swift
index 9ce7f9b27..65dd03f07 100644
--- a/Sources/FuzzilliCli/CodeGeneratorWeights.swift
+++ b/Sources/FuzzilliCli/CodeGeneratorWeights.swift
@@ -70,7 +70,7 @@ let codeGeneratorWeights = [
     "ReassignWithBinopGenerator":               25,
     "DupGenerator":                             2,
     "ReassignmentGenerator":                    25,
-    "DestructArrayGenerator":                   2,
+    "DestructArrayGenerator":                   7,
     "DestructArrayAndReassignGenerator":        7,
     "WithStatementGenerator":                   5,
     "LoadFromScopeGenerator":                   4,
diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift
index 7cbb64244..f4ec79d3d 100644
--- a/Tests/FuzzilliTests/LifterTest.swift
+++ b/Tests/FuzzilliTests/LifterTest.swift
@@ -1077,7 +1077,7 @@ class LifterTests: XCTestCase {
         let v8 = b.loadInt(1000)
         let v9 = b.loadBuiltin("JSON")
 
-        b.destructAndReassign([v8, v9], atIndices: [0,2], from: v4, hasRestElement: true)
+        b.destruct(v4, selecting: [0,2], into: [v8, v9], hasRestElement: true)
 
         let program = b.finalize()
         let lifted_program = fuzzer.lifter.lift(program)
