Version 2.19.0-30.0.dev

Merge commit 'f6f29df4b56cdeae756eb9ddd34df91b72c74ed0' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart
index 89e3b5a..e8cc373 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart
@@ -297,7 +297,8 @@
   Expression getObjectOffGlobalThis(Procedure node, List<String> selectors) {
     Expression currentTarget = _globalThis;
     for (String selector in selectors) {
-      currentTarget = _getProperty(node, currentTarget, selector);
+      currentTarget = _getProperty(node, currentTarget, selector,
+          typeArgument: _nonNullableObjectType);
     }
     return currentTarget;
   }
@@ -315,8 +316,9 @@
         type: _nonNullableObjectType);
     body.add(object);
     for (VariableDeclaration variable in node.function.namedParameters) {
-      body.add(ExpressionStatement(
-          _setProperty(node, VariableGet(object), variable.name!, variable)));
+      body.add(ExpressionStatement(_setProperty(
+          node, VariableGet(object), variable.name!, variable,
+          typeArgument: variable.type)));
     }
     body.add(ReturnStatement(VariableGet(object)));
     return Block(body);
@@ -349,12 +351,12 @@
   ///
   /// The new [Expression] is equivalent to:
   /// `js_util.getProperty([object], [getterName])`.
-  Expression _getProperty(
-          Procedure node, Expression object, String getterName) =>
+  Expression _getProperty(Procedure node, Expression object, String getterName,
+          {DartType? typeArgument}) =>
       StaticInvocation(
           _getPropertyTarget,
           Arguments([object, StringLiteral(getterName)],
-              types: [node.function.returnType]))
+              types: [typeArgument ?? node.function.returnType]))
         ..fileOffset = node.fileOffset;
 
   /// Returns a new function body for the given [node] external getter.
@@ -377,11 +379,11 @@
   /// The new [Expression] is equivalent to:
   /// `js_util.setProperty([object], [setterName], [value])`.
   Expression _setProperty(Procedure node, Expression object, String setterName,
-          VariableDeclaration value) =>
+          VariableDeclaration value, {DartType? typeArgument}) =>
       StaticInvocation(
           _setPropertyTarget,
           Arguments([object, StringLiteral(setterName), VariableGet(value)],
-              types: [node.function.returnType]))
+              types: [typeArgument ?? node.function.returnType]))
         ..fileOffset = node.fileOffset;
 
   /// Returns a new function body for the given [node] external setter.
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index e62bf7f..2d2a59a 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -803,8 +803,14 @@
       return createConstant(constant, info.nonNullableType, (function, b) {
         b.i32_const(info.classId);
         b.i32_const(initialIdentityHash);
-        types.encodeNullability(b, type);
-        b.i32_const(environmentIndex);
+
+        // A type parameter's type nullability is undetermined when it's
+        // syntactically not declared nullable and the bound of the type
+        // parameter is nullable. Because we are encoding the declared
+        // nullability, we only declare a type parameter to be nullable if it is
+        // explicitly declared to be nullabe.
+        b.i32_const(type.declaredNullability == Nullability.nullable ? 1 : 0);
+        b.i64_const(environmentIndex);
         translator.struct_new(b, info);
       });
     } else {
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index 7971b28..9e5effc 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -593,7 +593,8 @@
     if (className != null) {
       List<String> libAndClass = className.split("#");
       Class cls = translator.libraries
-          .firstWhere((l) => l.name == libAndClass[0])
+          .firstWhere(
+              (l) => l.name == libAndClass[0] && l.importUri.scheme == 'dart')
           .classes
           .firstWhere((c) => c.name == libAndClass[1]);
       int classId = translator.classInfo[cls]!.classId;
@@ -682,6 +683,8 @@
           return translator.types.makeTypeRulesSupers(b);
         case "_getTypeRulesSubstitutions":
           return translator.types.makeTypeRulesSubstitutions(b);
+        case "_getTypeNames":
+          return translator.types.makeTypeNames(b);
         case "_getInterfaceTypeRuntimeType":
           Expression object = node.arguments.positional[0];
           Expression typeArguments = node.arguments.positional[1];
@@ -793,6 +796,26 @@
           return w.NumType.f64;
         case "getID":
           return getID(node.arguments.positional.single);
+        case "makeListFixedLength":
+          ClassInfo receiverInfo =
+              translator.classInfo[translator.listBaseClass]!;
+          codeGen.wrap(
+              node.arguments.positional.single, receiverInfo.nonNullableType);
+          w.Local receiverLocal =
+              codeGen.function.addLocal(receiverInfo.nullableType);
+          b.local_tee(receiverLocal);
+          // We ignore the type argument and just update the classID of the
+          // receiver.
+          // TODO(joshualitt): If the amount of free space is significant, it
+          // might be worth doing a copy here.
+          ClassInfo topInfo = translator.topInfo;
+          ClassInfo fixedLengthListInfo =
+              translator.classInfo[translator.fixedLengthListClass]!;
+          b.i32_const(fixedLengthListInfo.classId);
+          b.struct_set(topInfo.struct, FieldIndex.classId);
+          b.local_get(receiverLocal);
+          b.ref_as_non_null();
+          return fixedLengthListInfo.nonNullableType;
       }
     }
 
@@ -1250,6 +1273,11 @@
         ClassInfo info = translator.classInfo[cls]!;
         b.local_get(paramLocals[0]);
         translator.ref_cast(b, info);
+        // TODO(joshualitt): Because we currently merge getters to support
+        // dynamic calls, the return types of `.length` and `.offsetInBytes` can
+        // change. Should we decide to stop merging getters, we should remove
+        // the conversions below.
+        w.ValueType outputType = function.type.outputs.single;
         switch (name) {
           case "length":
             assert(cls == translator.typedListBaseClass ||
@@ -1260,6 +1288,7 @@
               b.struct_get(info.struct, FieldIndex.byteDataViewLength);
             }
             b.i64_extend_i32_u();
+            translator.convertType(function, intType, outputType);
             return true;
           case "offsetInBytes":
             assert(cls == translator.typedListViewClass ||
@@ -1270,6 +1299,7 @@
               b.struct_get(info.struct, FieldIndex.byteDataViewOffsetInBytes);
             }
             b.i64_extend_i32_u();
+            translator.convertType(function, intType, outputType);
             return true;
           case "_typedData":
             assert(cls == translator.typedListViewClass ||
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 8e6c7d9..8cd8483 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -127,6 +127,7 @@
   late final Procedure hashImmutableIndexNullable;
   late final Procedure isSubtype;
   late final Procedure objectRuntimeType;
+  late final Procedure typeAsNullable;
   late final Map<Class, w.StorageType> builtinTypes;
   late final Map<w.ValueType, Class> boxedClasses;
 
@@ -272,6 +273,9 @@
     objectRuntimeType = lookupCore("Object")
         .procedures
         .firstWhere((p) => p.name.text == "_runtimeType");
+    typeAsNullable = lookupCore("_Type")
+        .procedures
+        .firstWhere((p) => p.name.text == "asNullable");
     builtinTypes = {
       coreTypes.boolClass: w.NumType.i32,
       coreTypes.intClass: w.NumType.i64,
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 50e4283..1011b26 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -63,6 +63,9 @@
   late final List<List<List<DartType>>> typeRulesSubstitutions =
       _buildTypeRulesSubstitutions();
 
+  /// A list which maps class ID to the classes [String] name.
+  late final List<String> typeNames = _buildTypeNames();
+
   Types(this.translator);
 
   w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
@@ -100,8 +103,14 @@
       if (superclassInfo.cls == null ||
           superclassInfo.cls == coreTypes.objectClass) continue;
       Class superclass = superclassInfo.cls!;
-      Iterable<Class> subclasses =
-          _getConcreteSubtypes(superclass).where((cls) => cls != superclass);
+
+      // TODO(joshualitt): This includes abstract types that can't be
+      // instantiated, but might be needed for subtype checks. The majority of
+      // abstract classes are probably unnecessary though. We should filter
+      // these cases to reduce the size of the type rules.
+      Iterable<Class> subclasses = translator.subtypes
+          .getSubtypesOf(superclass)
+          .where((cls) => cls != superclass);
       Iterable<InterfaceType> subtypes = subclasses.map(
           (Class cls) => cls.getThisType(coreTypes, Nullability.nonNullable));
       for (InterfaceType subtype in subtypes) {
@@ -144,6 +153,17 @@
     return typeRulesSubstitutions;
   }
 
+  List<String> _buildTypeNames() {
+    // This logic assumes `translator.classes` returns the classes indexed by
+    // class ID. If we ever change that logic, we will need to change this code.
+    List<String> typeNames = [];
+    for (ClassInfo classInfo in translator.classes) {
+      String className = classInfo.cls?.name ?? '';
+      typeNames.add(className);
+    }
+    return typeNames;
+  }
+
   /// Builds a map of subclasses to the transitive set of superclasses they
   /// implement.
   /// TODO(joshualitt): This implementation is just temporary. Eventually we
@@ -205,6 +225,25 @@
     return expectedType;
   }
 
+  /// Returns a list of string type names for pretty printing types.
+  w.ValueType makeTypeNames(w.Instructions b) {
+    w.ValueType expectedType =
+        translator.classInfo[translator.immutableListClass]!.nonNullableType;
+    DartType stringType = InterfaceType(
+        translator.stringBaseClass,
+        Nullability.nonNullable,
+        [translator.coreTypes.stringNonNullableRawType]);
+    List<StringConstant> listStringConstant = [];
+    for (String name in typeNames) {
+      listStringConstant.add(StringConstant(name));
+    }
+    DartType listStringType = InterfaceType(
+        translator.immutableListClass, Nullability.nonNullable, [stringType]);
+    translator.constants.instantiateConstant(null, b,
+        ListConstant(listStringType, listStringConstant), expectedType);
+    return expectedType;
+  }
+
   bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
 
   bool isGenericFunctionTypeParameter(TypeParameterType type) =>
@@ -358,7 +397,11 @@
         type is FutureOrType ||
         type is FunctionType);
     if (type is TypeParameterType) {
-      return codeGen.instantiateTypeParameter(type.parameter);
+      codeGen.instantiateTypeParameter(type.parameter);
+      if (type.declaredNullability == Nullability.nullable) {
+        codeGen.call(translator.typeAsNullable.reference);
+      }
+      return nonNullableTypeType;
     }
     ClassInfo info = translator.classInfo[classForType(type)]!;
     translator.functions.allocateClass(info.classId);
@@ -390,16 +433,20 @@
   void emitTypeTest(CodeGenerator codeGen, DartType type, DartType operandType,
       TreeNode node) {
     w.Instructions b = codeGen.b;
-    if (type is! InterfaceType) {
-      // TODO(joshualitt): We can enable this after fixing `.runtimeType`.
-      // makeType(codeGen, type);
-      // codeGen.call(translator.isSubtype.reference);
-      print("Not implemented: Type test with non-interface type $type"
+    if (type is FunctionType) {
+      // TODO(joshualitt): We can enable type tests for [FunctionType] after
+      // enabling `.runtimeType` for [FunctionType].
+      print("Not implemented: Type test with function type $type"
           " at ${node.location}");
       b.drop();
       b.i32_const(1);
       return;
     }
+    if (type is! InterfaceType) {
+      makeType(codeGen, type);
+      codeGen.call(translator.isSubtype.reference);
+      return;
+    }
     bool isPotentiallyNullable = operandType.isPotentiallyNullable;
     w.Label? resultLabel;
     if (isPotentiallyNullable) {
@@ -412,6 +459,15 @@
       b.local_get(operand);
       b.br_on_null(nullLabel);
     }
+    void _endPotentiallyNullableBlock() {
+      if (isPotentiallyNullable) {
+        b.br(resultLabel!);
+        b.end(); // nullLabel
+        encodeNullability(b, type);
+        b.end(); // resultLabel
+      }
+    }
+
     if (type.typeArguments.any((t) => t is! DynamicType)) {
       // If the tested-against type as an instance of the static operand type
       // has the same type arguments as the static operand type, it is not
@@ -421,8 +477,10 @@
           .getTypeAsInstanceOf(type, cls, codeGen.member.enclosingLibrary)
           ?.withDeclaredNullability(operandType.declaredNullability);
       if (base != operandType) {
-        print("Not implemented: Type test with type arguments"
-            " at ${node.location}");
+        makeType(codeGen, type);
+        codeGen.call(translator.isSubtype.reference);
+        _endPotentiallyNullableBlock();
+        return;
       }
     }
     List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
@@ -454,12 +512,7 @@
       b.i32_const(0);
       b.end(); // done
     }
-    if (isPotentiallyNullable) {
-      b.br(resultLabel!);
-      b.end(); // nullLabel
-      encodeNullability(b, type);
-      b.end(); // resultLabel
-    }
+    _endPotentiallyNullableBlock();
   }
 
   /// Returns true if a given type is nullable, and false otherwise. This
diff --git a/sdk/lib/_internal/wasm/lib/class_id.dart b/sdk/lib/_internal/wasm/lib/class_id.dart
index 08f2006..312ec3e 100644
--- a/sdk/lib/_internal/wasm/lib/class_id.dart
+++ b/sdk/lib/_internal/wasm/lib/class_id.dart
@@ -16,10 +16,12 @@
   external static int get cidUint8ArrayView;
   @pragma("wasm:class-id", "dart.core#Object")
   external static int get cidObject;
-  @pragma("wasm:class-id", "dart.async#Future")
-  external static int get cidFuture;
+  @pragma("wasm:class-id", "dart.async#_Future")
+  external static int get cid_Future;
   @pragma("wasm:class-id", "dart.core#Function")
   external static int get cidFunction;
+  @pragma("wasm:class-id", "dart.core#_Function")
+  external static int get cid_Function;
 
   // Class IDs for RTI Types.
   @pragma("wasm:class-id", "dart.core#_NeverType")
diff --git a/sdk/lib/_internal/wasm/lib/object_patch.dart b/sdk/lib/_internal/wasm/lib/object_patch.dart
index e61e064..8ba627e 100644
--- a/sdk/lib/_internal/wasm/lib/object_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/object_patch.dart
@@ -45,6 +45,8 @@
   /// override [runtimeType].
   @patch
   external Type get runtimeType;
+
+  @pragma("wasm:entry-point")
   _Type get _runtimeType => _getInterfaceTypeRuntimeType(this, _typeArguments);
 
   @patch
diff --git a/sdk/lib/_internal/wasm/lib/type.dart b/sdk/lib/_internal/wasm/lib/type.dart
index 4a1ea76..8df02e3 100644
--- a/sdk/lib/_internal/wasm/lib/type.dart
+++ b/sdk/lib/_internal/wasm/lib/type.dart
@@ -48,6 +48,7 @@
 
 @pragma("wasm:entry-point")
 class _NeverType extends _Type {
+  @pragma("wasm:entry-point")
   const _NeverType() : super(false);
 
   @override
@@ -63,6 +64,7 @@
 
 @pragma("wasm:entry-point")
 class _DynamicType extends _Type {
+  @pragma("wasm:entry-point")
   const _DynamicType() : super(true);
 
   @override
@@ -77,6 +79,7 @@
 
 @pragma("wasm:entry-point")
 class _VoidType extends _Type {
+  @pragma("wasm:entry-point")
   const _VoidType() : super(true);
 
   @override
@@ -91,6 +94,7 @@
 
 @pragma("wasm:entry-point")
 class _NullType extends _Type {
+  @pragma("wasm:entry-point")
   const _NullType() : super(true);
 
   @override
@@ -110,6 +114,7 @@
 class _InterfaceTypeParameterType extends _Type {
   final int environmentIndex;
 
+  @pragma("wasm:entry-point")
   const _InterfaceTypeParameterType(super.isNullable, this.environmentIndex);
 
   @override
@@ -121,7 +126,7 @@
       throw 'Type parameter should have been substituted already.';
 
   @override
-  String toString() => '$environmentIndex';
+  String toString() => 'T$environmentIndex';
 }
 
 @pragma("wasm:entry-point")
@@ -140,7 +145,7 @@
       throw 'Type parameter should have been substituted already.';
 
   @override
-  String toString() => '$environmentIndex';
+  String toString() => 'G$environmentIndex';
 }
 
 @pragma("wasm:entry-point")
@@ -151,7 +156,7 @@
   const _FutureOrType(bool isNullable, this.typeArgument) : super(isNullable);
 
   _InterfaceType get asFuture =>
-      _InterfaceType(ClassID.cidFuture, isNullable, [typeArgument]);
+      _InterfaceType(ClassID.cid_Future, isNullable, [typeArgument]);
 
   @override
   _Type get _asNonNullable {
@@ -231,8 +236,7 @@
   @override
   String toString() {
     StringBuffer s = StringBuffer();
-    s.write("Interface");
-    s.write(classId);
+    s.write(_getTypeNames()[classId]);
     if (typeArguments.isNotEmpty) {
       s.write("<");
       for (int i = 0; i < typeArguments.length; i++) {
@@ -364,8 +368,8 @@
 }
 
 // TODO(joshualitt): Implement. This should probably extend _FunctionType.
-@pragma("wasm:entry-point")
 class _GenericFunctionType extends _Type {
+  @pragma("wasm:entry-point")
   const _GenericFunctionType(bool isNullable) : super(isNullable);
 
   @override
@@ -380,6 +384,7 @@
 
 external List<List<int>> _getTypeRulesSupers();
 external List<List<List<_Type>>> _getTypeRulesSubstitutions();
+external List<String> _getTypeNames();
 
 class _Environment {
   List<List<_Type>> scopes = [];
@@ -450,7 +455,8 @@
   }
 
   bool isFunctionType(_Type t) =>
-      isSpecificInterfaceType(t, ClassID.cidFunction);
+      isSpecificInterfaceType(t, ClassID.cidFunction) ||
+      isSpecificInterfaceType(t, ClassID.cid_Function);
 
   bool areTypeArgumentsSubtypes(List<_Type> sArgs, _Environment? sEnv,
       List<_Type> tArgs, _Environment? tEnv) {
@@ -484,13 +490,22 @@
 
     List<_Type> substitutions = typeRulesSubstitutions[sId][sSuperIndexOfT];
 
+    // If we have empty type arguments then create a list of dynamic type
+    // arguments.
+    List<_Type> sTypeArguments = s.typeArguments;
+    if (substitutions.isNotEmpty && sTypeArguments.isEmpty) {
+      sTypeArguments = List<_Type>.generate(
+          substitutions.length, (int index) => const _DynamicType(),
+          growable: false);
+    }
+
     // If [sEnv] is null, then create a new environment. Otherwise, we are doing
     // a recursive type check, so extend the existing environment with [s]'s
     // type arguments.
     if (sEnv == null) {
-      sEnv = _Environment.from(s.typeArguments);
+      sEnv = _Environment.from(sTypeArguments);
     } else {
-      sEnv.push(s.typeArguments);
+      sEnv.push(sTypeArguments);
     }
     bool result =
         areTypeArgumentsSubtypes(substitutions, sEnv, t.typeArguments, tEnv);
@@ -562,6 +577,7 @@
     }
     while (sIndex < sNamedLength) {
       if (sNamed[sIndex].isRequired) return false;
+      sIndex++;
     }
     return true;
   }
@@ -606,7 +622,7 @@
       if (!isSubtype(sFutureOr.typeArgument, sEnv, t, tEnv)) {
         return false;
       }
-      return _isSubtype(sFutureOr.asFuture, t);
+      return isSubtype(sFutureOr.asFuture, sEnv, t, tEnv);
     }
 
     // Left Nullable:
@@ -643,6 +659,12 @@
       return true;
     }
 
+    // TODO(joshualitt): This is not correct, but it is necessary until we fully
+    // implement RTI for FunctionTypes.
+    if (isFunctionType(s) && (t.isFunction || t.isGenericFunction)) {
+      return true;
+    }
+
     // Positional Function Types + Named Function Types:
     if (s.isGenericFunction && t.isGenericFunction) {
       // TODO(joshualitt): Implement case.
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 44b3906..f8fb750 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -196,6 +196,7 @@
   bool shouldChain(Future<dynamic> value) => value is Future<T> || value is! T;
 }
 
+@pragma("wasm:entry-point")
 class _Future<T> implements Future<T> {
   /// Initial state, waiting for a result. In this state, the
   /// [_resultOrListeners] field holds a single-linked list of
diff --git a/tools/VERSION b/tools/VERSION
index d4e983a..e2c5c13 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 19
 PATCH 0
-PRERELEASE 29
+PRERELEASE 30
 PRERELEASE_PATCH 0
\ No newline at end of file