// 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 DynamicInterfaceSpecification; import 'package:kernel/ast.dart'; import 'package:kernel/core_types.dart' show CoreTypes; import 'pragma.dart' show kDynModuleExtendablePragmaName, kDynModuleCanBeOverriddenPragmaName, kDynModuleCallablePragmaName, kDynModuleImplicitlyCallablePragmaName; void annotateComponent(String dynamicInterfaceSpecification, Uri baseUri, Component component, CoreTypes coreTypes) { final spec = DynamicInterfaceSpecification( dynamicInterfaceSpecification, baseUri, component); final extendableAnnotator = annotateNodes( spec.extendable, kDynModuleExtendablePragmaName, baseUri, coreTypes, annotateClasses: true, annotateStaticMembers: false, annotateInstanceMembers: false); annotateNodes(spec.canBeOverridden, kDynModuleCanBeOverriddenPragmaName, baseUri, coreTypes, annotateClasses: false, annotateStaticMembers: false, annotateInstanceMembers: true); final callableAnnotator = annotateNodes( spec.callable, kDynModuleCallablePragmaName, baseUri, coreTypes, annotateClasses: true, annotateStaticMembers: true, annotateInstanceMembers: true); 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 nodes, String pragmaName, Uri baseUri, CoreTypes coreTypes, { required bool annotateClasses, required bool annotateStaticMembers, required bool annotateInstanceMembers, }) { final pragma = pragmaConstant(coreTypes, pragmaName); final annotator = _Annotator(pragma, annotateClasses: annotateClasses, annotateStaticMembers: annotateStaticMembers, annotateInstanceMembers: annotateInstanceMembers); for (final node in nodes) { node.accept(annotator); } return annotator; } class _Annotator extends RecursiveVisitor { final Constant pragma; final bool annotateClasses; final bool annotateStaticMembers; final bool annotateInstanceMembers; final Set annotatedClasses = Set.identity(); final Set annotatedMembers = Set.identity(); _Annotator( this.pragma, { required this.annotateClasses, required this.annotateStaticMembers, required this.annotateInstanceMembers, }); 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 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 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 && annotatedClasses.add(node)) { print("Annotated $node with $pragma"); node.addAnnotation(ConstantExpression(pragma)); } } void annotateMember(Member node) { if ((node.isInstanceMember ? annotateInstanceMembers : annotateStaticMembers) && annotatedMembers.add(node)) { print("Annotated $node with $pragma"); node.addAnnotation(ConstantExpression(pragma)); } } } class _ImplicitUsesAnnotator extends RecursiveVisitor { final Constant pragma; final Set annotatedClasses = Set.identity(); final Set annotatedMembers = Set.identity(); final Set _annotatedConstantClasses = Set.identity(); final Set _visitedConstants = Set.identity(); final Map _classInfos = Map.identity(); _ImplicitUsesAnnotator(this.pragma, Set explicitlyUsedClasses, Set explicitlyUsedMembers) { annotatedClasses.addAll(explicitlyUsedClasses); annotatedMembers.addAll(explicitlyUsedMembers); } void annotateMixinUses(Set extendableClasses) { for (final cls in extendableClasses) { if (cls.isMixinClass || cls.isMixinDeclaration) { cls.visitChildren(this); } } } void annotateMemberUses(Set 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); } } @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)) { print("Annotated $node with $pragma"); node.addAnnotation(ConstantExpression(pragma)); } } void annotateMember(Member node) { if (annotatedMembers.add(node)) { print("Annotated $node with $pragma"); 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); } } 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(); final nonSetterSelectors = Set(); bool collected = false; // Cache of dispatch targets. late final Map dispatchTargetsSetters = _initDispatchTargets(true); late final Map 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 _initDispatchTargets(bool setters) { Map 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; }