[set-methods] Add isSupersetOf method

This CL adds the generic path for isSupersetOf method
to set methods.

Bug: v8:13556
Change-Id: I7a4d3285b3dbcce6502989a9a0cde7efddf998ee
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4656305
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#89258}
diff --git a/BUILD.bazel b/BUILD.bazel
index 9fdc895..f88edf2 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -904,6 +904,7 @@
         "src/builtins/set-difference.tq",
         "src/builtins/set-intersection.tq",
         "src/builtins/set-is-subset-of.tq",
+        "src/builtins/set-is-superset-of.tq",
         "src/builtins/set-symmetric-difference.tq",
         "src/builtins/set-union.tq",
         "src/builtins/string-at.tq",
diff --git a/BUILD.gn b/BUILD.gn
index b177099..72e11bf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1913,6 +1913,7 @@
   "src/builtins/set-difference.tq",
   "src/builtins/set-intersection.tq",
   "src/builtins/set-is-subset-of.tq",
+  "src/builtins/set-is-superset-of.tq",
   "src/builtins/set-symmetric-difference.tq",
   "src/builtins/set-union.tq",
   "src/builtins/string-at.tq",
diff --git a/src/builtins/set-is-superset-of.tq b/src/builtins/set-is-superset-of.tq
new file mode 100644
index 0000000..0d0e173
--- /dev/null
+++ b/src/builtins/set-is-superset-of.tq
@@ -0,0 +1,66 @@
+// Copyright 2023 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+namespace collections {
+
+// https://tc39.es/proposal-set-methods/#sec-set.prototype.issupersetof
+transitioning javascript builtin SetPrototypeIsSupersetOf(
+    js-implicit context: NativeContext,
+    receiver: JSAny)(other: JSAny): Boolean {
+  const methodName: constexpr string = 'Set.prototype.isSupersetOf';
+  const fastIteratorResultMap = GetIteratorResultMap();
+
+  // 1. Let O be the this value.
+  // 2. Perform ? RequireInternalSlot(O, [[SetData]]).
+  const o = Cast<JSSet>(receiver) otherwise
+  ThrowTypeError(
+      MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
+
+  const table = Cast<OrderedHashSet>(o.table) otherwise unreachable;
+
+  // 3. Let otherRec be ? GetSetRecord(other).
+  let otherRec = GetSetRecord(other, methodName);
+
+  // 4. Let thisSize be the number of elements in O.[[SetData]].
+  const thisSize =
+      LoadOrderedHashTableMetadata(table, kOrderedHashSetNumberOfElementsIndex);
+
+  // 5. If thisSize < otherRec.[[Size]], return false.
+  if (thisSize < Convert<int32>(otherRec.size)) {
+    return False;
+  }
+
+  // 6. Let keysIter be ? GetKeysIterator(otherRec).
+  let keysIter =
+      GetKeysIterator(otherRec.object, UnsafeCast<Callable>(otherRec.keys));
+
+  // 7. Let next be true.
+  let nextRecord: JSReceiver;
+
+  // 8. Repeat, while next is not false,
+  while (true) {
+    //   a. Set next to ? IteratorStep(keysIter).
+    try {
+      nextRecord = iterator::IteratorStep(keysIter, fastIteratorResultMap)
+          otherwise Done;
+    } label Done {
+      // 9. Return true.
+      return True;
+    }
+    //   b. If next is not false, then
+    //      i. Let nextValue be ? IteratorValue(next).
+    const nextValue =
+        iterator::IteratorValue(nextRecord, fastIteratorResultMap);
+    //      ii. If SetDataHas(O.[[SetData]], nextValue) is false, then
+    if (!TableHasKey(table, nextValue)) {
+      //          1. Perform ? IteratorClose(keysIter,
+      //          NormalCompletion(unused)).
+      //          2. Return false.
+      iterator::IteratorClose(keysIter);
+      return False;
+    }
+  }
+  unreachable;
+}
+}
diff --git a/src/init/bootstrapper.cc b/src/init/bootstrapper.cc
index 1c4a4a9..d0a7a1a 100644
--- a/src/init/bootstrapper.cc
+++ b/src/init/bootstrapper.cc
@@ -4728,6 +4728,8 @@
                         Builtin::kSetPrototypeSymmetricDifference, 1, true);
   SimpleInstallFunction(isolate(), set_prototype, "isSubsetOf",
                         Builtin::kSetPrototypeIsSubsetOf, 1, true);
+  SimpleInstallFunction(isolate(), set_prototype, "isSupersetOf",
+                        Builtin::kSetPrototypeIsSupersetOf, 1, true);
 }
 
 void Genesis::InitializeGlobal_harmony_json_parse_with_source() {
diff --git a/test/mjsunit/harmony/set-is-superset-of.js b/test/mjsunit/harmony/set-is-superset-of.js
new file mode 100644
index 0000000..33dc262
--- /dev/null
+++ b/test/mjsunit/harmony/set-is-superset-of.js
@@ -0,0 +1,170 @@
+// Copyright 2023 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Flags: --harmony-set-methods
+
+(function TestIsSupersetOfSetFirstShorter() {
+  const firstSet = new Set();
+  firstSet.add(42);
+
+  const otherSet = new Set();
+  otherSet.add(42);
+  otherSet.add(44);
+
+  assertEquals(firstSet.isSupersetOf(otherSet), false);
+})();
+
+(function TestIsSupersetOfSetSecondShorterIsSuperset() {
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  const otherSet = new Set();
+  otherSet.add(42);
+
+  assertEquals(firstSet.isSupersetOf(otherSet), true);
+})();
+
+(function TestIsSupersetOfSetSecondShorterIsNotSuperset() {
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  const otherSet = new Set();
+  otherSet.add(46);
+
+  assertEquals(firstSet.isSupersetOf(otherSet), false);
+})();
+
+(function TestIsSupersetOfMapFirstShorter() {
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  const other = new Map();
+  other.set(42);
+  other.set(43);
+  other.set(47);
+
+  assertEquals(firstSet.isSupersetOf(other), false);
+})();
+
+(function TestIsSupersetOfMapSecondShorterIsSuperset() {
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  const other = new Map();
+  other.set(42);
+
+  assertEquals(firstSet.isSupersetOf(other), true);
+})();
+
+(function TestIsSupersetOfMapSecondShorterIsNotSuperset() {
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  const other = new Map();
+  other.set(44);
+
+  assertEquals(firstSet.isSupersetOf(other), false);
+})();
+
+(function TestIsSupersetOfSetLikeObjectFirstShorter() {
+  const SetLike = {
+    arr: [42, 44, 45],
+    size: 3,
+    keys() {
+      return this.arr[Symbol.iterator]();
+    },
+    has(key) {
+      return this.arr.indexOf(key) != -1;
+    }
+  };
+
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(45);
+
+  assertEquals(firstSet.isSupersetOf(SetLike), false);
+})();
+
+(function TestIsSupersetOfSetLikeObjectFirstShorterIsSuperset() {
+  const SetLike = {
+    arr: [42],
+    size: 1,
+    keys() {
+      return this.arr[Symbol.iterator]();
+    },
+    has(key) {
+      return this.arr.indexOf(key) != -1;
+    }
+  };
+
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  assertEquals(firstSet.isSupersetOf(SetLike), true);
+})();
+
+(function TestIsSupersetOfSetLikeObjectFirstShorterIsNotSuperset() {
+  const SetLike = {
+    arr: [44],
+    size: 1,
+    keys() {
+      return this.arr[Symbol.iterator]();
+    },
+    has(key) {
+      return this.arr.indexOf(key) != -1;
+    }
+  };
+
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+
+  assertEquals(firstSet.isSupersetOf(SetLike), false);
+})();
+
+(function TestIsSupersetOfSetEqualLengthIsSuperset() {
+  const SetLike = {
+    arr: [42, 43, 45],
+    size: 3,
+    keys() {
+      return this.arr[Symbol.iterator]();
+    },
+    has(key) {
+      return this.arr.indexOf(key) != -1;
+    }
+  };
+
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+  firstSet.add(45);
+
+  assertEquals(firstSet.isSupersetOf(SetLike), true);
+})();
+
+(function TestIsSupersetOfSetEqualLengthIsNotSuperset() {
+  const SetLike = {
+    arr: [42, 44, 45],
+    size: 3,
+    keys() {
+      return this.arr[Symbol.iterator]();
+    },
+    has(key) {
+      return this.arr.indexOf(key) != -1;
+    }
+  };
+
+  const firstSet = new Set();
+  firstSet.add(42);
+  firstSet.add(43);
+  firstSet.add(45);
+
+  assertEquals(firstSet.isSupersetOf(SetLike), false);
+})();