Version 2.17.0-78.0.dev

Merge commit '1044d83ab779e39b774439ae9976d2aab90aa27c' into 'dev'
diff --git a/pkg/front_end/test/fasta/expression_suite.dart b/pkg/front_end/test/fasta/expression_suite.dart
index 8ada32c..8e867b7 100644
--- a/pkg/front_end/test/fasta/expression_suite.dart
+++ b/pkg/front_end/test/fasta/expression_suite.dart
@@ -38,7 +38,17 @@
     show IncrementalCompiler;
 
 import "package:kernel/ast.dart"
-    show Component, DartType, DynamicType, Procedure, TypeParameter;
+    show
+        Class,
+        Component,
+        Constructor,
+        DartType,
+        DynamicType,
+        Field,
+        Library,
+        Member,
+        Procedure,
+        TypeParameter;
 
 import 'package:kernel/target/targets.dart' show TargetFlags;
 
@@ -65,7 +75,11 @@
   @override
   final List<Step> steps;
 
-  Context(this.compilerContext, this.errors, bool updateExpectations)
+  final bool fuzz;
+  final Set<Uri> fuzzedLibraries = {};
+  int fuzzCompiles = 0;
+
+  Context(this.compilerContext, this.errors, bool updateExpectations, this.fuzz)
       : steps = <Step>[
           const ReadTest(),
           const CompileExpression(),
@@ -399,6 +413,118 @@
       List<int> list = serializeProcedure(compiledProcedure);
       assert(list.length > 0);
     }
+
+    if (context.fuzz) {
+      await fuzz(compiler, compilerResult, context);
+    }
+  }
+
+  Future<void> fuzz(IncrementalCompiler compiler,
+      IncrementalCompilerResult compilerResult, Context context) async {
+    for (Library lib in compilerResult.classHierarchy!.knownLibraries) {
+      if (!context.fuzzedLibraries.add(lib.importUri)) continue;
+
+      for (Member m in lib.members) {
+        await fuzzMember(m, compiler, lib.importUri, context);
+      }
+
+      for (Class c in lib.classes) {
+        for (Member m in c.members) {
+          await fuzzMember(m, compiler, lib.importUri, context);
+        }
+      }
+    }
+  }
+
+  Future<void> fuzzMember(Member m, IncrementalCompiler compiler,
+      Uri libraryUri, Context context) async {
+    String expression = m.name.text;
+    if (m is Field || (m is Procedure && m.isGetter)) {
+      // fields and getters are fine as-is
+    } else if (m is Procedure && !m.isGetter) {
+      expression = "$expression()";
+    } else if (m is Constructor) {
+      if (m.parent is! Class) {
+        return;
+      }
+      Class parent = m.parent as Class;
+      if (m.name.text != "") {
+        expression = "${parent.name}.${m.name.text}()";
+      } else {
+        expression = "${parent.name}()";
+      }
+    } else {
+      print("Ignoring $m (${m.runtimeType})");
+      return;
+    }
+
+    String? className;
+    if (m.parent is Class && m is! Constructor) {
+      Class parent = m.parent as Class;
+      className = parent.name;
+    }
+
+    await fuzzTryCompile(compiler, "$expression", libraryUri, className,
+        !m.isInstanceMember, context);
+    if (className != null && !m.isInstanceMember) {
+      await fuzzTryCompile(compiler, "$className.$expression", libraryUri, null,
+          !m.isInstanceMember, context);
+    }
+    await fuzzTryCompile(compiler, "$expression.toString()", libraryUri,
+        className, !m.isInstanceMember, context);
+    if (className != null && !m.isInstanceMember) {
+      await fuzzTryCompile(compiler, "$className.$expression.toString()",
+          libraryUri, null, !m.isInstanceMember, context);
+    }
+    await fuzzTryCompile(compiler, "$expression.toString() == '42'", libraryUri,
+        className, !m.isInstanceMember, context);
+    if (className != null && !m.isInstanceMember) {
+      await fuzzTryCompile(
+          compiler,
+          "$className.$expression.toString() == '42'",
+          libraryUri,
+          null,
+          !m.isInstanceMember,
+          context);
+    }
+    await fuzzTryCompile(
+        compiler,
+        "() { var x = $expression.toString(); x == '42'; }()",
+        libraryUri,
+        className,
+        !m.isInstanceMember,
+        context);
+    if (className != null && !m.isInstanceMember) {
+      await fuzzTryCompile(
+          compiler,
+          "() { var x = $className.$expression.toString(); x == '42'; }()",
+          libraryUri,
+          null,
+          !m.isInstanceMember,
+          context);
+    }
+  }
+
+  Future<void> fuzzTryCompile(IncrementalCompiler compiler, String expression,
+      Uri libraryUri, String? className, bool isStatic, Context context) async {
+    context.fuzzCompiles++;
+    print("Fuzz compile #${context.fuzzCompiles} "
+        "('$expression' in $libraryUri $className)");
+    Procedure? compiledProcedure = await compiler.compileExpression(
+      expression,
+      {},
+      [],
+      "debugExpr",
+      libraryUri,
+      className: className,
+      isStatic: isStatic,
+    );
+    context.takeErrors();
+    if (compiledProcedure != null) {
+      // Confirm we can serialize generated procedure.
+      List<int> list = serializeProcedure(compiledProcedure);
+      assert(list.length > 0);
+    }
   }
 
   @override
@@ -430,6 +556,7 @@
           path: test.entryPoint!.path + ".dill");
       Uint8List dillData = await serializeComponent(component);
       context.fileSystem.entityForUri(dillFileUri).writeAsBytesSync(dillData);
+      Set<Uri> beforeFuzzedLibraries = context.fuzzedLibraries.toSet();
       await compileExpression(
           test, sourceCompiler, sourceCompilerResult, context);
 
@@ -444,6 +571,8 @@
       // Since it compiled successfully from source, the bootstrap-from-Dill
       // should also succeed without errors.
       assert(errors.isEmpty);
+      context.fuzzedLibraries.clear();
+      context.fuzzedLibraries.addAll(beforeFuzzedLibraries);
       await compileExpression(test, dillCompiler, dillCompilerResult, context);
     }
     return new Result.pass(tests);
@@ -492,13 +621,15 @@
 
   final bool updateExpectations = environment["updateExpectations"] == "true";
 
+  final bool fuzz = environment["fuzz"] == "true";
+
   final CompilerContext compilerContext = new CompilerContext(options);
 
   // Disable colors to ensure that expectation files are the same across
   // platforms and independent of stdin/stderr.
   colors.enableColors = false;
 
-  return new Context(compilerContext, errors, updateExpectations);
+  return new Context(compilerContext, errors, updateExpectations, fuzz);
 }
 
 void main([List<String> arguments = const []]) =>
diff --git a/pkg/front_end/test/fasta/tool_git_test.dart b/pkg/front_end/test/fasta/tool_git_test.dart
index 6e4b231..092e026 100644
--- a/pkg/front_end/test/fasta/tool_git_test.dart
+++ b/pkg/front_end/test/fasta/tool_git_test.dart
@@ -114,7 +114,7 @@
     "dump-ir": {
       "exitCode": 2,
       "stdout": "",
-      "stderr": "Usage: dump-ir dillfile [output]\n",
+      "stderr": "Usage: dump-ir dillFile [output]\n",
     },
   };
 
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 2fe783e..5226583 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -28,6 +28,7 @@
 affecting
 afterwards
 agree
+agreeing
 ahe
 ai
 aiki
@@ -174,6 +175,7 @@
 char
 charcode
 chars
+checkout
 checkpoint
 chloestefantsova
 chunks
@@ -492,6 +494,7 @@
 futureor
 g
 gardening
+gb
 gen
 generation
 getable
@@ -560,6 +563,7 @@
 ideographic
 idn
 ids
+idx
 iff
 il
 imitate
@@ -652,6 +656,7 @@
 jvm
 k
 kallentu
+kb
 kernel's
 kill
 klass
@@ -766,6 +771,7 @@
 nameless
 namer
 natively
+nativewrappers
 nbsp
 nc
 ncs
@@ -836,6 +842,7 @@
 overlooked
 overshadowed
 oversight
+overview
 overwrite
 overwriting
 ownership
@@ -1178,6 +1185,7 @@
 starter
 stated
 statics
+stats
 stderr
 stdin
 stdio
@@ -1452,6 +1460,7 @@
 xdc
 xdfff
 xef
+xff
 xi
 xj
 xk
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 33568c9..3300b99 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -188,6 +188,7 @@
 descriptors
 deviation
 dfast
+dfuzz
 di
 diagnosticable
 dictionaries
@@ -195,7 +196,6 @@
 differentiate
 dijkstra
 dijkstras
-dillfile
 dinteractive
 dirname
 disagree
diff --git a/pkg/front_end/test/weekly_tester.dart b/pkg/front_end/test/weekly_tester.dart
index 2fe9bbf..077210e 100644
--- a/pkg/front_end/test/weekly_tester.dart
+++ b/pkg/front_end/test/weekly_tester.dart
@@ -124,6 +124,24 @@
     }
   }
 
+  {
+    // Expression suite with fuzzing.
+    Uri expressionSuite =
+        Platform.script.resolve("fasta/expression_suite.dart");
+    if (!new File.fromUri(expressionSuite).existsSync()) {
+      exitCode = 1;
+      print("Couldn't find $expressionSuite");
+    } else {
+      startedProcesses.add(await run(
+        [
+          expressionSuite.toString(),
+          "-Dfuzz=true",
+        ],
+        "expression suite",
+      ));
+    }
+  }
+
   // Wait for everything to finish.
   List<int> exitCodes =
       await Future.wait(startedProcesses.map((e) => e.process.exitCode));
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index 9b60daf..81a46c2 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -319,7 +319,8 @@
       "pattern": [
         "_fe_analyzer_shared/lib/.*\\.dart$",
         "front_end/lib/.*\\.dart$",
-        "kernel/lib/.*\\.dart$"
+        "kernel/lib/.*\\.dart$",
+        "kernel/bin/.*\\.dart$"
       ],
       "exclude": [
         "kernel/lib/vm/.*\\.dart$",
diff --git a/pkg/front_end/tool/fasta b/pkg/front_end/tool/fasta
index 29d5544..1a3d971 100755
--- a/pkg/front_end/tool/fasta
+++ b/pkg/front_end/tool/fasta
@@ -46,7 +46,7 @@
       set -- "$@" /dev/fd/1
     fi
     if [ "$#" != "3" ]; then
-      stop "Usage: $1 dillfile [output]"
+      stop "Usage: $1 dillFile [output]"
     fi
     ;;
   testing)
diff --git a/pkg/front_end/tool/fasta.dart b/pkg/front_end/tool/fasta.dart
index 3e02026..14f8503 100644
--- a/pkg/front_end/tool/fasta.dart
+++ b/pkg/front_end/tool/fasta.dart
@@ -63,7 +63,7 @@
     case 'dump-ir':
       script = '${kernelBin}/dump.dart';
       if (remainingArguments.isEmpty || remainingArguments.length > 2) {
-        stop("Usage: $command dillfile [output]");
+        stop("Usage: $command dillFile [output]");
       }
       break;
     case 'testing':
diff --git a/pkg/kernel/bin/compare_hierarchies.dart b/pkg/kernel/bin/compare_hierarchies.dart
new file mode 100644
index 0000000..e6e35ca
--- /dev/null
+++ b/pkg/kernel/bin/compare_hierarchies.dart
@@ -0,0 +1,163 @@
+#!/usr/bin/env dart
+// 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 'dart:io';
+
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/kernel.dart';
+import 'package:kernel/src/tool/command_line_util.dart';
+
+void usage() {
+  print("Compares the hierarchies of two dill files.");
+  print("");
+  print("Usage: dart <script> dillFile1.dill dillFile2.dill "
+      "[import:uri#classToIgnore|another:uri#andClassToIgnore]");
+  exit(1);
+}
+
+void main(List<String> args) {
+  CommandLineHelper.requireVariableArgumentCount([2, 3], args, usage);
+  CommandLineHelper.requireFileExists(args[0]);
+  CommandLineHelper.requireFileExists(args[1]);
+  Component binary1 = CommandLineHelper.tryLoadDill(args[0]);
+  Component binary2 = CommandLineHelper.tryLoadDill(args[1]);
+  Map<Uri, Set<String>> ignoresMap = {};
+  if (args.length >= 3) {
+    List<String> ignores = args[2].split("|");
+    for (String ignore in ignores) {
+      List<String> uriClassName = ignore.split("#");
+      if (uriClassName.length != 2) {
+        print("Ignoring '$ignore' as it doesn't conform to "
+            "'importUri#className'");
+        continue;
+      }
+      Uri uri = Uri.parse(uriClassName[0]);
+      String className = uriClassName[1];
+      (ignoresMap[uri] ??= {}).add(className);
+    }
+  }
+
+  print("(1): ${args[0]}");
+  print("(2): ${args[1]}");
+  print("");
+
+  ClosedWorldClassHierarchy ch1 =
+      new ClassHierarchy(binary1, new CoreTypes(binary1))
+          as ClosedWorldClassHierarchy;
+  ClosedWorldClassHierarchy ch2 =
+      new ClassHierarchy(binary2, new CoreTypes(binary2))
+          as ClosedWorldClassHierarchy;
+
+  Map<Uri, Library> libMap1 = createLibMap(binary1);
+  Map<Uri, Library> libMap2 = createLibMap(binary2);
+  Set<Uri> agreeingImportUris = new Set<Uri>.from(libMap1.keys)
+    ..retainAll(libMap2.keys);
+
+  for (Uri uri in agreeingImportUris) {
+    Library lib1 = libMap1[uri]!;
+    Library lib2 = libMap2[uri]!;
+    Map<String, Class> libClass1 =
+        createPublicClassMap(lib1, ignored: ignoresMap[uri]);
+    Map<String, Class> libClass2 =
+        createPublicClassMap(lib2, ignored: ignoresMap[uri]);
+    Set<String> agreeingClasses = new Set<String>.from(libClass1.keys)
+      ..retainAll(libClass2.keys);
+    if (agreeingClasses.length != libClass1.length ||
+        libClass1.length != libClass2.length) {
+      print("Missing classes in lib $uri");
+      Set<String> missing = new Set<String>.from(libClass1.keys)
+        ..removeAll(libClass2.keys);
+      if (missing.isNotEmpty) {
+        print("In (1) but not in (2): ${missing.toList()}");
+      }
+      missing = new Set<String>.from(libClass2.keys)..removeAll(libClass1.keys);
+      if (missing.isNotEmpty) {
+        print("In (2) but not in (1): ${missing.toList()}");
+      }
+      print("");
+    }
+
+    for (String className in agreeingClasses) {
+      Class c1 = libClass1[className]!;
+      Class c2 = libClass2[className]!;
+      Set<ClassReference> c1Supertypes = createClassReferenceSet(
+          ch1.getAllSupertypeClassesForTesting(c1),
+          onlyPublic: true,
+          ignoresMap: ignoresMap);
+      Set<ClassReference> c2Supertypes = createClassReferenceSet(
+          ch2.getAllSupertypeClassesForTesting(c2),
+          onlyPublic: true,
+          ignoresMap: ignoresMap);
+      Set<ClassReference> missing = new Set<ClassReference>.from(c1Supertypes)
+        ..removeAll(c2Supertypes);
+      if (missing.isNotEmpty) {
+        print("$c1 in $lib1 from (1) has these extra supertypes: "
+            "${missing.toList()}");
+      }
+      missing = new Set<ClassReference>.from(c2Supertypes)
+        ..removeAll(c1Supertypes);
+      if (missing.isNotEmpty) {
+        print("$c2 in $lib2 from (2) has these extra supertypes: "
+            "${missing.toList()}");
+      }
+    }
+  }
+}
+
+Map<Uri, Library> createLibMap(Component c) {
+  Map<Uri, Library> map = {};
+  for (Library lib in c.libraries) {
+    map[lib.importUri] = lib;
+  }
+  return map;
+}
+
+Map<String, Class> createPublicClassMap(Library lib,
+    {required Set<String>? ignored}) {
+  Map<String, Class> map = {};
+  for (Class c in lib.classes) {
+    if (c.name.startsWith("_")) continue;
+    if (ignored?.contains(c.name) ?? false) continue;
+    map[c.name] = c;
+  }
+  return map;
+}
+
+Set<ClassReference> createClassReferenceSet(List<Class> classes,
+    {required bool onlyPublic, required Map<Uri, Set<String>> ignoresMap}) {
+  Set<ClassReference> result = {};
+  for (Class c in classes) {
+    if (onlyPublic && c.name.startsWith("_")) continue;
+    Set<String>? ignored = ignoresMap[c.enclosingLibrary.importUri];
+    if (ignored?.contains(c.name) ?? false) continue;
+    result.add(new ClassReference(c.name, c.enclosingLibrary.importUri));
+  }
+  return result;
+}
+
+class ClassReference {
+  final String name;
+  final Uri libImportUri;
+
+  const ClassReference(this.name, this.libImportUri);
+
+  @override
+  int get hashCode => name.hashCode * 13 + libImportUri.hashCode * 17;
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    if (other is! ClassReference) return false;
+    if (name != other.name) return false;
+    if (libImportUri != other.libImportUri) return false;
+    return true;
+  }
+
+  @override
+  String toString() {
+    return "$name ($libImportUri)";
+  }
+}
diff --git a/pkg/kernel/bin/dill_forensic.dart b/pkg/kernel/bin/dill_forensic.dart
index c95d02a..b68b32f 100755
--- a/pkg/kernel/bin/dill_forensic.dart
+++ b/pkg/kernel/bin/dill_forensic.dart
@@ -12,16 +12,16 @@
 
 void main(List<String> args) {
   if (args.length != 1) {
-    throw "Usage: dart <script> <dillfile>";
+    throw "Usage: dart <script> <dillFile>";
   }
   File file = new File(args.single);
   if (!file.existsSync()) {
     throw "Given file doesn't exist.\n"
-        "Usage: dart <script> <dillfile>";
+        "Usage: dart <script> <dillFile>";
   }
   Uint8List bytes = file.readAsBytesSync();
   List<Component> components = splitAndRead(bytes);
-  print("Sucessfully read ${components.length} sub-components.");
+  print("Successfully read ${components.length} sub-components.");
 
   for (int i = 0; i < components.length; i++) {
     Component component = components[i];
@@ -77,7 +77,7 @@
   tagOffsets.add(bytes.length);
 
   // Warning: O(n²) algorithm (though, as the tag is assumed to be rather unique
-  // in normal cases it will probably much better in practise
+  // in normal cases it will probably much better in practice
   // (and n will be low)).
   int fromIndex = 0;
   while (fromIndex < tagOffsets.length - 1) {
diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart
index cf9320c..440da58 100644
--- a/pkg/kernel/lib/class_hierarchy.dart
+++ b/pkg/kernel/lib/class_hierarchy.dart
@@ -491,6 +491,25 @@
     return result;
   }
 
+  List<Class> getAllSupertypeClassesForTesting(Class class_) {
+    List<Class?> allClassesByIndex =
+        new List<Class?>.filled(_infoMap.length, null);
+    for (MapEntry<Class, _ClassInfo> c in _infoMap.entries) {
+      allClassesByIndex[c.value.topologicalIndex] = c.key;
+    }
+
+    List<Class> result = [];
+    Uint32List list = _infoMap[class_]!.supertypeIntervalList;
+    for (int i = 0; i < list.length; i += 2) {
+      int from = list[i];
+      int to = list[i + 1];
+      for (int j = from; j < to; j++) {
+        result.add(allClassesByIndex[j]!);
+      }
+    }
+    return result;
+  }
+
   _ClassInfo infoFor(Class cls) {
     _ClassInfo? info = _infoMap[cls];
     if (info == null) {
diff --git a/tools/VERSION b/tools/VERSION
index 02fd046..9a2d9f1 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 77
+PRERELEASE 78
 PRERELEASE_PATCH 0
\ No newline at end of file