[dart2js] Add inferrer engine changes for type graph call linearization.

Change-Id: I23c1f12f4b619be9f4409b23f83cf54cd0fcc301
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/267062
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart
index 5871f23..bae68f9 100644
--- a/pkg/compiler/lib/src/closure.dart
+++ b/pkg/compiler/lib/src/closure.dart
@@ -23,7 +23,8 @@
   void writeToDataSink(DataSinkWriter sink);
 
   /// Look up information about the variables that have been mutated and are
-  /// used inside the scope of [node].
+  /// used inside the scope of [node]. Assumes member is not abstract and
+  /// therefore scope info exists.
   ScopeInfo getScopeInfo(MemberEntity member);
 
   ClosureRepresentationInfo getClosureInfo(ir.LocalFunction localFunction);
diff --git a/pkg/compiler/lib/src/inferrer_experimental/builder.dart b/pkg/compiler/lib/src/inferrer_experimental/builder.dart
index 7e49e67..02a512f 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/builder.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/builder.dart
@@ -225,10 +225,12 @@
     // be handled specially, in that we are computing their LUB at
     // each update, and reading them yields the type that was found in a
     // previous analysis of [outermostElement].
-    ScopeInfo scopeInfo = _closureDataLookup.getScopeInfo(_analyzedMember);
-    scopeInfo.forEachBoxedVariable(_localsMap, (variable, field) {
-      _capturedAndBoxed[variable] = field;
-    });
+    if (!_analyzedMember.isAbstract) {
+      ScopeInfo scopeInfo = _closureDataLookup.getScopeInfo(_analyzedMember);
+      scopeInfo.forEachBoxedVariable(_localsMap, (variable, field) {
+        _capturedAndBoxed[variable] = field;
+      });
+    }
 
     return visit(_analyzedNode)!;
   }
diff --git a/pkg/compiler/lib/src/inferrer_experimental/engine.dart b/pkg/compiler/lib/src/inferrer_experimental/engine.dart
index 34d7e38..cb9e1a0 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/engine.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/engine.dart
@@ -25,6 +25,7 @@
 import '../options.dart';
 import '../serialization/serialization.dart';
 import '../universe/call_structure.dart';
+import '../universe/member_hierarchy.dart';
 import '../universe/selector.dart';
 import '../universe/side_effects.dart';
 import '../inferrer/abstract_value_domain.dart';
@@ -80,7 +81,9 @@
   /// The maximum number of times we allow a node in the graph to
   /// change types. If a node reaches that limit, we give up
   /// inferencing on it and give it the dynamic type.
-  final int _MAX_CHANGE_COUNT = 6;
+  /// TODO(natebiggs): This value is needed right now because some types
+  /// do not converge. See https://github.com/dart-lang/sdk/issues/50626
+  final int _MAX_CHANGE_COUNT = 12;
 
   int _overallRefineCount = 0;
   int _addedInGraph = 0;
@@ -108,6 +111,8 @@
   // [ClosureWorldRefiner].
   NoSuchMethodData get noSuchMethodData => closedWorld.noSuchMethodData;
 
+  final MemberHierarchyBuilder memberHierarchyBuilder;
+
   InferrerEngine(
       this._options,
       this._progress,
@@ -118,7 +123,8 @@
       this.globalLocalsMap,
       this.inferredDataBuilder)
       : this.types = TypeSystem(closedWorld,
-            KernelTypeSystemStrategy(closedWorld, globalLocalsMap));
+            KernelTypeSystemStrategy(closedWorld, globalLocalsMap)),
+        memberHierarchyBuilder = MemberHierarchyBuilder(closedWorld);
 
   /// Applies [f] to all elements in the universe that match [selector] and
   /// [mask]. If [f] returns false, aborts the iteration.
@@ -309,6 +315,8 @@
   }
 
   void _runOverAllElements() {
+    metrics.memberHierarchy
+        .measure(() => memberHierarchyBuilder.init(_joinOverriddenMember));
     metrics.analyze.measure(_analyzeAllElements);
     final dump =
         debug.PRINT_GRAPH ? TypeGraphDump(_compilerOutput, this) : null;
@@ -317,6 +325,10 @@
     _buildWorkQueue();
     metrics.refine1.measure(_refine);
 
+    // Update overrides that need to be considered for closurization before
+    // tracing.
+    _updateOverrideClosurizations();
+
     metrics.trace.measure(() {
       // Try to infer element types of lists and compute their escape information.
       types.allocatedLists.values.forEach((ListTypeInformation info) {
@@ -397,7 +409,7 @@
             // of this closure call are not a root to trace but an intermediate
             // for some other function.
             Iterable<FunctionEntity> elements = List<FunctionEntity>.from(
-                info.callees.where((e) => e.isFunction));
+                info.callees.where((e) => e.isFunction && !e.isAbstract));
             trace(elements, ClosureTracerVisitor(elements, info, this));
           }
         } else if (info is MemberTypeInformation) {
@@ -429,6 +441,10 @@
     _workQueue.addAll(seenTypes);
     metrics.refine2.measure(_refine);
 
+    // Update overrides that need to be considered for closurization after
+    // the final round of refines.
+    _updateOverrideClosurizations();
+
     if (debug.PRINT_SUMMARY) {
       types.allocatedLists.values.forEach((_info) {
         ListTypeInformation info = _info;
@@ -494,11 +510,11 @@
 
   /// Call [analyze] for all live members.
   void _analyzeAllElements() {
-    Iterable<MemberEntity> processedMembers = closedWorld.processedMembers
-        .where((MemberEntity member) => !member.isAbstract);
-
     _progress.startPhase();
-    processedMembers.forEach((MemberEntity member) {
+    final toProcess = closedWorld.processedMembers
+        .followedBy(closedWorld.liveAbstractInstanceMembers)
+        .toSet();
+    toProcess.forEach((MemberEntity member) {
       _progress.showProgress(
           'Added ', _addedInGraph, ' elements in inferencing graph.');
       // This also forces the creation of the [ElementTypeInformation] to ensure
@@ -616,12 +632,13 @@
       }
     } else {
       final method = element as FunctionEntity;
-      recordReturnType(method, type!);
+      // Abstract methods don't have a body so they won't have a return type.
+      if (!method.isAbstract) recordReturnType(method, type!);
     }
   }
 
   /// Visits [body] to compute the [TypeInformation] node for [member].
-  TypeInformation? _computeMemberTypeInformation(
+  TypeInformation _computeMemberTypeInformation(
       MemberEntity member, ir.Node? body) {
     KernelTypeGraphBuilder visitor = KernelTypeGraphBuilder(
         _options,
@@ -742,12 +759,13 @@
   }
 
   /// Update the inputs to parameters in the graph. [remove] tells whether
-  /// inputs must be added or removed. If [init] is false, parameters are
-  /// added to the work queue.
-  void updateParameterInputs(TypeInformation caller, MemberEntity callee,
+  /// inputs must be added or removed. If [addToQueue] is `true`, parameters are
+  /// added to the work queue. Returns `true` if the call requires [callee] to
+  /// be closurized.
+  bool updateParameterInputs(TypeInformation callSiteType, MemberEntity callee,
       ArgumentsTypes? arguments, Selector? selector,
       {required bool remove, required bool addToQueue}) {
-    if (callee.name == Identifiers.noSuchMethod_) return;
+    if (callee.name == Identifiers.noSuchMethod_) return false;
     if (callee is FieldEntity) {
       if (selector!.isSetter) {
         ElementTypeInformation info = types.getInferredTypeOfMember(callee);
@@ -759,30 +777,14 @@
         if (addToQueue) _workQueue.add(info);
       }
     } else if (callee.isGetter) {
-      return;
+      return false;
     } else if (selector != null && selector.isGetter) {
       // We are tearing a function off and thus create a closure.
       assert(callee.isFunction);
-      MemberTypeInformation info = types.getInferredTypeOfMember(callee);
-      if (remove) {
-        info.closurizedCount--;
-      } else {
-        info.closurizedCount++;
-        if (callee.isStatic || callee.isTopLevel) {
-          types.allocatedClosures.add(info);
-        } else {
-          // We add the call-site type information here so that we
-          // can benefit from further refinement of the selector.
-          types.allocatedClosures.add(caller);
-        }
-        types.strategy.forEachParameter(callee as FunctionEntity,
-            (Local parameter) {
-          ParameterTypeInformation info =
-              types.getInferredTypeOfParameter(parameter);
-          info.tagAsTearOffClosureParameter(this);
-          if (addToQueue) _workQueue.add(info);
-        });
-      }
+      final memberInfo = types.getInferredTypeOfMember(callee);
+      _markForClosurization(memberInfo, callSiteType,
+          remove: remove, addToQueue: addToQueue);
+      return true;
     } else {
       final method = callee as FunctionEntity;
       ParameterStructure parameterStructure = method.parameterStructure;
@@ -808,6 +810,160 @@
         if (addToQueue) _workQueue.add(info);
       });
     }
+    return false;
+  }
+
+  void _joinOverrideParameters(MemberEntity parent, MemberEntity override) {
+    final method = parent as FunctionEntity;
+    ParameterStructure parameterStructure = method.parameterStructure;
+    int parameterIndex = 0;
+    // Collect the parent parameter type infos.
+    final List<TypeInformation> positional = [];
+    final Map<String, TypeInformation> named = {};
+    types.strategy.forEachParameter(parent, (Local parameter) {
+      TypeInformation type = types.getInferredTypeOfParameter(parameter);
+      if (parameterIndex < parameterStructure.requiredPositionalParameters) {
+        positional.add(type);
+      } else if (parameterStructure.namedParameters.isNotEmpty) {
+        named[parameter.name!] = type;
+      } else if (parameterIndex < parameterStructure.positionalParameters) {
+        positional.add(type);
+      }
+      parameterIndex++;
+    });
+    parameterIndex = 0;
+
+    // Add the parent parameter type infos as inputs to the override's
+    // parameters.
+    types.strategy.forEachParameter(override as FunctionEntity,
+        (Local parameter) {
+      TypeInformation? parentParamInfo;
+      if (parameterIndex < parameterStructure.requiredPositionalParameters) {
+        parentParamInfo = positional[parameterIndex];
+      } else if (parameterStructure.namedParameters.isNotEmpty) {
+        parentParamInfo = named[parameter.name];
+      } else if (parameterIndex < positional.length) {
+        parentParamInfo = positional[parameterIndex];
+      }
+      // If the override includes parameters that the parent doesn't
+      // (optional parameters) then use the override's default type as any
+      // default value will be used within the body of the override.
+      parentParamInfo ??= getDefaultTypeOfParameter(parameter);
+      TypeInformation overrideParamInfo =
+          types.getInferredTypeOfParameter(parameter);
+      overrideParamInfo.addInput(parentParamInfo);
+      parameterIndex++;
+    });
+  }
+
+  /// Adds edges between [parent] and [override] based on the type of member
+  /// each is.
+  ///
+  /// Possible override configurations (parent/override):
+  /// - field/getter
+  /// - field/setter
+  /// - field/field
+  /// - getter/getter
+  /// - getter/field
+  /// - setter/setter
+  /// - setter/field
+  /// - method/method
+  void _joinOverriddenMember(MemberEntity parent, MemberEntity override) {
+    if (parent.name == Identifiers.noSuchMethod_) return;
+    final parentType = types.getInferredTypeOfMember(parent);
+    final overrideType = types.getInferredTypeOfMember(override);
+    if (parent is FieldEntity) {
+      if (override.isGetter) {
+        parentType.addInput(overrideType);
+      } else if (override.isSetter) {
+        types.strategy.forEachParameter(override as FunctionEntity,
+            (Local parameter) {
+          final paramInfo = types.getInferredTypeOfParameter(parameter);
+          paramInfo.addInput(parentType);
+        });
+      } else {
+        assert(override is FieldEntity);
+        parentType.addInput(overrideType);
+        if (parent.isAssignable) {
+          // Parent has an implicit setter so the types of set values need to
+          // flow into the override.
+          overrideType.addInput(parentType);
+        }
+      }
+    } else if (parent.isGetter) {
+      assert(override.isGetter || override is FieldEntity);
+      parentType.addInput(overrideType);
+    } else if (parent.isSetter) {
+      if (override.isSetter) {
+        _joinOverrideParameters(parent, override);
+      } else {
+        assert(override is FieldEntity);
+        types.strategy.forEachParameter(parent as FunctionEntity,
+            (Local parameter) {
+          final paramInfo = types.getInferredTypeOfParameter(parameter);
+          overrideType.addInput(paramInfo);
+        });
+      }
+    } else {
+      assert(parent.isFunction && override.isFunction);
+      parentType.addInput(overrideType);
+      _joinOverrideParameters(parent, override);
+    }
+  }
+
+  void _markForClosurization(
+      MemberTypeInformation memberInfo, TypeInformation callSiteType,
+      {required bool remove, required bool addToQueue}) {
+    final member = memberInfo.member;
+    if (remove) {
+      memberInfo.closurizedCount--;
+    } else {
+      memberInfo.closurizedCount++;
+      if (member.isStatic || member.isTopLevel) {
+        types.allocatedClosures.add(memberInfo);
+      } else {
+        // We add the call-site type information here so that we
+        // can benefit from further refinement of the selector.
+        types.allocatedClosures.add(callSiteType);
+      }
+      types.strategy.forEachParameter(member as FunctionEntity,
+          (Local parameter) {
+        ParameterTypeInformation info =
+            types.getInferredTypeOfParameter(parameter);
+        info.tagAsTearOffClosureParameter(this);
+        if (addToQueue) _workQueue.add(info);
+      });
+    }
+  }
+
+  void _registerOverridesCalled(
+      MemberEntity callee,
+      DynamicCallSiteTypeInformation callSiteType,
+      ir.Node? callSite,
+      Set<MemberEntity> visited,
+      {required bool isClosurized}) {
+    memberHierarchyBuilder.forEachOverride(callee, (override) {
+      if (override.isAbstract || !visited.add(override)) return;
+      MemberTypeInformation info = types.getInferredTypeOfMember(override);
+      info.addCall(callSiteType.caller, callSite);
+
+      if (isClosurized) {
+        _markForClosurization(info, callSiteType,
+            remove: false, addToQueue: false);
+      }
+    });
+  }
+
+  void _updateOverrideClosurizations() {
+    final Set<MemberEntity> visited = {};
+    for (final call in types.allocatedCalls) {
+      if (call is! DynamicCallSiteTypeInformation) continue;
+      for (final target in call.callees) {
+        if (!visited.add(target)) continue;
+        _registerOverridesCalled(target, call, null, visited,
+            isClosurized: call.closurizedTargets.contains(target));
+      }
+    }
   }
 
   /// Sets the type of a parameter's default value to [type]. If the global
@@ -871,8 +1027,7 @@
       // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'.
       info.addInput(types.boolType);
     }
-
-    if (info.inputs.isEmpty) info.addInput(type);
+    info.addInput(type);
   }
 
   /// Notifies to the inferrer that [analyzedElement] can have return type
@@ -1140,6 +1295,7 @@
 class _InferrerEngineMetrics extends MetricsBase {
   final time = DurationMetric('time');
   final analyze = DurationMetric('time.analyze');
+  final memberHierarchy = DurationMetric('time.memberHierarchy');
   final refine1 = DurationMetric('time.refine1');
   final trace = DurationMetric('time.trace');
   final refine2 = DurationMetric('time.refine2');
@@ -1152,6 +1308,7 @@
     primary = [time, ...subMetrics.primary];
     secondary = [
       analyze,
+      memberHierarchy,
       refine1,
       trace,
       refine2,
diff --git a/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart b/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart
index 69c1b89..1555457 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart
@@ -104,7 +104,7 @@
       int.fromEnvironment('dart2js.tracing.limit', defaultValue: 32);
   // TODO(natebiggs): We allow null here to maintain current functionality
   // but we should verify we actually need to allow it.
-  final Setlet<MemberEntity> analyzedElements = Setlet<MemberEntity>();
+  final Setlet<MemberEntity?> analyzedElements = Setlet<MemberEntity?>();
 
   TracerVisitor(this.tracedType, this.inferrer);
 
@@ -163,7 +163,7 @@
         break;
       }
       for (final info in user.users) {
-        analyzedElements.add(info.owner!);
+        analyzedElements.add(info.owner);
         info.accept(this);
       }
       while (!listsToAnalyze.isEmpty) {
diff --git a/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart
index d9cdf1f..01d3fc4 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart
@@ -1095,6 +1095,9 @@
     callee.removeCall(caller, _call!);
   }
 
+  // TODO(natebiggs): Populate this below.
+  late final Set<MemberEntity> closurizedTargets = {};
+
   @override
   void addToGraph(InferrerEngine inferrer) {
     assert((receiver as dynamic) != null); // TODO(48820): Remove when sound.
diff --git a/pkg/compiler/lib/src/inferrer_experimental/type_system.dart b/pkg/compiler/lib/src/inferrer_experimental/type_system.dart
index e7aec78..68d1039 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/type_system.dart
@@ -331,8 +331,6 @@
   }
 
   MemberTypeInformation getInferredTypeOfMember(MemberEntity member) {
-    assert(!member.isAbstract,
-        failedAt(member, "Unexpected abstract member $member."));
     return memberTypeInformations[member] ??= _getInferredTypeOfMember(member);
   }
 
diff --git a/pkg/compiler/lib/src/inferrer_experimental/types.dart b/pkg/compiler/lib/src/inferrer_experimental/types.dart
index 9ae851a..f7197f9 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/types.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/types.dart
@@ -323,7 +323,7 @@
         (MemberEntity member, GlobalTypeInferenceMemberResult result) =>
             result.writeToDataSink(
                 sink,
-                elementMap.getMemberContextNode(member)!,
+                elementMap.getMemberContextNode(member),
                 closedWorld.abstractValueDomain)));
     sink.writeDeferrable(() => sink.writeLocalMap(
         _parameterResults.loaded(),