Create a variant of EnumSet that works when compiled to js

Fixed: 54468
Change-Id: I36d3b8789695ef6a74a32c6801ec5c8c19aae381
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/366822
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Jonas Jensen <jonasfj@google.com>
diff --git a/pkg/analyzer/lib/src/generated/utilities_collection.dart b/pkg/analyzer/lib/src/generated/utilities_collection.dart
index 5725b73..4d20795 100644
--- a/pkg/analyzer/lib/src/generated/utilities_collection.dart
+++ b/pkg/analyzer/lib/src/generated/utilities_collection.dart
@@ -2,36 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/// The set of [Enum] values, backed by [int].
-extension type EnumSet<T extends Enum>(int _bits) {
-  EnumSet.empty() : this(0);
-
-  /// Whether [constant] is present.
-  bool operator [](T constant) {
-    var index = constant.index;
-    _checkIndex(index);
-
-    var mask = 1 << index;
-    return (_bits & mask) != 0;
-  }
-
-  /// Returns a new set, with presence of [constant] updated.
-  EnumSet<T> updated(T constant, bool value) {
-    var index = constant.index;
-    _checkIndex(index);
-
-    var mask = 1 << index;
-    if (value) {
-      return EnumSet<T>(_bits | mask);
-    } else {
-      return EnumSet<T>(_bits & ~mask);
-    }
-  }
-
-  /// Throws an exception if the [index] does not fit [int].
-  static void _checkIndex(int index) {
-    if (index < 0 || index > 60) {
-      throw RangeError("Index not between 0 and 60: $index");
-    }
-  }
-}
+// Export [EnumSet] depending on the compilation target.
+export 'utilities_collection_native.dart'
+    if (dart.library.js) 'utilities_collection_js.dart';
diff --git a/pkg/analyzer/lib/src/generated/utilities_collection_js.dart b/pkg/analyzer/lib/src/generated/utilities_collection_js.dart
new file mode 100644
index 0000000..ef81378
--- /dev/null
+++ b/pkg/analyzer/lib/src/generated/utilities_collection_js.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Provides [EnumSet] which works when compiled to JS.
+library;
+
+/// The set of [Enum] values, up to `60` constants.
+extension type EnumSet<T extends Enum>((int, int) _bits) {
+  EnumSet.empty() : this((0, 0));
+
+  /// Whether [constant] is present.
+  bool operator [](T constant) {
+    var index = constant.index;
+    _checkIndex(index);
+
+    // In JavaScript bitwise operations are performed on 32-bit integers.
+    if (index <= 30) {
+      var mask = 1 << index;
+      return (_bits.$1 & mask) != 0;
+    } else {
+      var mask = 1 << (index - 30);
+      return (_bits.$2 & mask) != 0;
+    }
+  }
+
+  /// Returns a new set, with presence of [constant] updated.
+  EnumSet<T> updated(T constant, bool value) {
+    var index = constant.index;
+    _checkIndex(index);
+
+    if (index <= 30) {
+      var mask = 1 << index;
+      var field = _bits.$1;
+      var newField = value ? field | mask : field & ~mask;
+      return EnumSet<T>((newField, _bits.$2));
+    } else {
+      var mask = 1 << (index - 30);
+      var field = _bits.$2;
+      var newField = value ? field | mask : field & ~mask;
+      return EnumSet<T>((_bits.$1, newField));
+    }
+  }
+
+  /// Throws an exception if the [index] does not fit the storage.
+  static void _checkIndex(int index) {
+    if (index < 0 || index > 60) {
+      throw RangeError("Index not between 0 and 60: $index");
+    }
+  }
+}
diff --git a/pkg/analyzer/lib/src/generated/utilities_collection_native.dart b/pkg/analyzer/lib/src/generated/utilities_collection_native.dart
new file mode 100644
index 0000000..b30c0a6
--- /dev/null
+++ b/pkg/analyzer/lib/src/generated/utilities_collection_native.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Provides efficient [EnumSet] which only works when 64 bit integers are
+/// available.
+library;
+
+/// The set of [Enum] values, backed by [int].
+extension type EnumSet<T extends Enum>(int _bits) {
+  EnumSet.empty() : this(0);
+
+  /// Whether [constant] is present.
+  bool operator [](T constant) {
+    var index = constant.index;
+    _checkIndex(index);
+
+    var mask = 1 << index;
+    return (_bits & mask) != 0;
+  }
+
+  /// Returns a new set, with presence of [constant] updated.
+  EnumSet<T> updated(T constant, bool value) {
+    var index = constant.index;
+    _checkIndex(index);
+
+    var mask = 1 << index;
+    if (value) {
+      return EnumSet<T>(_bits | mask);
+    } else {
+      return EnumSet<T>(_bits & ~mask);
+    }
+  }
+
+  /// Throws an exception if the [index] does not fit [int].
+  static void _checkIndex(int index) {
+    if (index < 0 || index > 60) {
+      throw RangeError("Index not between 0 and 60: $index");
+    }
+  }
+}