Version 2.17.0-214.0.dev

Merge commit '5317e4c605d56ab7297ce9e4d92c68d81064b693' into 'dev'
diff --git a/pkg/dart2wasm/lib/class_info.dart b/pkg/dart2wasm/lib/class_info.dart
index 54037e0..e420c22 100644
--- a/pkg/dart2wasm/lib/class_info.dart
+++ b/pkg/dart2wasm/lib/class_info.dart
@@ -21,6 +21,8 @@
   static const boxValue = 1;
   static const identityHash = 1;
   static const stringArray = 2;
+  static const hashBaseIndex = 2;
+  static const hashBaseData = 4;
   static const closureContext = 2;
   static const closureFunction = 3;
   static const typedListBaseLength = 2;
@@ -45,6 +47,8 @@
     check(translator.boxedDoubleClass, "value", FieldIndex.boxValue);
     check(translator.oneByteStringClass, "_array", FieldIndex.stringArray);
     check(translator.twoByteStringClass, "_array", FieldIndex.stringArray);
+    check(translator.hashFieldBaseClass, "_index", FieldIndex.hashBaseIndex);
+    check(translator.hashFieldBaseClass, "_data", FieldIndex.hashBaseData);
     check(translator.functionClass, "context", FieldIndex.closureContext);
   }
 }
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index 789f070..4440b45 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -2,6 +2,7 @@
 // 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 'dart:math';
 import 'dart:typed_data';
 
 import 'package:dart2wasm/class_info.dart';
@@ -578,6 +579,51 @@
   }
 
   @override
+  ConstantInfo? visitMapConstant(MapConstant constant) {
+    Constant keyTypeConstant = TypeLiteralConstant(constant.keyType);
+    ensureConstant(keyTypeConstant);
+    Constant valueTypeConstant = TypeLiteralConstant(constant.valueType);
+    ensureConstant(valueTypeConstant);
+    List<Constant> dataElements =
+        List.generate(constant.entries.length * 2, (i) {
+      ConstantMapEntry entry = constant.entries[i >> 1];
+      return i.isEven ? entry.key : entry.value;
+    });
+    ListConstant dataList = ListConstant(const DynamicType(), dataElements);
+    ensureConstant(dataList);
+
+    ClassInfo info = translator.classInfo[translator.immutableMapClass]!;
+    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 =
+          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(hashMask); // _hashMask
+      constants.instantiateConstant(function, b, dataList, dataType); // _data
+      b.i64_const(dataElements.length); // _usedData
+      b.i64_const(0); // _deletedKeys
+      constants.instantiateConstant(
+          function, b, keyTypeConstant, constants.typeInfo.nullableType);
+      constants.instantiateConstant(
+          function, b, valueTypeConstant, constants.typeInfo.nullableType);
+      translator.struct_new(b, info);
+    });
+  }
+
+  @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
index 6871599..17e4bf4 100644
--- a/pkg/dart2wasm/lib/constants_backend.dart
+++ b/pkg/dart2wasm/lib/constants_backend.dart
@@ -7,68 +7,17 @@
 import 'package:kernel/core_types.dart';
 
 class WasmConstantsBackend extends ConstantsBackend {
-  final Class immutableMapClass;
   final Class unmodifiableSetClass;
   final Field unmodifiableSetMap;
 
-  WasmConstantsBackend._(this.immutableMapClass, this.unmodifiableSetMap,
-      this.unmodifiableSetClass);
+  WasmConstantsBackend._(this.unmodifiableSetMap, this.unmodifiableSetClass);
 
   factory WasmConstantsBackend(CoreTypes coreTypes) {
-    final Library coreLibrary = coreTypes.coreLibrary;
-    final Class immutableMapClass = coreLibrary.classes
-        .firstWhere((Class klass) => klass.name == '_ImmutableMap');
     Field unmodifiableSetMap =
         coreTypes.index.getField('dart:collection', '_UnmodifiableSet', '_map');
 
-    return new WasmConstantsBackend._(immutableMapClass, unmodifiableSetMap,
-        unmodifiableSetMap.enclosingClass!);
-  }
-
-  @override
-  Constant lowerMapConstant(MapConstant constant) {
-    // The _ImmutableMap class is implemented via one field pointing to a list
-    // of key/value pairs -- see runtime/lib/immutable_map.dart!
-    final List<Constant> kvListPairs =
-        new List<Constant>.generate(2 * constant.entries.length, (int i) {
-      final int index = i ~/ 2;
-      final ConstantMapEntry entry = constant.entries[index];
-      return i % 2 == 0 ? entry.key : entry.value;
-    });
-    // This is a bit fishy, since we merge the key and the value type by
-    // putting both into the same list.
-    final ListConstant kvListConstant =
-        new ListConstant(const DynamicType(), kvListPairs);
-    assert(immutableMapClass.fields.length == 1);
-    final Field kvPairListField = immutableMapClass.fields[0];
-    return new InstanceConstant(immutableMapClass.reference, <DartType>[
-      constant.keyType,
-      constant.valueType,
-    ], <Reference, Constant>{
-      // We use getterReference as we refer to the field itself.
-      kvPairListField.getterReference: kvListConstant,
-    });
-  }
-
-  @override
-  bool isLoweredMapConstant(Constant constant) {
-    return constant is InstanceConstant &&
-        constant.classNode == immutableMapClass;
-  }
-
-  @override
-  void forEachLoweredMapConstantEntry(
-      Constant constant, void Function(Constant key, Constant value) f) {
-    assert(isLoweredMapConstant(constant));
-    final InstanceConstant instance = constant as InstanceConstant;
-    assert(immutableMapClass.fields.length == 1);
-    final Field kvPairListField = immutableMapClass.fields[0];
-    final ListConstant kvListConstant =
-        instance.fieldValues[kvPairListField.getterReference] as ListConstant;
-    assert(kvListConstant.entries.length % 2 == 0);
-    for (int index = 0; index < kvListConstant.entries.length; index += 2) {
-      f(kvListConstant.entries[index], kvListConstant.entries[index + 1]);
-    }
+    return new WasmConstantsBackend._(
+        unmodifiableSetMap, unmodifiableSetMap.enclosingClass!);
   }
 
   @override
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index e795d85..002daa3 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -145,6 +145,13 @@
       return w.NumType.i64;
     }
 
+    if (node.interfaceTarget == translator.immutableMapIndexNullable) {
+      ClassInfo info = translator.classInfo[translator.hashFieldBaseClass]!;
+      codeGen.wrap(node.receiver, info.nullableType);
+      b.struct_get(info.struct, FieldIndex.hashBaseIndex);
+      return info.struct.fields[FieldIndex.hashBaseIndex].type.unpacked;
+    }
+
     return null;
   }
 
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index fa68680..76623cc 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -18,7 +18,7 @@
 class WasmTarget extends Target {
   Class? _growableList;
   Class? _immutableList;
-  Class? _immutableMap;
+  Class? _wasmImmutableLinkedHashMap;
   Class? _unmodifiableSet;
   Class? _compactLinkedCustomHashMap;
   Class? _compactLinkedHashSet;
@@ -157,8 +157,8 @@
 
   @override
   Class concreteConstMapLiteralClass(CoreTypes coreTypes) {
-    return _immutableMap ??=
-        coreTypes.index.getClass('dart:collection', '_ImmutableMap');
+    return _wasmImmutableLinkedHashMap ??= coreTypes.index
+        .getClass('dart:collection', '_WasmImmutableLinkedHashMap');
   }
 
   @override
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 1630176..e34f5ba 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -77,6 +77,8 @@
   late final Class fixedLengthListClass;
   late final Class growableListClass;
   late final Class immutableListClass;
+  late final Class immutableMapClass;
+  late final Class hashFieldBaseClass;
   late final Class stringBaseClass;
   late final Class oneByteStringClass;
   late final Class twoByteStringClass;
@@ -91,6 +93,7 @@
   late final Procedure stringInterpolate;
   late final Procedure mapFactory;
   late final Procedure mapPut;
+  late final Procedure immutableMapIndexNullable;
   late final Map<Class, w.StorageType> builtinTypes;
   late final Map<w.ValueType, Class> boxedClasses;
 
@@ -172,6 +175,8 @@
     listBaseClass = lookupCore("_ListBase");
     growableListClass = lookupCore("_GrowableList");
     immutableListClass = lookupCore("_ImmutableList");
+    immutableMapClass = lookupCollection("_WasmImmutableLinkedHashMap");
+    hashFieldBaseClass = lookupCollection("_HashFieldBase");
     stringBaseClass = lookupCore("_StringBase");
     oneByteStringClass = lookupCore("_OneByteString");
     twoByteStringClass = lookupCore("_TwoByteString");
@@ -190,10 +195,12 @@
     mapFactory = lookupCollection("LinkedHashMap").procedures.firstWhere(
         (p) => p.kind == ProcedureKind.Factory && p.name.text == "_default");
     mapPut = lookupCollection("_CompactLinkedCustomHashMap")
-        .superclass! // _HashBase
         .superclass! // _LinkedHashMapMixin<K, V>
         .procedures
         .firstWhere((p) => p.name.text == "[]=");
+    immutableMapIndexNullable = lookupCollection("_HashAbstractImmutableBase")
+        .procedures
+        .firstWhere((p) => p.name.text == "_indexNullable");
     builtinTypes = {
       coreTypes.boolClass: w.NumType.i32,
       coreTypes.intClass: w.NumType.i64,
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_case1.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_case1.dart.expect
index 4a919d4..63b00b0 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_case1.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_case1.dart.expect
@@ -15,7 +15,7 @@
     : self::InheritedElement::_dependents = <self::Element, core::Object?>{}, super self::Element::•()
     ;
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3]  method setDependencies([@vm.inferred-type.metadata=!] self::Element dependent, [@vm.inferred-type.metadata=dart.core::_Smi?] core::Object? value) → void {
-    [@vm.call-site-attributes.metadata=receiverType:dart.core::Map<#lib::Element, dart.core::Object?>] [@vm.direct-call.metadata=dart.collection::__InternalLinkedHashMap&_HashVMBase&MapMixin&_LinkedHashMapMixin.[]=] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=#lib::InheritedElement._dependents] [@vm.inferred-type.metadata=dart.collection::_InternalLinkedHashMap<#lib::Element, dart.core::Object?>] this.{self::InheritedElement::_dependents}{core::Map<self::Element, core::Object?>}.{core::Map::[]=}(dependent, value){(self::Element, core::Object?) → void};
+    [@vm.call-site-attributes.metadata=receiverType:dart.core::Map<#lib::Element, dart.core::Object?>] [@vm.direct-call.metadata=dart.collection::__InternalLinkedHashMap&_HashVMBase&MapMixin&_HashBase&_OperatorEqualsAndHashCode&_LinkedHashMapMixin.[]=] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=#lib::InheritedElement._dependents] [@vm.inferred-type.metadata=dart.collection::_InternalLinkedHashMap<#lib::Element, dart.core::Object?>] this.{self::InheritedElement::_dependents}{core::Map<self::Element, core::Object?>}.{core::Map::[]=}(dependent, value){(self::Element, core::Object?) → void};
   }
 }
 static method main() → dynamic {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/const_map.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/const_map.dart.expect
index 6c1795f..88fd00f 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/const_map.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/const_map.dart.expect
@@ -11,7 +11,7 @@
     : super core::Object::•()
     ;
   static method fromReader() → self::_Attribute {
-    final self::_AttributeName name = [@vm.direct-call.metadata=dart.collection::_InternalImmutableLinkedHashMap.[]] [@vm.inferred-type.metadata=#lib::_AttributeName? (skip check)] #C8.{core::Map::[]}(#C1){(core::Object?) → self::_AttributeName?}!;
+    final self::_AttributeName name = [@vm.direct-call.metadata=dart.collection::__InternalImmutableLinkedHashMap&_HashVMImmutableBase&MapMixin&_HashBase&_OperatorEqualsAndCanonicalHashCode&_LinkedHashMapMixin&_UnmodifiableMapMixin&_ImmutableLinkedHashMapMixin.[]] [@vm.inferred-type.metadata=#lib::_AttributeName? (skip check)] #C8.{core::Map::[]}(#C1){(core::Object?) → self::_AttributeName?}!;
     return new self::_Attribute::_();
   }
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/const_set.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/const_set.dart.expect
index f345b4b..f8d211c 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/const_set.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/const_set.dart.expect
@@ -11,7 +11,7 @@
     : super core::Object::•()
     ;
   static method fromReader() → self::_Attribute {
-    final core::bool name = [@vm.direct-call.metadata=dart.collection::_CompactImmutableLinkedHashSet.contains] [@vm.inferred-type.metadata=!? (skip check)] #C3.{core::Set::contains}(#C2){(core::Object?) → core::bool};
+    final core::bool name = [@vm.direct-call.metadata=dart.collection::__CompactImmutableLinkedHashSet&_HashVMImmutableBase&SetMixin&_HashBase&_OperatorEqualsAndCanonicalHashCode&_LinkedHashSetMixin&_UnmodifiableSetMixin&_ImmutableLinkedHashSetMixin.contains] [@vm.inferred-type.metadata=!? (skip check)] #C3.{core::Set::contains}(#C2){(core::Object?) → core::bool};
     return let final self::_AttributeName #t1 = #C8.{core::List::[]}(#C2){(core::int) → self::_AttributeName} in new self::_Attribute::_();
   }
 }
diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc
index a6bc6e5..ca1f9dc 100644
--- a/runtime/vm/compiler/frontend/constant_reader.cc
+++ b/runtime/vm/compiler/frontend/constant_reader.cc
@@ -293,8 +293,7 @@
       const intptr_t used_data = (length << 1);
       map.set_used_data(used_data);
 
-      const intptr_t data_size = Utils::RoundUpToPowerOfTwo(used_data);
-      const auto& data = Array::Handle(Z, Array::New(data_size));
+      const auto& data = Array::Handle(Z, Array::New(used_data));
       map.set_data(data);
 
       map.set_deleted_keys(0);
@@ -343,8 +342,7 @@
       const intptr_t used_data = length;
       set.set_used_data(used_data);
 
-      const intptr_t data_size = Utils::RoundUpToPowerOfTwo(used_data);
-      const auto& data = Array::Handle(Z, Array::New(data_size));
+      const auto& data = Array::Handle(Z, Array::New(used_data));
       set.set_data(data);
 
       set.set_deleted_keys(0);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 2b6b9c2..0ef9ca6 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -24498,7 +24498,7 @@
   Zone* const zone = thread->zone();
 
   const auto& data_array = Array::Handle(zone, data());
-  const intptr_t data_length = data_array.Length();
+  const intptr_t data_length = Utils::RoundUpToPowerOfTwo(data_array.Length());
   const intptr_t index_size_mult = IsLinkedHashMap() ? 1 : 2;
   const intptr_t index_size = Utils::Maximum(LinkedHashBase::kInitialIndexSize,
                                              data_length * index_size_mult);
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index ac5c703..237ffb0 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -5226,12 +5226,14 @@
     // Check data, only for non-nested.
     const auto& data1 = Array::Handle(map1.data());
     const auto& data2 = Array::Handle(map2.data());
-    const bool data_length_equal = data1.Length() == data2.Length();
+    const intptr_t data1_length = Smi::Value(map1.used_data());
+    const intptr_t data2_length = Smi::Value(map2.used_data());
+    const bool data_length_equal = data1_length == data2_length;
     bool data_equal = data_length_equal;
     if (data_length_equal) {
       auto& object1 = Instance::Handle();
       auto& object2 = Instance::Handle();
-      for (intptr_t i = 0; i < data1.Length(); i++) {
+      for (intptr_t i = 0; i < data1_length; i++) {
         object1 ^= data1.At(i);
         object2 ^= data2.At(i);
         data_equal &= object1.CanonicalizeEquals(object2);
@@ -5242,14 +5244,14 @@
         THR_Print("LinkedHashBaseEqual Data not equal.\n");
         THR_Print("LinkedHashBaseEqual data1.length %" Pd " data1.length %" Pd
                   " \n",
-                  data1.Length(), data2.Length());
+                  data1_length, data2_length);
         auto& object1 = Instance::Handle();
-        for (intptr_t i = 0; i < data1.Length(); i++) {
+        for (intptr_t i = 0; i < data1_length; i++) {
           object1 ^= data1.At(i);
           THR_Print("LinkedHashBaseEqual data1[%" Pd "] %s\n", i,
                     object1.ToCString());
         }
-        for (intptr_t i = 0; i < data2.Length(); i++) {
+        for (intptr_t i = 0; i < data2_length; i++) {
           object1 ^= data2.At(i);
           THR_Print("LinkedHashBaseEqual data2[%" Pd "] %s\n", i,
                     object1.ToCString());
@@ -5315,7 +5317,7 @@
     const TypeArguments& type_arguments) {
   auto& map = LinkedHashMap::Handle(ImmutableLinkedHashMap::NewUninitialized());
 
-  const auto& data = Array::Handle(Array::New(input_data.Length()));
+  const auto& data = Array::Handle(Array::New(used_data));
   for (intptr_t i = 0; i < used_data; i++) {
     data.SetAt(i, Object::Handle(input_data.At(i)));
   }
@@ -5706,7 +5708,7 @@
     const TypeArguments& type_arguments) {
   auto& set = LinkedHashSet::Handle(ImmutableLinkedHashSet::NewUninitialized());
 
-  const auto& data = Array::Handle(Array::New(input_data.Length()));
+  const auto& data = Array::Handle(Array::New(used_data));
   for (intptr_t i = 0; i < used_data; i++) {
     data.SetAt(i, Object::Handle(input_data.At(i)));
   }
diff --git a/sdk/lib/_internal/vm/lib/compact_hash.dart b/sdk/lib/_internal/vm/lib/compact_hash.dart
index fa4b32d..c49bb48 100644
--- a/sdk/lib/_internal/vm/lib/compact_hash.dart
+++ b/sdk/lib/_internal/vm/lib/compact_hash.dart
@@ -16,7 +16,30 @@
   }
 }
 
-abstract class _HashFieldBase {
+// Common interface for [_HashFieldBase] and [_HashVMBase].
+abstract class _HashAbstractBase {
+  Uint32List get _index;
+  void set _index(Uint32List value);
+
+  int get _hashMask;
+  void set _hashMask(int value);
+
+  List get _data;
+  void set _data(List value);
+
+  int get _usedData;
+  void set _usedData(int value);
+
+  int get _deletedKeys;
+  void set _deletedKeys(int value);
+}
+
+abstract class _HashAbstractImmutableBase extends _HashAbstractBase {
+  @pragma("wasm:entry-point")
+  Uint32List? get _indexNullable;
+}
+
+abstract class _HashFieldBase implements _HashAbstractBase {
   // Each occupied entry in _index is a fixed-size integer that encodes a pair:
   //   [ hash pattern for key | index of entry in _data ]
   // The hash pattern is based on hashCode, but is guaranteed to be non-zero.
@@ -43,11 +66,11 @@
   // Note: All fields are initialized in a single constructor so that the VM
   // recognizes they cannot hold null values. This makes a big (20%) performance
   // difference on some operations.
-  _HashFieldBase(int dataSize);
+  _HashFieldBase();
 }
 
 // Base class for VM-internal classes; keep in sync with _HashFieldBase.
-abstract class _HashVMBase {
+abstract class _HashVMBase implements _HashAbstractBase {
   @pragma("vm:recognized", "other")
   @pragma("vm:exact-result-type", "dart:typed_data#_Uint32List")
   @pragma("vm:prefer-inline")
@@ -99,8 +122,9 @@
   external void set _deletedKeys(int value);
 }
 
-// Base class for VM-internal classes; keep in sync with _HashFieldBase.
-abstract class _HashVMImmutableBase extends _HashVMBase {
+// Base class for immutable VM-internal classes.
+abstract class _HashVMImmutableBase extends _HashVMBase
+    implements _HashAbstractImmutableBase {
   // The data is an immutable list rather than a mutable list.
   @pragma("vm:recognized", "other")
   @pragma("vm:exact-result-type", "dart:core#_ImmutableList")
@@ -123,8 +147,7 @@
 // This mixin can be applied to _HashFieldBase or _HashVMBase (for
 // normal and VM-internalized classes, respectiveley), which provide the
 // actual fields/accessors that this mixin assumes.
-// TODO(koda): Consider moving field comments to _HashFieldBase.
-abstract class _HashBase implements _HashVMBase {
+mixin _HashBase on _HashAbstractBase {
   // The number of bits used for each component is determined by table size.
   // If initialized, the length of _index is (at least) twice the number of
   // entries in _data, and both are doubled when _data is full. Thus, _index
@@ -208,17 +231,22 @@
   }
 }
 
-class _OperatorEqualsAndHashCode {
+abstract class _EqualsAndHashCode {
+  int _hashCode(e);
+  bool _equals(e1, e2);
+}
+
+mixin _OperatorEqualsAndHashCode implements _EqualsAndHashCode {
   int _hashCode(e) => e.hashCode;
   bool _equals(e1, e2) => e1 == e2;
 }
 
-class _IdenticalAndIdentityHashCode {
+mixin _IdenticalAndIdentityHashCode implements _EqualsAndHashCode {
   int _hashCode(e) => identityHashCode(e);
   bool _equals(e1, e2) => identical(e1, e2);
 }
 
-class _OperatorEqualsAndCanonicalHashCode {
+mixin _OperatorEqualsAndCanonicalHashCode implements _EqualsAndHashCode {
   static final int cidSymbol = ClassID.getID(#a);
 
   int _hashCode(e) {
@@ -232,6 +260,14 @@
   bool _equals(e1, e2) => e1 == e2;
 }
 
+mixin _CustomEqualsAndHashCode implements _EqualsAndHashCode {
+  dynamic get _hasher;
+  dynamic get _equality;
+
+  int _hashCode(e) => _hasher(e);
+  bool _equals(e1, e2) => _equality(e1, e2);
+}
+
 final _uninitializedIndex = new Uint32List(_HashBase._UNINITIALIZED_INDEX_SIZE);
 // Note: not const. Const arrays are made immutable by having a different class
 // than regular arrays that throws on element assignment. We want the data field
@@ -243,9 +279,9 @@
 class _InternalLinkedHashMap<K, V> extends _HashVMBase
     with
         MapMixin<K, V>,
-        _LinkedHashMapMixin<K, V>,
         _HashBase,
-        _OperatorEqualsAndHashCode
+        _OperatorEqualsAndHashCode,
+        _LinkedHashMapMixin<K, V>
     implements LinkedHashMap<K, V> {
   _InternalLinkedHashMap() {
     _index = _uninitializedIndex;
@@ -273,15 +309,19 @@
 class _InternalImmutableLinkedHashMap<K, V> extends _HashVMImmutableBase
     with
         MapMixin<K, V>,
-        _LinkedHashMapMixin<K, V>,
         _HashBase,
         _OperatorEqualsAndCanonicalHashCode,
-        _UnmodifiableMapMixin<K, V>
+        _LinkedHashMapMixin<K, V>,
+        _UnmodifiableMapMixin<K, V>,
+        _ImmutableLinkedHashMapMixin<K, V>
     implements LinkedHashMap<K, V> {
   factory _InternalImmutableLinkedHashMap._uninstantiable() {
     throw new UnsupportedError("ImmutableMap can only be allocated by the VM");
   }
+}
 
+mixin _ImmutableLinkedHashMapMixin<K, V>
+    on _LinkedHashMapMixin<K, V>, _HashAbstractImmutableBase {
   bool containsKey(Object? key) {
     if (_indexNullable == null) {
       _createIndex();
@@ -297,8 +337,8 @@
   }
 
   void _createIndex() {
-    final size = max(_data.length, _HashBase._INITIAL_INDEX_SIZE);
-    assert(size == _roundUpToPowerOfTwo(size));
+    final size =
+        _roundUpToPowerOfTwo(max(_data.length, _HashBase._INITIAL_INDEX_SIZE));
     final newIndex = new Uint32List(size);
     final hashMask = _HashBase._indexSizeToHashMask(size);
     assert(_hashMask == hashMask);
@@ -345,12 +385,7 @@
   return x + 1;
 }
 
-abstract class _LinkedHashMapMixin<K, V> implements _HashBase {
-  int _hashCode(e);
-  bool _equals(e1, e2);
-  int get _checkSum;
-  bool _isModifiedSince(List oldData, int oldCheckSum);
-
+mixin _LinkedHashMapMixin<K, V> on _HashBase, _EqualsAndHashCode {
   int get length => (_usedData >> 1) - _deletedKeys;
   bool get isEmpty => length == 0;
   bool get isNotEmpty => !isEmpty;
@@ -592,12 +627,10 @@
 class _CompactLinkedIdentityHashMap<K, V> extends _HashFieldBase
     with
         MapMixin<K, V>,
-        _LinkedHashMapMixin<K, V>,
         _HashBase,
-        _IdenticalAndIdentityHashCode
+        _IdenticalAndIdentityHashCode,
+        _LinkedHashMapMixin<K, V>
     implements LinkedHashMap<K, V> {
-  _CompactLinkedIdentityHashMap() : super(_HashBase._INITIAL_INDEX_SIZE);
-
   void addAll(Map<K, V> other) {
     if (other is _CompactLinkedIdentityHashMap) {
       final otherBase = other as _CompactLinkedIdentityHashMap;
@@ -610,15 +643,15 @@
 }
 
 class _CompactLinkedCustomHashMap<K, V> extends _HashFieldBase
-    with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase
+    with
+        MapMixin<K, V>,
+        _HashBase,
+        _CustomEqualsAndHashCode,
+        _LinkedHashMapMixin<K, V>
     implements LinkedHashMap<K, V> {
-  final _equality;
-  final _hasher;
-  final _validKey;
-
-  // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode.
-  int _hashCode(e) => _hasher(e);
-  bool _equals(e1, e2) => _equality(e1, e2);
+  final dynamic _equality;
+  final dynamic _hasher;
+  final dynamic _validKey;
 
   bool containsKey(Object? o) => _validKey(o) ? super.containsKey(o) : false;
   V? operator [](Object? o) => _validKey(o) ? super[o] : null;
@@ -628,8 +661,7 @@
   void operator []=(K key, V value);
 
   _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey)
-      : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test,
-        super(_HashBase._INITIAL_INDEX_SIZE);
+      : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test;
 }
 
 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ...
@@ -739,12 +771,7 @@
   E get current => _current as E;
 }
 
-abstract class _LinkedHashSetMixin<E> implements _HashBase {
-  int _hashCode(e);
-  bool _equals(e1, e2);
-  int get _checkSum;
-  bool _isModifiedSince(List oldData, int oldCheckSum);
-
+mixin _LinkedHashSetMixin<E> on _HashBase, _EqualsAndHashCode {
   bool get isEmpty => length == 0;
   bool get isNotEmpty => !isEmpty;
   int get length => _usedData - _deletedKeys;
@@ -910,14 +937,14 @@
   }
 }
 
-// Set implementation, analogous to _CompactLinkedHashMap.
+// Set implementation, analogous to _InternalLinkedHashMap.
 @pragma('vm:entry-point')
 class _CompactLinkedHashSet<E> extends _HashVMBase
     with
         SetMixin<E>,
-        _LinkedHashSetMixin<E>,
         _HashBase,
-        _OperatorEqualsAndHashCode
+        _OperatorEqualsAndHashCode,
+        _LinkedHashSetMixin<E>
     implements LinkedHashSet<E> {
   _CompactLinkedHashSet() {
     _index = _uninitializedIndex;
@@ -951,15 +978,19 @@
 class _CompactImmutableLinkedHashSet<E> extends _HashVMImmutableBase
     with
         SetMixin<E>,
-        _LinkedHashSetMixin<E>,
         _HashBase,
         _OperatorEqualsAndCanonicalHashCode,
-        _UnmodifiableSetMixin<E>
+        _LinkedHashSetMixin<E>,
+        _UnmodifiableSetMixin<E>,
+        _ImmutableLinkedHashSetMixin<E>
     implements LinkedHashSet<E> {
   factory _CompactImmutableLinkedHashSet._uninstantiable() {
     throw new UnsupportedError("ImmutableSet can only be allocated by the VM");
   }
+}
 
+mixin _ImmutableLinkedHashSetMixin<E>
+    on Set<E>, _LinkedHashSetMixin<E>, _HashAbstractImmutableBase {
   E? lookup(Object? key) {
     if (_indexNullable == null) {
       _createIndex();
@@ -1029,12 +1060,10 @@
 class _CompactLinkedIdentityHashSet<E> extends _HashFieldBase
     with
         SetMixin<E>,
-        _LinkedHashSetMixin<E>,
         _HashBase,
-        _IdenticalAndIdentityHashCode
+        _IdenticalAndIdentityHashCode,
+        _LinkedHashSetMixin<E>
     implements LinkedHashSet<E> {
-  _CompactLinkedIdentityHashSet() : super(_HashBase._INITIAL_INDEX_SIZE);
-
   Set<E> toSet() => new _CompactLinkedIdentityHashSet<E>()..addAll(this);
 
   static Set<R> _newEmpty<R>() => new _CompactLinkedIdentityHashSet<R>();
@@ -1053,22 +1082,22 @@
 }
 
 class _CompactLinkedCustomHashSet<E> extends _HashFieldBase
-    with SetMixin<E>, _LinkedHashSetMixin<E>, _HashBase
+    with
+        SetMixin<E>,
+        _HashBase,
+        _CustomEqualsAndHashCode,
+        _LinkedHashSetMixin<E>
     implements LinkedHashSet<E> {
-  final _equality;
-  final _hasher;
-  final _validKey;
-
-  int _hashCode(e) => _hasher(e);
-  bool _equals(e1, e2) => _equality(e1, e2);
+  final dynamic _equality;
+  final dynamic _hasher;
+  final dynamic _validKey;
 
   bool contains(Object? o) => _validKey(o) ? super.contains(o) : false;
   E? lookup(Object? o) => _validKey(o) ? super.lookup(o) : null;
   bool remove(Object? o) => _validKey(o) ? super.remove(o) : false;
 
   _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey)
-      : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test,
-        super(_HashBase._INITIAL_INDEX_SIZE);
+      : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test;
 
   Set<R> cast<R>() => Set.castFrom<E, R>(this);
   Set<E> toSet() =>
diff --git a/sdk/lib/_internal/wasm/lib/hash_factories.dart b/sdk/lib/_internal/wasm/lib/hash_factories.dart
index 4217e38..8159f09 100644
--- a/sdk/lib/_internal/wasm/lib/hash_factories.dart
+++ b/sdk/lib/_internal/wasm/lib/hash_factories.dart
@@ -49,3 +49,24 @@
   @patch
   factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
 }
+
+abstract class _HashWasmImmutableBase extends _HashFieldBase
+    implements _HashAbstractImmutableBase {
+  external Uint32List? get _indexNullable;
+}
+
+@pragma("wasm:entry-point")
+class _WasmImmutableLinkedHashMap<K, V> extends _HashWasmImmutableBase
+    with
+        MapMixin<K, V>,
+        _HashBase,
+        _OperatorEqualsAndHashCode,
+        _LinkedHashMapMixin<K, V>,
+        _UnmodifiableMapMixin<K, V>,
+        _ImmutableLinkedHashMapMixin<K, V>
+    implements LinkedHashMap<K, V> {
+  factory _WasmImmutableLinkedHashMap._uninstantiable() {
+    throw new UnsupportedError(
+        "Immutable maps can only be instantiated via constants");
+  }
+}
diff --git a/sdk/lib/_internal/wasm/lib/immutable_map.dart b/sdk/lib/_internal/wasm/lib/immutable_map.dart
deleted file mode 100644
index 74127f4..0000000
--- a/sdk/lib/_internal/wasm/lib/immutable_map.dart
+++ /dev/null
@@ -1,221 +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.
-
-// part of "core_patch.dart";
-
-/// Immutable map class for compiler generated map literals.
-// TODO(lrn): Extend MapBase with UnmodifiableMapMixin when mixins
-// support forwarding const constructors.
-@pragma("wasm:entry-point")
-class _ImmutableMap<K, V> implements Map<K, V> {
-  final _ImmutableList _kvPairs;
-
-  @pragma("wasm:entry-point")
-  const _ImmutableMap._create(_ImmutableList keyValuePairs)
-      : _kvPairs = keyValuePairs;
-
-  Map<K2, V2> cast<K2, V2>() => Map.castFrom<K, V, K2, V2>(this);
-
-  V? operator [](Object? key) {
-    // To preserve the key-value order of the map literal, the keys are
-    // not sorted. Need to do linear search or implement an additional
-    // lookup table.
-    for (int i = 0; i < _kvPairs.length - 1; i += 2) {
-      if (key == _kvPairs[i]) {
-        return _kvPairs[i + 1];
-      }
-    }
-    return null;
-  }
-
-  bool get isEmpty {
-    return _kvPairs.length == 0;
-  }
-
-  bool get isNotEmpty => !isEmpty;
-
-  int get length {
-    return _kvPairs.length ~/ 2;
-  }
-
-  void forEach(void f(K key, V value)) {
-    for (int i = 0; i < _kvPairs.length; i += 2) {
-      f(_kvPairs[i], _kvPairs[i + 1]);
-    }
-  }
-
-  Iterable<K> get keys {
-    return new _ImmutableMapKeyIterable<K>(this);
-  }
-
-  Iterable<V> get values {
-    return new _ImmutableMapValueIterable<V>(this);
-  }
-
-  bool containsKey(Object? key) {
-    for (int i = 0; i < _kvPairs.length; i += 2) {
-      if (key == _kvPairs[i]) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  bool containsValue(Object? value) {
-    for (int i = 1; i < _kvPairs.length; i += 2) {
-      if (value == _kvPairs[i]) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  void operator []=(K key, V value) {
-    throw new UnsupportedError("Cannot set value in unmodifiable Map");
-  }
-
-  void addAll(Map<K, V> other) {
-    throw new UnsupportedError("Cannot set value in unmodifiable Map");
-  }
-
-  V putIfAbsent(K key, V ifAbsent()) {
-    throw new UnsupportedError("Cannot set value in unmodifiable Map");
-  }
-
-  void clear() {
-    throw new UnsupportedError("Cannot clear unmodifiable Map");
-  }
-
-  V? remove(Object? key) {
-    throw new UnsupportedError("Cannot remove from unmodifiable Map");
-  }
-
-  Iterable<MapEntry<K, V>> get entries =>
-      new _ImmutableMapEntryIterable<K, V>(this);
-
-  Map<K2, V2> map<K2, V2>(MapEntry<K2, V2> f(K key, V value)) {
-    var result = <K2, V2>{};
-    for (int i = 0; i < _kvPairs.length; i += 2) {
-      var entry = f(_kvPairs[i], _kvPairs[i + 1]);
-      result[entry.key] = entry.value;
-    }
-    return result;
-  }
-
-  void addEntries(Iterable<MapEntry<K, V>> newEntries) {
-    throw new UnsupportedError("Cannot modify an unmodifiable Map");
-  }
-
-  V update(K key, V update(V value), {V ifAbsent()?}) {
-    throw new UnsupportedError("Cannot modify an unmodifiable Map");
-  }
-
-  void updateAll(V update(K key, V value)) {
-    throw new UnsupportedError("Cannot modify an unmodifiable Map");
-  }
-
-  void removeWhere(bool predicate(K key, V value)) {
-    throw new UnsupportedError("Cannot modify an unmodifiable Map");
-  }
-
-  String toString() => MapBase.mapToString(this);
-}
-
-class _ImmutableMapKeyIterable<E> extends EfficientLengthIterable<E> {
-  final _ImmutableMap _map;
-  _ImmutableMapKeyIterable(this._map);
-
-  Iterator<E> get iterator {
-    return new _ImmutableMapKeyIterator<E>(_map);
-  }
-
-  int get length => _map.length;
-}
-
-class _ImmutableMapValueIterable<E> extends EfficientLengthIterable<E> {
-  final _ImmutableMap _map;
-  _ImmutableMapValueIterable(this._map);
-
-  Iterator<E> get iterator {
-    return new _ImmutableMapValueIterator<E>(_map);
-  }
-
-  int get length => _map.length;
-}
-
-class _ImmutableMapEntryIterable<K, V>
-    extends EfficientLengthIterable<MapEntry<K, V>> {
-  final _ImmutableMap _map;
-  _ImmutableMapEntryIterable(this._map);
-
-  Iterator<MapEntry<K, V>> get iterator {
-    return new _ImmutableMapEntryIterator<K, V>(_map);
-  }
-
-  int get length => _map.length;
-}
-
-class _ImmutableMapKeyIterator<E> implements Iterator<E> {
-  _ImmutableMap _map;
-  int _nextIndex = 0;
-  E? _current;
-
-  _ImmutableMapKeyIterator(this._map);
-
-  bool moveNext() {
-    int newIndex = _nextIndex;
-    if (newIndex < _map.length) {
-      _nextIndex = newIndex + 1;
-      _current = _map._kvPairs[newIndex * 2];
-      return true;
-    }
-    _current = null;
-    return false;
-  }
-
-  E get current => _current as E;
-}
-
-class _ImmutableMapValueIterator<E> implements Iterator<E> {
-  _ImmutableMap _map;
-  int _nextIndex = 0;
-  E? _current;
-
-  _ImmutableMapValueIterator(this._map);
-
-  bool moveNext() {
-    int newIndex = _nextIndex;
-    if (newIndex < _map.length) {
-      _nextIndex = newIndex + 1;
-      _current = _map._kvPairs[newIndex * 2 + 1];
-      return true;
-    }
-    _current = null;
-    return false;
-  }
-
-  E get current => _current as E;
-}
-
-class _ImmutableMapEntryIterator<K, V> implements Iterator<MapEntry<K, V>> {
-  _ImmutableMap _map;
-  int _nextIndex = 0;
-  MapEntry<K, V>? _current;
-
-  _ImmutableMapEntryIterator(this._map);
-
-  bool moveNext() {
-    int newIndex = _nextIndex;
-    if (newIndex < _map.length) {
-      _nextIndex = newIndex + 1;
-      _current = new MapEntry<K, V>(
-          _map._kvPairs[newIndex * 2], _map._kvPairs[newIndex * 2 + 1]);
-      return true;
-    }
-    _current = null;
-    return false;
-  }
-
-  MapEntry<K, V> get current => _current as MapEntry<K, V>;
-}
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index 619c1c0..b05ae57 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -197,7 +197,6 @@
           "_internal/wasm/lib/function.dart",
           "_internal/wasm/lib/growable_list.dart",
           "_internal/wasm/lib/identical_patch.dart",
-          "_internal/wasm/lib/immutable_map.dart",
           "_internal/wasm/lib/int.dart",
           "_internal/vm/lib/integers_patch.dart",
           "_internal/wasm/lib/list.dart",
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index 055f5e8..d4e109f 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -189,7 +189,6 @@
       - _internal/wasm/lib/function.dart
       - _internal/wasm/lib/growable_list.dart
       - _internal/wasm/lib/identical_patch.dart
-      - _internal/wasm/lib/immutable_map.dart
       - _internal/wasm/lib/int.dart
       - _internal/vm/lib/integers_patch.dart
       - _internal/wasm/lib/list.dart
diff --git a/tools/VERSION b/tools/VERSION
index f0d0abe..59c79ec 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 213
+PRERELEASE 214
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/utils.py b/tools/utils.py
index cecdbc2..3741a0b 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -154,10 +154,10 @@
     os_id = platform.machine()
     if os_id.startswith('armv6'):
         return 'armv6'
+    elif os_id.startswith('aarch64') or os_id == 'arm64':
+        return 'arm64'
     elif os_id.startswith('arm'):
         return 'arm'
-    elif os_id.startswith('aarch64'):
-        return 'arm64'
     elif '64' in os_id:
         return 'x64'
     elif (not os_id) or (not re.match('(x|i[3-6])86', os_id) is None):