diff --git a/pkg/vm/lib/transformations/dynamic_interface_annotator.dart b/pkg/vm/lib/transformations/dynamic_interface_annotator.dart index 3da52cab7969..66734dc289a0 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 @@ import 'pragma.dart' kDynModuleCallablePragmaName, kDynModuleExtendablePragmaName, kDynModuleImplicitlyCallablePragmaName, - kDynModuleImplicitlyExtendablePragmaName; + kDynModuleImplicitlyExtendablePragmaName, + kDynModuleCanBeOverriddenImplicitlyPragmaName; const bool _debug = false; @@ -33,13 +35,28 @@ void annotateComponent(String dynamicInterfaceSpecification, Uri baseUri, 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,16 +188,73 @@ class _Annotator extends RecursiveVisitor { } } -void annotateImplicitlyExtendable( - CoreTypes coreTypes, Set extendableClasses) { - final pragma = - pragmaConstant(coreTypes, kDynModuleImplicitlyExtendablePragmaName); - for (final cls in [...extendableClasses]) { +class _ImplicitExtendableAnnotator { + final Constant pragma; + final Set extendableClasses; + final Set implicitlyExtendable = Set.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 overriddenMembers; + final Set implicitlyOverriddenSetters = Set.identity(); + final Set implicitlyOverriddenNonSetters = Set.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); } } } diff --git a/pkg/vm/lib/transformations/pragma.dart b/pkg/vm/lib/transformations/pragma.dart index 152b8ad27fae..f98aaf21c68f 100644 --- a/pkg/vm/lib/transformations/pragma.dart +++ b/pkg/vm/lib/transformations/pragma.dart @@ -26,6 +26,8 @@ const kDynModuleExtendablePragmaName = "dyn-module:extendable"; 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 c4d2f149ee17..fb9265bf6c88 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 @@ extendable: 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 c23fe993fe90..9d0891f42ab7 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 N {} 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 7d07468ee912..c76db5ad7241 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 @@ class O extends self::N { } @#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 @@ base class V extends core::Object { static field core::Object? sfield9; static field core::Object? _sfield10; @#C5 -static const field core::List const11 = #C16; +static const field core::List const11 = #C18; @#C5 static method smethod10() → void {} static method _smethod11() → void {} @@ -215,9 +232,11 @@ constants { #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 = [#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 = [#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 25190b6df781..76ad80cfaaa8 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 @@ void testIsA1(obj) { 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 5a13e50639fe..b63685c50fce 100644 --- a/runtime/vm/class_finalizer.cc +++ b/runtime/vm/class_finalizer.cc @@ -589,10 +589,6 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) { 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::MarkClassCanBeFuture(Zone* zone, const Class& cls) { } } -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 c575c9ecf008..7253df7b4a87 100644 --- a/runtime/vm/class_finalizer.h +++ b/runtime/vm/class_finalizer.h @@ -54,11 +54,6 @@ class ClassFinalizer : public AllStatic { #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 34b16ac851f4..1c0c7b7b0bd4 100644 --- a/runtime/vm/kernel_loader.cc +++ b/runtime/vm/kernel_loader.cc @@ -1383,8 +1383,9 @@ void KernelLoader::LoadClass(const Library& library, 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 @@ void KernelLoader::FinishClassLoading(const Class& klass, 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 @@ void KernelLoader::ReadVMAnnotations(intptr_t annotation_count, "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 @@ void KernelLoader::LoadProcedure(const Library& library, 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 537a3f45aec0..08baaecaebd9 100644 --- a/runtime/vm/kernel_loader.h +++ b/runtime/vm/kernel_loader.h @@ -238,8 +238,12 @@ class KernelLoader : public ValueObject { using SharedPragma = BitField; using DynModuleExtendablePragma = BitField; - using DynModuleCanBeOverriddenPragma = + using DynModuleImplicitlyExtendablePragma = BitField; + using DynModuleCanBeOverriddenPragma = + BitField; + using DynModuleCanBeOverriddenImplicitlyPragma = + BitField; void FinishTopLevelClassLoading(const Class& toplevel_class, const Library& library, diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index fd57986d5bf7..9d47c10e8762 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -3263,11 +3263,6 @@ void Class::set_can_be_future(bool value) const { 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 fa2bf6341852..e0c83870182b 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -2116,13 +2116,9 @@ class Class : public Object { // It means that variable of static type based on this class may hold // a Future instance. using CanBeFutureBit = BitField; - // This class can be extended, implemented or mixed-in by - // a dynamically loaded class. - using IsDynamicallyExtendableBit = - BitField; // This class has a dynamically extendable subtype. using HasDynamicallyExtendableSubtypesBit = - BitField; + BitField; // This class was loaded from bytecode at runtime. using IsDeclaredInBytecodeBit = BitField; @@ -2195,11 +2191,6 @@ class Class : public Object { 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());