Implement ClassHierarchyBuilder.getLegacyLeastUpperBound

Change-Id: Id946a8e49ef414e5d2f6439d15e6cb134c9ccb83
Reviewed-on: https://dart-review.googlesource.com/c/93425
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index a47bf6c..f902c31 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -195,6 +195,43 @@
         .substituteType(supertype.build(null));
   }
 
+  InterfaceType getKernelLegacyLeastUpperBound(
+      InterfaceType type1, InterfaceType type2) {
+    if (type1 == type2) return type1;
+    ClassHierarchyNode node1 = getNodeFromKernelClass(type1.classNode);
+    ClassHierarchyNode node2 = getNodeFromKernelClass(type2.classNode);
+    Set<ClassHierarchyNode> nodes1 = node1.computeAllSuperNodes(this).toSet();
+    List<ClassHierarchyNode> nodes2 = node2.computeAllSuperNodes(this);
+    List<ClassHierarchyNode> common = <ClassHierarchyNode>[];
+
+    for (int i = 0; i < nodes2.length; i++) {
+      ClassHierarchyNode node = nodes2[i];
+      if (node == null) continue;
+      if (nodes1.contains(node)) {
+        DartType candidate1 = getKernelTypeAsInstanceOf(type1, node.cls.target);
+        DartType candidate2 = getKernelTypeAsInstanceOf(type2, node.cls.target);
+        if (candidate1 == candidate2) {
+          common.add(node);
+        }
+      }
+    }
+
+    if (common.length == 1) return objectKernelClass.rawType;
+    common.sort(ClassHierarchyNode.compareMaxInheritancePath);
+
+    for (int i = 0; i < common.length - 1; i++) {
+      ClassHierarchyNode node = common[i];
+      if (node.maxInheritancePath != common[i + 1].maxInheritancePath) {
+        return getKernelTypeAsInstanceOf(type1, node.cls.target);
+      } else {
+        do {
+          i++;
+        } while (node.maxInheritancePath == common[i + 1].maxInheritancePath);
+      }
+    }
+    return objectKernelClass.rawType;
+  }
+
   static ClassHierarchyBuilder build(
       KernelClassBuilder objectClass,
       List<KernelClassBuilder> classes,
@@ -867,6 +904,27 @@
       this.interfaces,
       this.maxInheritancePath);
 
+  /// Returns a list of all supertypes of [cls], including this node.
+  List<ClassHierarchyNode> computeAllSuperNodes(
+      ClassHierarchyBuilder hierarchy) {
+    List<ClassHierarchyNode> result = new List<ClassHierarchyNode>(
+        1 + superclasses.length + interfaces.length);
+    for (int i = 0; i < superclasses.length; i++) {
+      Declaration declaration = superclasses[i].declaration;
+      if (declaration is KernelClassBuilder) {
+        result[i] = hierarchy.getNodeFromClass(declaration);
+      }
+    }
+    for (int i = 0; i < interfaces.length; i++) {
+      Declaration declaration = interfaces[i].declaration;
+      if (declaration is KernelClassBuilder) {
+        result[i + superclasses.length] =
+            hierarchy.getNodeFromClass(declaration);
+      }
+    }
+    return result..last = this;
+  }
+
   String toString([StringBuffer sb]) {
     sb ??= new StringBuffer();
     sb
@@ -922,6 +980,11 @@
         ..writeln();
     }
   }
+
+  static int compareMaxInheritancePath(
+      ClassHierarchyNode a, ClassHierarchyNode b) {
+    return b.maxInheritancePath.compareTo(a.maxInheritancePath);
+  }
 }
 
 class MergeResult {
@@ -1058,7 +1121,6 @@
   @override
   InterfaceType getLegacyLeastUpperBound(
       InterfaceType type1, InterfaceType type2) {
-    // TODO(ahe): Compute the actual LUB.
-    return type1;
+    return hierarchy.getKernelLegacyLeastUpperBound(type1, type2);
   }
 }
diff --git a/pkg/front_end/test/fasta/types/fasta_legacy_upper_bound_test.dart b/pkg/front_end/test/fasta/types/fasta_legacy_upper_bound_test.dart
new file mode 100644
index 0000000..5e53470
--- /dev/null
+++ b/pkg/front_end/test/fasta/types/fasta_legacy_upper_bound_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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" show DartType;
+
+import "package:kernel/core_types.dart" show CoreTypes;
+
+import "package:kernel/target/targets.dart" show NoneTarget, TargetFlags;
+
+import "package:front_end/src/api_prototype/compiler_options.dart"
+    show CompilerOptions;
+
+import "package:front_end/src/base/processed_options.dart"
+    show ProcessedOptions;
+
+import "package:front_end/src/fasta/compiler_context.dart" show CompilerContext;
+
+import "package:front_end/src/fasta/dill/dill_loader.dart" show DillLoader;
+
+import "package:front_end/src/fasta/dill/dill_target.dart" show DillTarget;
+
+import "package:front_end/src/fasta/kernel/kernel_builder.dart"
+    show ClassHierarchyBuilder, KernelClassBuilder;
+
+import "package:front_end/src/fasta/ticker.dart" show Ticker;
+
+import "legacy_upper_bound_helper.dart" show LegacyUpperBoundTest;
+
+class FastaLegacyUpperBoundTest extends LegacyUpperBoundTest {
+  final Ticker ticker;
+  final CompilerContext context;
+
+  ClassHierarchyBuilder hierarchy;
+
+  FastaLegacyUpperBoundTest(this.ticker, this.context);
+
+  @override
+  Future<void> parseComponent(String source) async {
+    await super.parseComponent(source);
+
+    DillTarget target = new DillTarget(
+        ticker,
+        await context.options.getUriTranslator(),
+        new NoneTarget(new TargetFlags()));
+    final DillLoader loader = target.loader;
+    loader.appendLibraries(component);
+    await target.buildOutlines();
+    KernelClassBuilder objectClass = loader.coreLibrary["Object"];
+    hierarchy = new ClassHierarchyBuilder(
+        objectClass, loader, new CoreTypes(component));
+  }
+
+  @override
+  DartType getLegacyLeastUpperBound(DartType a, DartType b) {
+    return hierarchy.getKernelLegacyLeastUpperBound(a, b);
+  }
+}
+
+main() {
+  final Ticker ticker = new Ticker();
+  final CompilerContext context = new CompilerContext(new ProcessedOptions(
+      options: new CompilerOptions()
+        ..packagesFileUri = Uri.base.resolve(".packages")));
+  context.runInContext<void>(
+      (_) => new FastaLegacyUpperBoundTest(ticker, context).test());
+}
diff --git a/pkg/front_end/test/fasta/types/kernel_type_parser.dart b/pkg/front_end/test/fasta/types/kernel_type_parser.dart
index 5d77b7f..5bf1d1c 100644
--- a/pkg/front_end/test/fasta/types/kernel_type_parser.dart
+++ b/pkg/front_end/test/fasta/types/kernel_type_parser.dart
@@ -49,6 +49,7 @@
   KernelEnvironment libraryEnvironment =
       new KernelEnvironment(uri, uri).extend(coreEnvironment.declarations);
   Library library = parseLibrary(uri, source, environment: libraryEnvironment);
+  library.name = "lib";
   return new Component(libraries: <Library>[coreLibrary, library]);
 }