d2252b1fd6
Front-end server and gen_kernel tools now take --dump-detailed-dynamic-interface=foo.json option which can be used along with --dynamic-interface when AOT-compiling application with exposed dynamic interface. This option dumps dynamic interface broken down to members and classes as JSON. TEST=pkg/vm/test/transformations/dynamic_interface_annotator_test.dart Fixes b/420811696 Change-Id: Id1b15521f47a4869fefd40e835044584fbfb5274 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/431901 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
728 lines
20 KiB
Dart
728 lines
20 KiB
Dart
// 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,
|
|
});
|
|
}
|
|
}
|
|
}
|