blob: 96939a7dc0f177a1a5f6617f2d9a0916f4f79d08 [file] [log] [blame]
// Copyright (c) 2024, 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:front_end/src/api_prototype/dynamic_module_validator.dart'
show DynamicInterfaceLanguageImplPragmas, 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'
show
kDynModuleCanBeOverriddenPragmaName,
kDynModuleCallablePragmaName,
kDynModuleExtendablePragmaName,
kDynModuleImplicitlyCallablePragmaName,
kDynModuleImplicitlyExtendablePragmaName,
kDynModuleCanBeOverriddenImplicitlyPragmaName;
void annotateComponent(
String dynamicInterfaceSpecification,
Uri baseUri,
Component component,
CoreTypes coreTypes, {
Map<String, List<Map<String, String>>>? detailedDynamicInterfaceJson,
}) {
final spec = DynamicInterfaceSpecification(
dynamicInterfaceSpecification,
baseUri,
component,
);
discoverLanguageImplPragmasInCoreLibraries(spec, component, coreTypes);
final _DetailedDynamicInterfaceLogger? logger =
detailedDynamicInterfaceJson != null
? _DetailedDynamicInterfaceLogger(detailedDynamicInterfaceJson)
: null;
logger?.setActiveSection('extendable');
final extendableAnnotator = annotateNodes(
spec.extendable,
kDynModuleExtendablePragmaName,
baseUri,
coreTypes,
annotateClasses: true,
annotateFinalClasses: false,
annotateStaticMembers: false,
annotateInstanceMembers: false,
logger: logger,
);
_ImplicitExtendableAnnotator(
pragmaConstant(coreTypes, kDynModuleImplicitlyExtendablePragmaName),
extendableAnnotator.annotatedClasses,
).annotate();
logger?.setActiveSection('can-be-overridden');
final canBeOverriddenAnnotator = annotateNodes(
spec.canBeOverridden,
kDynModuleCanBeOverriddenPragmaName,
baseUri,
coreTypes,
annotateClasses: false,
annotateFinalClasses: true,
annotateStaticMembers: false,
annotateInstanceMembers: true,
logger: logger,
);
final hierarchy = ClassHierarchy(component, coreTypes);
copyCanBeOverriddenToMixinApplications(
canBeOverriddenAnnotator.annotatedMembers,
canBeOverriddenAnnotator.pragma,
component,
hierarchy,
);
_ImplicitOverridesAnnotator(
pragmaConstant(coreTypes, kDynModuleCanBeOverriddenImplicitlyPragmaName),
hierarchy,
canBeOverriddenAnnotator.annotatedMembers,
).annotate();
logger?.setActiveSection('callable');
final callableAnnotator = annotateNodes(
spec.callable,
kDynModuleCallablePragmaName,
baseUri,
coreTypes,
annotateClasses: true,
annotateFinalClasses: true,
annotateStaticMembers: true,
annotateInstanceMembers: true,
logger: logger,
);
final implicitUsesAnnotator = _ImplicitUsesAnnotator(
pragmaConstant(coreTypes, kDynModuleImplicitlyCallablePragmaName),
callableAnnotator.annotatedClasses,
callableAnnotator.annotatedMembers,
);
implicitUsesAnnotator.annotateMixinUses(extendableAnnotator.annotatedClasses);
implicitUsesAnnotator.annotateMemberUses(callableAnnotator.annotatedMembers);
implicitUsesAnnotator.annotateDispatchTargets(component);
}
InstanceConstant pragmaConstant(CoreTypes coreTypes, String pragmaName) {
return InstanceConstant(coreTypes.pragmaClass.reference, [], {
coreTypes.pragmaName.fieldReference: StringConstant(pragmaName),
coreTypes.pragmaOptions.fieldReference: NullConstant(),
});
}
_Annotator annotateNodes(
Set<TreeNode> nodes,
String pragmaName,
Uri baseUri,
CoreTypes coreTypes, {
required bool annotateClasses,
required bool annotateFinalClasses,
required bool annotateStaticMembers,
required bool annotateInstanceMembers,
_DetailedDynamicInterfaceLogger? logger,
}) {
final pragma = pragmaConstant(coreTypes, pragmaName);
final annotator = _Annotator(
pragma,
annotateClasses: annotateClasses,
annotateFinalClasses: annotateFinalClasses,
annotateStaticMembers: annotateStaticMembers,
annotateInstanceMembers: annotateInstanceMembers,
logger: logger,
);
for (final node in nodes) {
node.accept(annotator);
}
return annotator;
}
class _Annotator extends RecursiveVisitor {
final Constant pragma;
final bool annotateClasses;
final bool annotateFinalClasses;
final bool annotateStaticMembers;
final bool annotateInstanceMembers;
final _DetailedDynamicInterfaceLogger? logger;
final Set<Class> annotatedClasses = Set<Class>.identity();
final Set<Member> annotatedMembers = Set<Member>.identity();
_Annotator(
this.pragma, {
required this.annotateClasses,
required this.annotateFinalClasses,
required this.annotateStaticMembers,
required this.annotateInstanceMembers,
this.logger,
});
bool get annotateMembers => annotateStaticMembers || annotateInstanceMembers;
@override
void visitLibrary(Library node) {
for (final c in node.classes) {
if (c.name[0] != '_') {
c.accept(this);
}
}
for (final ext in node.extensions) {
if (ext.name[0] != '_') {
ext.accept(this);
}
}
for (final extensionType in node.extensionTypeDeclarations) {
if (extensionType.name[0] != '_') {
extensionType.accept(this);
}
}
for (final exportRef in node.additionalExports) {
exportRef.node!.accept(this);
}
if (annotateMembers) {
_visitPublicMembers(node.procedures);
_visitPublicMembers(node.fields);
}
}
@override
void visitClass(Class node) {
annotateClass(node);
if (annotateMembers) {
_visitPublicMembers(node.constructors);
_visitPublicMembers(node.procedures);
_visitPublicMembers(node.fields);
}
}
void _visitPublicMembers(List<Member> members) {
for (final m in members) {
if (!m.name.isPrivate) {
m.accept(this);
}
}
}
@override
void defaultMember(Member node) {
annotateMember(node);
}
@override
void visitClassReference(Class node) {
annotateClass(node);
}
void annotateClass(Class node) {
if (annotateClasses &&
(annotateFinalClasses || !node.isFinal) &&
annotatedClasses.add(node)) {
logger?.logNode(node);
node.addAnnotation(ConstantExpression(pragma));
}
}
void annotateMember(Member node) {
if ((node.isInstanceMember
? annotateInstanceMembers
: annotateStaticMembers) &&
annotatedMembers.add(node)) {
logger?.logNode(node);
node.addAnnotation(ConstantExpression(pragma));
}
}
@override
void visitExtension(Extension node) {
for (final md in node.memberDescriptors) {
final member = md.memberReference?.node;
if (member != null) {
annotateMember(member as Member);
}
final tearOff = md.tearOffReference?.node;
if (tearOff != null) {
annotateMember(tearOff as Member);
}
}
}
@override
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
for (final md in node.memberDescriptors) {
final member = md.memberReference?.node;
if (member != null) {
annotateMember(member as Member);
}
final tearOff = md.tearOffReference?.node;
if (tearOff != null) {
annotateMember(tearOff as Member);
}
}
}
}
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 (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;
final Set<Class> annotatedClasses = Set<Class>.identity();
final Set<Member> annotatedMembers = Set<Member>.identity();
final Set<Class> _annotatedConstantClasses = Set<Class>.identity();
final Set<Constant> _visitedConstants = Set<Constant>.identity();
final Map<Class, _ClassInfo> _classInfos = Map<Class, _ClassInfo>.identity();
_ImplicitUsesAnnotator(
this.pragma,
Set<Class> explicitlyUsedClasses,
Set<Member> explicitlyUsedMembers,
) {
annotatedClasses.addAll(explicitlyUsedClasses);
annotatedMembers.addAll(explicitlyUsedMembers);
}
void annotateMixinUses(Set<Class> extendableClasses) {
for (final cls in extendableClasses) {
if (cls.isMixinClass || cls.isMixinDeclaration) {
cls.visitChildren(this);
}
}
}
void annotateMemberUses(Set<Member> explicitlyUsedMembers) {
for (final node in explicitlyUsedMembers) {
node.acceptReference(this);
}
}
@override
void visitClassReference(Class node) {
annotateClass(node);
}
@override
void visitConstructorReference(Constructor node) {
annotateMember(node);
if (node.isConst) {
annotateConstantClass(node.enclosingClass);
node.visitChildren(this);
}
}
@override
void visitProcedureReference(Procedure node) {
annotateMember(node);
if (node.isRedirectingFactory) {
final target = node.function.redirectingFactoryTarget?.target;
target?.acceptReference(this);
}
}
@override
void visitFieldReference(Field node) {
annotateMember(node);
if (node.isConst) {
(node.initializer as ConstantExpression).constant.acceptReference(this);
}
}
@override
void defaultConstantReference(Constant node) {
if (_visitedConstants.add(node)) {
node.visitChildren(this);
}
}
@override
void visitInstanceConstantReference(InstanceConstant node) {
super.visitInstanceConstantReference(node);
annotateConstantClass(node.classNode);
}
void annotateClass(Class node) {
if (annotatedClasses.add(node)) {
node.addAnnotation(ConstantExpression(pragma));
}
}
void annotateMember(Member node) {
if (annotatedMembers.add(node)) {
node.addAnnotation(ConstantExpression(pragma));
}
}
// Annotate class potentially used in an InstanceConstant.
void annotateConstantClass(Class node) {
if (_annotatedConstantClasses.add(node)) {
annotateClass(node);
for (final f in node.fields) {
if (f.isInstanceMember) {
annotateMember(f);
f.initializer?.accept(this);
}
}
final superclass = node.superclass;
if (superclass != null) {
annotateConstantClass(superclass);
}
}
}
void annotateDispatchTargets(Component component) {
for (final m in annotatedMembers) {
if (m.isInstanceMember) {
_getClassInfo(m.enclosingClass!).addSelector(m);
}
}
for (final lib in component.libraries) {
for (final classNode in lib.classes) {
final classInfo = _getClassInfo(classNode);
classInfo.collectSelectors();
for (final selector in classInfo.setterSelectors) {
final dispatchTarget = classInfo.dispatchTargetsSetters[selector];
if (dispatchTarget != null) {
annotateMember(dispatchTarget);
}
}
for (final selector in classInfo.nonSetterSelectors) {
final dispatchTarget = classInfo.dispatchTargetsNonSetters[selector];
if (dispatchTarget != null) {
annotateMember(dispatchTarget);
}
}
}
}
}
_ClassInfo _getClassInfo(Class cls) =>
_classInfos[cls] ??= _createClassInfo(cls);
_ClassInfo _createClassInfo(Class cls) {
final superclass = cls.superclass;
final superclassInfo =
superclass != null ? _getClassInfo(superclass) : null;
final mixedInClass = cls.mixedInClass;
final mixedInClassInfo =
mixedInClass != null ? _getClassInfo(mixedInClass) : null;
final implementedClassInfos =
cls.implementedTypes
.map((sup) => _getClassInfo(sup.classNode))
.toList();
return _ClassInfo(
cls,
superclassInfo,
mixedInClassInfo,
implementedClassInfos,
);
}
}
class _ClassInfo {
final Class classNode;
final _ClassInfo? superclass;
final _ClassInfo? mixedInClass;
final List<_ClassInfo> implementedClasses;
// Callable selectors.
final setterSelectors = Set<Name>();
final nonSetterSelectors = Set<Name>();
bool collected = false;
// Cache of dispatch targets.
late final Map<Name, Member> dispatchTargetsSetters = _initDispatchTargets(
true,
);
late final Map<Name, Member> dispatchTargetsNonSetters = _initDispatchTargets(
false,
);
_ClassInfo(
this.classNode,
this.superclass,
this.mixedInClass,
this.implementedClasses,
);
void addSelector(Member m) {
if (m.isInstanceMember) {
if (!_isSetter(m)) {
nonSetterSelectors.add(m.name);
}
if (m.hasSetter) {
setterSelectors.add(m.name);
}
}
}
void addAllSelectors(_ClassInfo other) {
setterSelectors.addAll(other.setterSelectors);
nonSetterSelectors.addAll(other.nonSetterSelectors);
}
void collectSelectors() {
if (!collected) {
final superclass = this.superclass;
if (superclass != null) {
superclass.collectSelectors();
addAllSelectors(superclass);
}
final mixedInClass = this.mixedInClass;
if (mixedInClass != null) {
mixedInClass.collectSelectors();
addAllSelectors(mixedInClass);
}
for (final c in implementedClasses) {
c.collectSelectors();
addAllSelectors(c);
}
collected = true;
}
}
Map<Name, Member> _initDispatchTargets(bool setters) {
Map<Name, Member> targets;
final superclass = this.superclass;
if (superclass != null) {
targets = Map.of(
setters
? superclass.dispatchTargetsSetters
: superclass.dispatchTargetsNonSetters,
);
} else {
targets = {};
}
final mixedInClass = this.mixedInClass;
if (mixedInClass != null) {
targets.addAll(
setters
? mixedInClass.dispatchTargetsSetters
: mixedInClass.dispatchTargetsNonSetters,
);
}
for (Field f in classNode.fields) {
if (!f.isStatic && !f.isAbstract) {
if (!setters || f.hasSetter) {
targets[f.name] = f;
}
}
}
for (Procedure p in classNode.procedures) {
if (!p.isStatic && !p.isAbstract) {
if (p.isSetter == setters) {
targets[p.name] = p;
}
}
}
return targets;
}
static bool _isSetter(Member m) => m is Procedure && m.isSetter;
}
void discoverLanguageImplPragmasInCoreLibraries(
DynamicInterfaceSpecification spec,
Component component,
CoreTypes coreTypes,
) {
final languageImplPragmas = DynamicInterfaceLanguageImplPragmas(coreTypes);
final visitor = _DiscoverLanguageImplPragmasVisitor(
spec,
languageImplPragmas,
);
for (final lib in component.libraries) {
if (languageImplPragmas.isPlatformLibrary(lib)) {
visitor.visitLibrary(lib);
}
}
}
class _DiscoverLanguageImplPragmasVisitor extends RecursiveVisitor {
final DynamicInterfaceSpecification spec;
final DynamicInterfaceLanguageImplPragmas languageImplPragmas;
_DiscoverLanguageImplPragmasVisitor(this.spec, this.languageImplPragmas);
@override
void visitClass(Class node) {
if (languageImplPragmas.isExtendable(node)) {
spec.extendable.add(node);
}
if (languageImplPragmas.isCallable(node)) {
spec.callable.add(node);
}
node.visitChildren(this);
}
@override
void defaultMember(Member node) {
if (languageImplPragmas.isCallable(node)) {
spec.callable.add(node);
}
if (languageImplPragmas.canBeOverridden(node)) {
if (!node.isInstanceMember) {
throw 'Expected instance member $node';
}
spec.canBeOverridden.add(node);
}
}
}
// Mixin transformation copies all members of mixins into mixin applications.
// So we need to copy can-be-overridden pragmas from members of mixins to
// their copies in the transformed mixin applications.
void copyCanBeOverriddenToMixinApplications(
Set<Member> overriddenMembers,
Constant pragma,
Component component,
ClassHierarchy hierarchy,
) {
void processMember(Member original, Class mixinApplication) {
if (!original.isInstanceMember) {
return;
}
if (!overriddenMembers.contains(original)) {
return;
}
final member = ClassHierarchy.findMemberByName(
hierarchy.getDeclaredMembers(
mixinApplication,
setters: original is Procedure && original.isSetter,
),
original.name,
);
if (member == null) {
return;
}
if (overriddenMembers.add(member)) {
member.addAnnotation(ConstantExpression(pragma));
}
}
for (final library in component.libraries) {
for (final cls in library.classes) {
if (cls.isEliminatedMixin) {
final origin = cls.implementedTypes.last.classNode;
for (final proc in origin.procedures) {
processMember(proc, cls);
}
for (final field in origin.fields) {
processMember(field, cls);
}
}
}
}
}
class _DetailedDynamicInterfaceLogger {
final Map<String, List<Map<String, String>>> json;
late List<Map<String, String>> section;
_DetailedDynamicInterfaceLogger(this.json);
void setActiveSection(String name) {
section = (json[name] ??= <Map<String, String>>[]);
}
void logNode(TreeNode node) {
switch (node) {
case Class():
section.add({
'library': node.enclosingLibrary.importUri.toString(),
'class': node.name,
});
case Member() when node.enclosingClass != null:
section.add({
'library': node.enclosingLibrary.importUri.toString(),
'class': node.enclosingClass!.demangledName,
'member': node.name.text,
});
case Member() when node.enclosingClass == null:
section.add({
'library': node.enclosingLibrary.importUri.toString(),
'member': node.name.text,
});
}
}
}