[dart2wasm] Implement set literals in the backend

Change-Id: I678a6c16248bfafce2e39c6c3e39505109250f35
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242861
Reviewed-by: Joshua Litt <joshualitt@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 821c38f..4a0d1c3 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -1976,6 +1976,34 @@
   }
 
   @override
+  w.ValueType visitSetLiteral(SetLiteral node, w.ValueType expectedType) {
+    w.BaseFunction setFactory =
+        translator.functions.getFunction(translator.setFactory.reference);
+    w.ValueType factoryReturnType = setFactory.type.outputs.single;
+    types.makeType(this, node.typeArgument, node);
+    b.call(setFactory);
+    if (node.expressions.isEmpty) {
+      return factoryReturnType;
+    }
+    w.BaseFunction setAdd =
+        translator.functions.getFunction(translator.setAdd.reference);
+    w.ValueType addReceiverType = setAdd.type.inputs[0];
+    w.ValueType addKeyType = setAdd.type.inputs[1];
+    w.Local setLocal = addLocal(addReceiverType);
+    translator.convertType(function, factoryReturnType, setLocal.type);
+    b.local_set(setLocal);
+    for (Expression element in node.expressions) {
+      b.local_get(setLocal);
+      translator.convertType(function, setLocal.type, addReceiverType);
+      wrap(element, addKeyType);
+      b.call(setAdd);
+      b.drop();
+    }
+    b.local_get(setLocal);
+    return setLocal.type;
+  }
+
+  @override
   w.ValueType visitTypeLiteral(TypeLiteral node, w.ValueType expectedType) {
     return types.makeType(this, node.type, node);
   }
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index 7a1a84a..412bd0b 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -596,13 +596,6 @@
     translator.functions.allocateClass(info.classId);
     w.RefType type = info.nonNullableType;
     return createConstant(constant, type, (function, b) {
-      // This computation of the hash mask follows the computations in
-      // [_ImmutableLinkedHashMapMixin._createIndex] and
-      // [_HashBase._indexSizeToHashMask].
-      const int initialIndexSize = 8;
-      final int indexSize = max(dataElements.length, initialIndexSize);
-      final int hashMask = (1 << (31 - (indexSize - 1).bitLength)) - 1;
-
       w.RefType indexType =
           info.struct.fields[FieldIndex.hashBaseIndex].type as w.RefType;
       w.RefType dataType =
@@ -611,7 +604,7 @@
       b.i32_const(info.classId);
       b.i32_const(initialIdentityHash);
       b.ref_null(indexType.heapType); // _index
-      b.i64_const(hashMask); // _hashMask
+      b.i64_const(_computeHashMask(constant.entries.length)); // _hashMask
       constants.instantiateConstant(function, b, dataList, dataType); // _data
       b.i64_const(dataElements.length); // _usedData
       b.i64_const(0); // _deletedKeys
@@ -624,6 +617,46 @@
   }
 
   @override
+  ConstantInfo? visitSetConstant(SetConstant constant) {
+    Constant elementTypeConstant = TypeLiteralConstant(constant.typeArgument);
+    ensureConstant(elementTypeConstant);
+    ListConstant dataList = ListConstant(const DynamicType(), constant.entries);
+    ensureConstant(dataList);
+
+    ClassInfo info = translator.classInfo[translator.immutableSetClass]!;
+    translator.functions.allocateClass(info.classId);
+    w.RefType type = info.nonNullableType;
+    return createConstant(constant, type, (function, b) {
+      w.RefType indexType =
+          info.struct.fields[FieldIndex.hashBaseIndex].type as w.RefType;
+      w.RefType dataType =
+          info.struct.fields[FieldIndex.hashBaseData].type as w.RefType;
+
+      b.i32_const(info.classId);
+      b.i32_const(initialIdentityHash);
+      b.ref_null(indexType.heapType); // _index
+      b.i64_const(_computeHashMask(constant.entries.length)); // _hashMask
+      constants.instantiateConstant(function, b, dataList, dataType); // _data
+      b.i64_const(constant.entries.length); // _usedData
+      b.i64_const(0); // _deletedKeys
+      constants.instantiateConstant(
+          function, b, elementTypeConstant, constants.typeInfo.nullableType);
+      translator.struct_new(b, info);
+    });
+  }
+
+  int _computeHashMask(int entries) {
+    // This computation of the hash mask follows the computations in
+    // [_ImmutableLinkedHashMapMixin._createIndex],
+    // [_ImmutableLinkedHashSetMixin._createIndex] and
+    // [_HashBase._indexSizeToHashMask].
+    const int initialIndexSize = 8;
+    final int indexSize = max(entries * 2, initialIndexSize);
+    final int hashMask = (1 << (31 - (indexSize - 1).bitLength)) - 1;
+    return hashMask;
+  }
+
+  @override
   ConstantInfo? visitStaticTearOffConstant(StaticTearOffConstant constant) {
     w.DefinedFunction closureFunction =
         translator.getTearOffFunction(constant.targetReference.asProcedure);
diff --git a/pkg/dart2wasm/lib/constants_backend.dart b/pkg/dart2wasm/lib/constants_backend.dart
deleted file mode 100644
index 17e4bf4..0000000
--- a/pkg/dart2wasm/lib/constants_backend.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2022, 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.
-
-import 'package:kernel/ast.dart';
-import 'package:kernel/target/targets.dart';
-import 'package:kernel/core_types.dart';
-
-class WasmConstantsBackend extends ConstantsBackend {
-  final Class unmodifiableSetClass;
-  final Field unmodifiableSetMap;
-
-  WasmConstantsBackend._(this.unmodifiableSetMap, this.unmodifiableSetClass);
-
-  factory WasmConstantsBackend(CoreTypes coreTypes) {
-    Field unmodifiableSetMap =
-        coreTypes.index.getField('dart:collection', '_UnmodifiableSet', '_map');
-
-    return new WasmConstantsBackend._(
-        unmodifiableSetMap, unmodifiableSetMap.enclosingClass!);
-  }
-
-  @override
-  Constant lowerSetConstant(SetConstant constant) {
-    final DartType elementType = constant.typeArgument;
-    final List<Constant> entries = constant.entries;
-    final List<ConstantMapEntry> mapEntries =
-        new List<ConstantMapEntry>.generate(entries.length, (int index) {
-      return new ConstantMapEntry(entries[index], new NullConstant());
-    });
-    Constant map = lowerMapConstant(
-        new MapConstant(elementType, const NullType(), mapEntries));
-    return new InstanceConstant(unmodifiableSetClass.reference, [elementType],
-        <Reference, Constant>{unmodifiableSetMap.getterReference: map});
-  }
-
-  @override
-  bool isLoweredSetConstant(Constant constant) {
-    if (constant is InstanceConstant &&
-        constant.classNode == unmodifiableSetClass) {
-      InstanceConstant instance = constant;
-      return isLoweredMapConstant(
-          instance.fieldValues[unmodifiableSetMap.getterReference]!);
-    }
-    return false;
-  }
-
-  @override
-  void forEachLoweredSetConstantElement(
-      Constant constant, void Function(Constant element) f) {
-    assert(isLoweredSetConstant(constant));
-    final InstanceConstant instance = constant as InstanceConstant;
-    final Constant mapConstant =
-        instance.fieldValues[unmodifiableSetMap.getterReference]!;
-    forEachLoweredMapConstantEntry(mapConstant, (Constant key, Constant value) {
-      f(key);
-    });
-  }
-}
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index bb31c61..84535f6 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -168,7 +168,7 @@
     }
 
     // _HashAbstractImmutableBase._indexNullable
-    if (target == translator.immutableMapIndexNullable) {
+    if (target == translator.hashImmutableIndexNullable) {
       ClassInfo info = translator.classInfo[translator.hashFieldBaseClass]!;
       codeGen.wrap(receiver, info.nullableType);
       b.struct_get(info.struct, FieldIndex.hashBaseIndex);
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index 2a2f425..b2c8a13 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -21,21 +21,20 @@
 import 'package:vm/transformations/ffi/use_sites.dart' as transformFfiUseSites
     show transformLibraries;
 
-import 'package:dart2wasm/constants_backend.dart';
 import 'package:dart2wasm/transformers.dart' as wasmTrans;
 
 class WasmTarget extends Target {
   Class? _growableList;
   Class? _immutableList;
   Class? _wasmImmutableLinkedHashMap;
-  Class? _unmodifiableSet;
+  Class? _wasmImmutableLinkedHashSet;
   Class? _compactLinkedCustomHashMap;
-  Class? _compactLinkedHashSet;
+  Class? _compactLinkedCustomHashSet;
   Class? _oneByteString;
   Class? _twoByteString;
 
   @override
-  late final ConstantsBackend constantsBackend;
+  ConstantsBackend get constantsBackend => const ConstantsBackend();
 
   @override
   String get name => 'wasm';
@@ -85,7 +84,6 @@
       DiagnosticReporter diagnosticReporter,
       {void Function(String msg)? logger,
       ChangedStructureNotifier? changedStructureNotifier}) {
-    constantsBackend = WasmConstantsBackend(coreTypes);
     _patchHostEndian(coreTypes);
   }
 
@@ -167,7 +165,7 @@
   }
 
   @override
-  bool get supportsSetLiterals => false;
+  bool get supportsSetLiterals => true;
 
   @override
   int get enabledLateLowerings => LateLowering.all;
@@ -213,14 +211,14 @@
 
   @override
   Class concreteSetLiteralClass(CoreTypes coreTypes) {
-    return _compactLinkedHashSet ??=
-        coreTypes.index.getClass('dart:collection', '_CompactLinkedHashSet');
+    return _compactLinkedCustomHashSet ??= coreTypes.index
+        .getClass('dart:collection', '_CompactLinkedCustomHashSet');
   }
 
   @override
   Class concreteConstSetLiteralClass(CoreTypes coreTypes) {
-    return _unmodifiableSet ??=
-        coreTypes.index.getClass('dart:collection', '_UnmodifiableSet');
+    return _wasmImmutableLinkedHashSet ??= coreTypes.index
+        .getClass('dart:collection', '_WasmImmutableLinkedHashSet');
   }
 
   @override
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index d6711a2..fa258cf 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -83,6 +83,7 @@
   late final Class growableListClass;
   late final Class immutableListClass;
   late final Class immutableMapClass;
+  late final Class immutableSetClass;
   late final Class hashFieldBaseClass;
   late final Class stringBaseClass;
   late final Class oneByteStringClass;
@@ -105,7 +106,9 @@
   late final Procedure throwWasmRefError;
   late final Procedure mapFactory;
   late final Procedure mapPut;
-  late final Procedure immutableMapIndexNullable;
+  late final Procedure setFactory;
+  late final Procedure setAdd;
+  late final Procedure hashImmutableIndexNullable;
   late final Map<Class, w.StorageType> builtinTypes;
   late final Map<w.ValueType, Class> boxedClasses;
 
@@ -183,6 +186,7 @@
     growableListClass = lookupCore("_GrowableList");
     immutableListClass = lookupCore("_ImmutableList");
     immutableMapClass = lookupCollection("_WasmImmutableLinkedHashMap");
+    immutableSetClass = lookupCollection("_WasmImmutableLinkedHashSet");
     hashFieldBaseClass = lookupCollection("_HashFieldBase");
     stringBaseClass = lookupCore("_StringBase");
     oneByteStringClass = lookupCore("_OneByteString");
@@ -216,7 +220,13 @@
         .superclass! // _LinkedHashMapMixin<K, V>
         .procedures
         .firstWhere((p) => p.name.text == "[]=");
-    immutableMapIndexNullable = lookupCollection("_HashAbstractImmutableBase")
+    setFactory = lookupCollection("LinkedHashSet").procedures.firstWhere(
+        (p) => p.kind == ProcedureKind.Factory && p.name.text == "_default");
+    setAdd = lookupCollection("_CompactLinkedCustomHashSet")
+        .superclass! // _LinkedHashSetMixin<K, V>
+        .procedures
+        .firstWhere((p) => p.name.text == "add");
+    hashImmutableIndexNullable = lookupCollection("_HashAbstractImmutableBase")
         .procedures
         .firstWhere((p) => p.name.text == "_indexNullable");
     builtinTypes = {
diff --git a/sdk/lib/_internal/vm/lib/compact_hash.dart b/sdk/lib/_internal/vm/lib/compact_hash.dart
index c49bb48..003be2c 100644
--- a/sdk/lib/_internal/vm/lib/compact_hash.dart
+++ b/sdk/lib/_internal/vm/lib/compact_hash.dart
@@ -1096,6 +1096,9 @@
   E? lookup(Object? o) => _validKey(o) ? super.lookup(o) : null;
   bool remove(Object? o) => _validKey(o) ? super.remove(o) : false;
 
+  @pragma("wasm:entry-point")
+  bool add(E key);
+
   _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey)
       : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test;
 
diff --git a/sdk/lib/_internal/wasm/lib/hash_factories.dart b/sdk/lib/_internal/wasm/lib/hash_factories.dart
index 8159f09..c0f6160 100644
--- a/sdk/lib/_internal/wasm/lib/hash_factories.dart
+++ b/sdk/lib/_internal/wasm/lib/hash_factories.dart
@@ -46,6 +46,10 @@
     return new _CompactLinkedCustomHashSet<E>(equals, hashCode, isValidKey);
   }
 
+  @pragma("wasm:entry-point")
+  factory LinkedHashSet._default() =>
+      _CompactLinkedCustomHashSet<E>(_defaultEquals, _defaultHashCode, null);
+
   @patch
   factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
 }
@@ -70,3 +74,19 @@
         "Immutable maps can only be instantiated via constants");
   }
 }
+
+@pragma("wasm:entry-point")
+class _WasmImmutableLinkedHashSet<E> extends _HashWasmImmutableBase
+    with
+        SetMixin<E>,
+        _HashBase,
+        _OperatorEqualsAndHashCode,
+        _LinkedHashSetMixin<E>,
+        _UnmodifiableSetMixin<E>,
+        _ImmutableLinkedHashSetMixin<E>
+    implements LinkedHashSet<E> {
+  factory _WasmImmutableLinkedHashSet._uninstantiable() {
+    throw new UnsupportedError(
+        "Immutable sets can only be instantiated via constants");
+  }
+}