[vm,dynamic_modules] Support members which are overridden implictly (transitively) by a dynamic module

Consider the following situation: member M1 is overridden by another
member M2; M2 is overridden in a dynamic module.

Members which can be overridden in a dynamic module (such as M2)
should be specified as 'can-be-overridden' in the dynamic interface.
Members which are overridden implicitly/transitively (such as M1)
are not required to be mentioned in the dynamic interface.
However, when determining possible targets for a call with
interface target M1, compiler should treat it as potentially
overridden in a dynamic module.

This change adds such handling to the VM/AOT. Dynamic interface
annotator now marks members such as M1 with
'dyn-module:can-be-overridden-implicitly' pragma, and
VM/AOT takes both can-be-overridden and can-be-overridden-implicitly
into account.

This change also simplifies handling of implicitly extenable classes
in the VM/AOT - now VM handles both extendable and implicitly-extendable
pragmas (from dynamic interface annotator) instead of recalculating
implicitly extendable classes on its own.

TEST=pkg/vm/test/transformations/dynamic_interface_annotator_test.dart
TEST=dynamic_modules_suite/implicitly_extendable

Fixes https://github.com/dart-lang/sdk/issues/59880

Change-Id: Id4570cc86303f8e45a061d696e9bca0d0b2b4b81
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403951
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
diff --git a/pkg/vm/lib/transformations/dynamic_interface_annotator.dart b/pkg/vm/lib/transformations/dynamic_interface_annotator.dart
index 3da52ca..66734dc 100644
--- a/pkg/vm/lib/transformations/dynamic_interface_annotator.dart
+++ b/pkg/vm/lib/transformations/dynamic_interface_annotator.dart
@@ -5,6 +5,7 @@
 import 'package:front_end/src/api_prototype/dynamic_module_validator.dart'
     show DynamicInterfaceSpecification;
 import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 import 'package:kernel/core_types.dart' show CoreTypes;
 
 import 'pragma.dart'
@@ -13,7 +14,8 @@
         kDynModuleCallablePragmaName,
         kDynModuleExtendablePragmaName,
         kDynModuleImplicitlyCallablePragmaName,
-        kDynModuleImplicitlyExtendablePragmaName;
+        kDynModuleImplicitlyExtendablePragmaName,
+        kDynModuleCanBeOverriddenImplicitlyPragmaName;
 
 const bool _debug = false;
 
@@ -33,13 +35,28 @@
       annotateFinalClasses: false,
       annotateStaticMembers: false,
       annotateInstanceMembers: false);
-  annotateImplicitlyExtendable(coreTypes, extendableAnnotator.annotatedClasses);
-  annotateNodes(spec.canBeOverridden, kDynModuleCanBeOverriddenPragmaName,
-      baseUri, coreTypes,
+
+  _ImplicitExtendableAnnotator(
+          pragmaConstant(coreTypes, kDynModuleImplicitlyExtendablePragmaName),
+          extendableAnnotator.annotatedClasses)
+      .annotate();
+
+  final canBeOverriddenAnnotator = annotateNodes(spec.canBeOverridden,
+      kDynModuleCanBeOverriddenPragmaName, baseUri, coreTypes,
       annotateClasses: false,
       annotateFinalClasses: true,
       annotateStaticMembers: false,
       annotateInstanceMembers: true);
+
+  final hierarchy = ClassHierarchy(component, coreTypes);
+  pragmaConstant(coreTypes, kDynModuleCanBeOverriddenImplicitlyPragmaName);
+  _ImplicitOverridesAnnotator(
+          pragmaConstant(
+              coreTypes, kDynModuleCanBeOverriddenImplicitlyPragmaName),
+          hierarchy,
+          canBeOverriddenAnnotator.annotatedMembers)
+      .annotate();
+
   final callableAnnotator = annotateNodes(
       spec.callable, kDynModuleCallablePragmaName, baseUri, coreTypes,
       annotateClasses: true,
@@ -171,20 +188,77 @@
   }
 }
 
-void annotateImplicitlyExtendable(
-    CoreTypes coreTypes, Set<Class> extendableClasses) {
-  final pragma =
-      pragmaConstant(coreTypes, kDynModuleImplicitlyExtendablePragmaName);
-  for (final cls in [...extendableClasses]) {
+class _ImplicitExtendableAnnotator {
+  final Constant pragma;
+  final Set<Class> extendableClasses;
+  final Set<Class> implicitlyExtendable = Set<Class>.identity();
+
+  _ImplicitExtendableAnnotator(this.pragma, this.extendableClasses);
+
+  void annotate() {
+    for (final cls in extendableClasses) {
+      annotateSupertypesOf(cls);
+    }
+  }
+
+  void annotateSupertypesOf(Class cls) {
     for (final supertype in cls.supers) {
       final supertypeClass = supertype.classNode;
-      if (extendableClasses.add(supertypeClass)) {
+      if (implicitlyExtendable.add(supertypeClass) &&
+          !extendableClasses.contains(supertypeClass)) {
         supertypeClass.addAnnotation(ConstantExpression(pragma));
+        annotateSupertypesOf(supertypeClass);
       }
     }
   }
 }
 
+class _ImplicitOverridesAnnotator {
+  final Constant pragma;
+  final ClassHierarchy hierarchy;
+  final Set<Member> overriddenMembers;
+  final Set<Member> implicitlyOverriddenSetters = Set<Member>.identity();
+  final Set<Member> implicitlyOverriddenNonSetters = Set<Member>.identity();
+
+  _ImplicitOverridesAnnotator(
+      this.pragma, this.hierarchy, this.overriddenMembers);
+
+  void annotate() {
+    for (final member in overriddenMembers) {
+      final cls = member.enclosingClass!;
+      if (member.hasGetter) {
+        annotateSupertypesOf(cls, member.name, setter: false);
+      }
+      if (member.hasSetter) {
+        annotateSupertypesOf(cls, member.name, setter: true);
+      }
+    }
+  }
+
+  void annotateSupertypesOf(Class cls, Name memberName,
+      {required bool setter}) {
+    for (final supertype in cls.supers) {
+      final supertypeClass = supertype.classNode;
+      final member = ClassHierarchy.findMemberByName(
+          hierarchy.getDeclaredMembers(supertypeClass, setters: setter),
+          memberName);
+      if (member != null) {
+        final implicitlyOverridden = setter
+            ? implicitlyOverriddenSetters
+            : implicitlyOverriddenNonSetters;
+        if (implicitlyOverridden.add(member) &&
+            !overriddenMembers.contains(member)) {
+          member.addAnnotation(ConstantExpression(pragma));
+        } else {
+          // The member is already annotated - do not go deeper.
+          continue;
+        }
+      }
+      annotateSupertypesOf(supertypeClass, memberName, setter: setter);
+    }
+  }
+}
+
 class _ImplicitUsesAnnotator extends RecursiveVisitor {
   final Constant pragma;
 
diff --git a/pkg/vm/lib/transformations/pragma.dart b/pkg/vm/lib/transformations/pragma.dart
index 152b8ad..f98aaf2 100644
--- a/pkg/vm/lib/transformations/pragma.dart
+++ b/pkg/vm/lib/transformations/pragma.dart
@@ -26,6 +26,8 @@
 const kDynModuleImplicitlyExtendablePragmaName =
     "dyn-module:implicitly-extendable";
 const kDynModuleCanBeOverriddenPragmaName = "dyn-module:can-be-overridden";
+const kDynModuleCanBeOverriddenImplicitlyPragmaName =
+    "dyn-module:can-be-overridden-implicitly";
 const kDynModuleCallablePragmaName = "dyn-module:callable";
 const kDynModuleImplicitlyCallablePragmaName = "dyn-module:implicitly-callable";
 const kDynModuleEntryPointPragmaName = "dyn-module:entry-point";
diff --git a/pkg/vm/testcases/transformations/dynamic_interface_annotator/dynamic_interface.yaml b/pkg/vm/testcases/transformations/dynamic_interface_annotator/dynamic_interface.yaml
index c4d2f14..fb9265b 100644
--- a/pkg/vm/testcases/transformations/dynamic_interface_annotator/dynamic_interface.yaml
+++ b/pkg/vm/testcases/transformations/dynamic_interface_annotator/dynamic_interface.yaml
@@ -18,7 +18,7 @@
 
 can-be-overridden:
   - library: 'lib1.dart'
-    class: ['A', 'U']
+    class: ['A', 'Q', 'U']
   - library: 'lib2.dart'
     class: 'D'
     member: 'build'
diff --git a/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart b/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart
index c23fe99..9d0891f 100644
--- a/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart
+++ b/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart
@@ -79,9 +79,17 @@
 
 class O extends N {}
 
-class P {}
+class P1 implements P2 {
+  void foo() {}
+}
 
-class Q implements P {}
+abstract class P2 {
+  void foo();
+}
+
+class Q implements P1 {
+  void foo() {}
+}
 
 final class R {}
 
diff --git a/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart.expect b/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart.expect
index 7d07468..c76db5a 100644
--- a/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart.expect
+++ b/pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart.expect
@@ -141,19 +141,36 @@
 }
 @#C11
 @#C5
-class P extends core::Object {
+class P1 extends core::Object implements self::P2 {
   @#C5
-  synthetic constructor •() → self::P
+  synthetic constructor •() → self::P1
     : super core::Object::•()
     ;
+  @#C13
+  @#C5
+  method foo() → void {}
+}
+@#C11
+@#C5
+abstract class P2 extends core::Object {
+  @#C5
+  synthetic constructor •() → self::P2
+    : super core::Object::•()
+    ;
+  @#C13
+  @#C5
+  abstract method foo() → void;
 }
 @#C3
 @#C5
-class Q extends core::Object implements self::P {
+class Q extends core::Object implements self::P1 {
   @#C5
   synthetic constructor •() → self::Q
     : super core::Object::•()
     ;
+  @#C7
+  @#C5
+  method foo() → void {}
 }
 @#C5
 final class R extends core::Object {
@@ -199,7 +216,7 @@
 static field core::Object? sfield9;
 static field core::Object? _sfield10;
 @#C5
-static const field core::List<core::Object> const11 = #C16;
+static const field core::List<core::Object> const11 = #C18;
 @#C5
 static method smethod10() → void {}
 static method _smethod11() → void {}
@@ -215,9 +232,11 @@
   #C9 = core::pragma {name:#C8, options:#C2}
   #C10 = "dyn-module:implicitly-extendable"
   #C11 = core::pragma {name:#C10, options:#C2}
-  #C12 = 2
-  #C13 = 1
-  #C14 = self::_E2 {_y:#C12, _x:#C13}
-  #C15 = redirecting-factory-tearoff self::_F::_foo
-  #C16 = <core::Object>[#C14, #C15]
+  #C12 = "dyn-module:can-be-overridden-implicitly"
+  #C13 = core::pragma {name:#C12, options:#C2}
+  #C14 = 2
+  #C15 = 1
+  #C16 = self::_E2 {_y:#C14, _x:#C15}
+  #C17 = redirecting-factory-tearoff self::_F::_foo
+  #C18 = <core::Object>[#C16, #C17]
 }
diff --git a/runtime/tests/vm/dart/dynamic_module_pragmas_il_test.dart b/runtime/tests/vm/dart/dynamic_module_pragmas_il_test.dart
index 25190b6..76ad80c 100644
--- a/runtime/tests/vm/dart/dynamic_module_pragmas_il_test.dart
+++ b/runtime/tests/vm/dart/dynamic_module_pragmas_il_test.dart
@@ -55,8 +55,10 @@
   myprint(obj is C1);
 }
 
+@pragma('dyn-module:implicitly-extendable')
 abstract class A2 {
   void foo();
+  @pragma('dyn-module:can-be-overridden-implicitly')
   void bar();
   void baz();
 }
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 5a13e50..b63685c 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -589,10 +589,6 @@
   if (is_future_subtype && !cls.is_abstract()) {
     MarkClassCanBeFuture(zone, cls);
   }
-  if (cls.is_dynamically_extendable()) {
-    MarkClassHasDynamicallyExtendableSubtypes(zone, cls);
-  }
-
   ClassHiearchyUpdater(zone).Register(cls);
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
@@ -687,25 +683,6 @@
   }
 }
 
-void ClassFinalizer::MarkClassHasDynamicallyExtendableSubtypes(
-    Zone* zone,
-    const Class& cls) {
-  if (cls.has_dynamically_extendable_subtypes()) return;
-
-  cls.set_has_dynamically_extendable_subtypes(true);
-
-  Class& base = Class::Handle(zone, cls.SuperClass());
-  if (!base.IsNull()) {
-    MarkClassHasDynamicallyExtendableSubtypes(zone, base);
-  }
-  auto& interfaces = Array::Handle(zone, cls.interfaces());
-  auto& type = AbstractType::Handle(zone);
-  for (intptr_t i = 0; i < interfaces.Length(); ++i) {
-    type ^= interfaces.At(i);
-    base = type.type_class();
-    MarkClassHasDynamicallyExtendableSubtypes(zone, base);
-  }
-}
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
 void ClassFinalizer::FinalizeClass(const Class& cls) {
diff --git a/runtime/vm/class_finalizer.h b/runtime/vm/class_finalizer.h
index c575c9e..7253df7 100644
--- a/runtime/vm/class_finalizer.h
+++ b/runtime/vm/class_finalizer.h
@@ -54,11 +54,6 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
   // Mark [cls], its superclass and superinterfaces as can_be_future().
   static void MarkClassCanBeFuture(Zone* zone, const Class& cls);
-
-  // Mark [cls] and all its superclasses and superinterfaces as
-  // has_dynamically_extendable_subtypes().
-  static void MarkClassHasDynamicallyExtendableSubtypes(Zone* zone,
-                                                        const Class& cls);
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
   // Ensures members of the class are loaded, class layout is finalized and size
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 34b16ac..1c0c7b7 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1383,8 +1383,9 @@
   if (HasPragma::decode(pragma_bits)) {
     out_class->set_has_pragma(true);
   }
-  if (DynModuleExtendablePragma::decode(pragma_bits)) {
-    out_class->set_is_dynamically_extendable(true);
+  if (DynModuleExtendablePragma::decode(pragma_bits) ||
+      DynModuleImplicitlyExtendablePragma::decode(pragma_bits)) {
+    out_class->set_has_dynamically_extendable_subtypes(true);
     IG->set_has_dynamically_extendable_classes(true);
   }
   class_helper.SetJustRead(ClassHelper::kAnnotations);
@@ -1622,8 +1623,6 @@
     signature.set_result_type(T.ReceiverType(klass));
     function.set_has_pragma(HasPragma::decode(pragma_bits));
     function.set_is_visible(!InvisibleFunctionPragma::decode(pragma_bits));
-    function.SetIsDynamicallyOverridden(
-        DynModuleCanBeOverriddenPragma::decode(pragma_bits));
 
     FunctionNodeHelper function_node_helper(&helper_);
     function_node_helper.ReadUntilExcluding(
@@ -1806,11 +1805,21 @@
                                              "dyn-module:extendable")) {
           *pragma_bits = DynModuleExtendablePragma::update(true, *pragma_bits);
         }
+        if (constant_reader.IsStringConstant(
+                name_index, "dyn-module:implicitly-extendable")) {
+          *pragma_bits =
+              DynModuleImplicitlyExtendablePragma::update(true, *pragma_bits);
+        }
         if (constant_reader.IsStringConstant(name_index,
                                              "dyn-module:can-be-overridden")) {
           *pragma_bits =
               DynModuleCanBeOverriddenPragma::update(true, *pragma_bits);
         }
+        if (constant_reader.IsStringConstant(
+                name_index, "dyn-module:can-be-overridden-implicitly")) {
+          *pragma_bits = DynModuleCanBeOverriddenImplicitlyPragma::update(
+              true, *pragma_bits);
+        }
       }
     } else {
       helper_.SkipExpression();
@@ -1877,7 +1886,8 @@
                             is_synthetic);
   function.set_is_visible(!InvisibleFunctionPragma::decode(pragma_bits));
   function.SetIsDynamicallyOverridden(
-      DynModuleCanBeOverriddenPragma::decode(pragma_bits));
+      DynModuleCanBeOverriddenPragma::decode(pragma_bits) ||
+      DynModuleCanBeOverriddenImplicitlyPragma::decode(pragma_bits));
   if (register_function) {
     functions_.Add(&function);
   } else {
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index 537a3f4..08baaec 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -238,8 +238,12 @@
   using SharedPragma = BitField<uint32_t, bool, FfiNativePragma::kNextBit>;
   using DynModuleExtendablePragma =
       BitField<uint32_t, bool, SharedPragma::kNextBit>;
-  using DynModuleCanBeOverriddenPragma =
+  using DynModuleImplicitlyExtendablePragma =
       BitField<uint32_t, bool, DynModuleExtendablePragma::kNextBit>;
+  using DynModuleCanBeOverriddenPragma =
+      BitField<uint32_t, bool, DynModuleImplicitlyExtendablePragma::kNextBit>;
+  using DynModuleCanBeOverriddenImplicitlyPragma =
+      BitField<uint32_t, bool, DynModuleCanBeOverriddenPragma::kNextBit>;
 
   void FinishTopLevelClassLoading(const Class& toplevel_class,
                                   const Library& library,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index fd57986..9d47c10 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3263,11 +3263,6 @@
   set_state_bits(CanBeFutureBit::update(value, state_bits()));
 }
 
-void Class::set_is_dynamically_extendable(bool value) const {
-  ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
-  set_state_bits(IsDynamicallyExtendableBit::update(value, state_bits()));
-}
-
 void Class::set_has_dynamically_extendable_subtypes(bool value) const {
   ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
   set_state_bits(
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index fa2bf63..e0c8387 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2116,13 +2116,9 @@
   // It means that variable of static type based on this class may hold
   // a Future instance.
   using CanBeFutureBit = BitField<uint32_t, bool, IsFutureSubtypeBit::kNextBit>;
-  // This class can be extended, implemented or mixed-in by
-  // a dynamically loaded class.
-  using IsDynamicallyExtendableBit =
-      BitField<uint32_t, bool, CanBeFutureBit::kNextBit>;
   // This class has a dynamically extendable subtype.
   using HasDynamicallyExtendableSubtypesBit =
-      BitField<uint32_t, bool, IsDynamicallyExtendableBit::kNextBit>;
+      BitField<uint32_t, bool, CanBeFutureBit::kNextBit>;
   // This class was loaded from bytecode at runtime.
   using IsDeclaredInBytecodeBit =
       BitField<uint32_t, bool, HasDynamicallyExtendableSubtypesBit::kNextBit>;
@@ -2195,11 +2191,6 @@
   void set_can_be_future(bool value) const;
   bool can_be_future() const { return CanBeFutureBit::decode(state_bits()); }
 
-  void set_is_dynamically_extendable(bool value) const;
-  bool is_dynamically_extendable() const {
-    return IsDynamicallyExtendableBit::decode(state_bits());
-  }
-
   void set_has_dynamically_extendable_subtypes(bool value) const;
   bool has_dynamically_extendable_subtypes() const {
     return HasDynamicallyExtendableSubtypesBit::decode(state_bits());