a6cc772b67
Change-Id: I206a7a8132b5ea79dc019acf5c2e0cd800307bcf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212161 Reviewed-by: Jens Johansen <jensj@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
309 lines
12 KiB
Dart
309 lines
12 KiB
Dart
// Copyright (c) 2021, 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 'dart:io' show Directory, Platform;
|
|
import 'package:_fe_analyzer_shared/src/testing/features.dart';
|
|
import 'package:_fe_analyzer_shared/src/testing/id.dart';
|
|
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
|
|
import 'package:front_end/src/fasta/kernel/kernel_api.dart';
|
|
import 'package:front_end/src/testing/id_testing_helper.dart';
|
|
import 'package:front_end/src/testing/id_testing_utils.dart';
|
|
import 'package:front_end/src/fasta/kernel/class_hierarchy_builder.dart';
|
|
import 'package:front_end/src/testing/id_extractor.dart';
|
|
import 'package:kernel/ast.dart';
|
|
|
|
void main(List<String> args) async {
|
|
Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
|
|
await runTests<Features>(dataDir,
|
|
args: args,
|
|
createUriForFileName: createUriForFileName,
|
|
onFailure: onFailure,
|
|
runTest: runTestFor(
|
|
const ClassHierarchyDataComputer(), [cfeNonNullableConfig]));
|
|
}
|
|
|
|
class ClassHierarchyDataComputer extends DataComputer<Features> {
|
|
const ClassHierarchyDataComputer();
|
|
|
|
/// Function that computes a data mapping for [library].
|
|
///
|
|
/// Fills [actualMap] with the data.
|
|
@override
|
|
void computeLibraryData(
|
|
TestConfig config,
|
|
InternalCompilerResult compilerResult,
|
|
Library library,
|
|
Map<Id, ActualData<Features>> actualMap,
|
|
{bool? verbose}) {
|
|
new InheritanceDataExtractor(compilerResult, actualMap)
|
|
.computeForLibrary(library);
|
|
}
|
|
|
|
@override
|
|
void computeClassData(
|
|
TestConfig config,
|
|
InternalCompilerResult compilerResult,
|
|
Class cls,
|
|
Map<Id, ActualData<Features>> actualMap,
|
|
{bool? verbose}) {
|
|
new InheritanceDataExtractor(compilerResult, actualMap)
|
|
.computeForClass(cls);
|
|
}
|
|
|
|
@override
|
|
bool get supportsErrors => true;
|
|
|
|
@override
|
|
Features? computeErrorData(TestConfig config, InternalCompilerResult compiler,
|
|
Id id, List<FormattedMessage> errors) {
|
|
return null; //errorsToText(errors, useCodes: true);
|
|
}
|
|
|
|
@override
|
|
DataInterpreter<Features> get dataValidator =>
|
|
const FeaturesDataInterpreter();
|
|
}
|
|
|
|
class Tag {
|
|
static const String superclasses = 'superclasses';
|
|
static const String interfaces = 'interfaces';
|
|
static const String hasNoSuchMethod = 'hasNoSuchMethod';
|
|
static const String abstractMembers = 'abstractMembers';
|
|
static const String classBuilder = 'classBuilder';
|
|
static const String isSourceDeclaration = 'isSourceDeclaration';
|
|
static const String isSynthesized = 'isSynthesized';
|
|
static const String member = 'member';
|
|
static const String maxInheritancePath = 'maxInheritancePath';
|
|
static const String declaredOverrides = 'declared-overrides';
|
|
static const String mixinApplicationOverrides = 'mixin-overrides';
|
|
static const String inheritedImplements = 'inherited-implements';
|
|
static const String abstractForwardingStub = 'abstractForwardingStub';
|
|
static const String concreteForwardingStub = 'concreteForwardingStub';
|
|
static const String memberSignature = 'memberSignature';
|
|
static const String abstractMixinStub = 'abstractMixinStub';
|
|
static const String concreteMixinStub = 'concreteMixinStub';
|
|
static const String declarations = 'declarations';
|
|
static const String stubTarget = 'stubTarget';
|
|
static const String type = 'type';
|
|
static const String covariance = 'covariance';
|
|
}
|
|
|
|
class InheritanceDataExtractor extends CfeDataExtractor<Features> {
|
|
final InternalCompilerResult _compilerResult;
|
|
|
|
InheritanceDataExtractor(
|
|
this._compilerResult, Map<Id, ActualData<Features>> actualMap)
|
|
: super(_compilerResult, actualMap);
|
|
|
|
CoreTypes get _coreTypes => _compilerResult.coreTypes!;
|
|
|
|
ClassHierarchyBuilder get _classHierarchyBuilder =>
|
|
_compilerResult.kernelTargetForTesting!.loader.builderHierarchy;
|
|
|
|
@override
|
|
void computeForClass(Class node) {
|
|
super.computeForClass(node);
|
|
ClassHierarchyNode classHierarchyNode =
|
|
_classHierarchyBuilder.getNodeFromClass(node);
|
|
ClassHierarchyNodeDataForTesting data = classHierarchyNode.dataForTesting!;
|
|
void addMember(ClassMember classMember,
|
|
{required bool isSetter, required bool isClassMember}) {
|
|
Member member = classMember.getMember(_classHierarchyBuilder);
|
|
Member memberOrigin = member.memberSignatureOrigin ?? member;
|
|
if (memberOrigin.enclosingClass == _coreTypes.objectClass) {
|
|
return;
|
|
}
|
|
Features features = new Features();
|
|
|
|
String memberName = classMemberName(classMember);
|
|
memberName += isClassMember ? '#cls' : '#int';
|
|
MemberId id = new MemberId.internal(memberName, className: node.name);
|
|
|
|
TreeNode nodeWithOffset;
|
|
if (member.enclosingClass == node) {
|
|
nodeWithOffset = computeTreeNodeWithOffset(member)!;
|
|
} else {
|
|
nodeWithOffset = computeTreeNodeWithOffset(node)!;
|
|
}
|
|
if (classMember.isSourceDeclaration) {
|
|
features.add(Tag.isSourceDeclaration);
|
|
}
|
|
if (classMember.isSynthesized) {
|
|
features.add(Tag.isSynthesized);
|
|
if (member.enclosingClass != node) {
|
|
features[Tag.member] = memberQualifiedName(member);
|
|
}
|
|
if (classMember.hasDeclarations) {
|
|
for (ClassMember declaration in classMember.declarations) {
|
|
features.addElement(
|
|
Tag.declarations, classMemberQualifiedName(declaration));
|
|
}
|
|
}
|
|
}
|
|
features[Tag.classBuilder] = classMember.classBuilder.name;
|
|
|
|
Set<ClassMember>? declaredOverrides =
|
|
data.declaredOverrides[data.aliasMap[classMember] ?? classMember];
|
|
if (declaredOverrides != null) {
|
|
for (ClassMember override in declaredOverrides) {
|
|
features.addElement(
|
|
Tag.declaredOverrides, classMemberQualifiedName(override));
|
|
}
|
|
}
|
|
|
|
Set<ClassMember>? mixinApplicationOverrides = data
|
|
.mixinApplicationOverrides[data.aliasMap[classMember] ?? classMember];
|
|
if (mixinApplicationOverrides != null) {
|
|
for (ClassMember override in mixinApplicationOverrides) {
|
|
features.addElement(Tag.mixinApplicationOverrides,
|
|
classMemberQualifiedName(override));
|
|
}
|
|
}
|
|
|
|
Set<ClassMember>? inheritedImplements =
|
|
data.inheritedImplements[data.aliasMap[classMember] ?? classMember];
|
|
if (inheritedImplements != null) {
|
|
for (ClassMember implement in inheritedImplements) {
|
|
features.addElement(
|
|
Tag.inheritedImplements, classMemberQualifiedName(implement));
|
|
}
|
|
}
|
|
|
|
if (member.enclosingClass == node && member is Procedure) {
|
|
switch (member.stubKind) {
|
|
case ProcedureStubKind.Regular:
|
|
// TODO: Handle this case.
|
|
break;
|
|
case ProcedureStubKind.AbstractForwardingStub:
|
|
features.add(Tag.abstractForwardingStub);
|
|
features[Tag.type] = procedureType(member);
|
|
features[Tag.covariance] =
|
|
classMember.getCovariance(_classHierarchyBuilder).toString();
|
|
break;
|
|
case ProcedureStubKind.ConcreteForwardingStub:
|
|
features.add(Tag.concreteForwardingStub);
|
|
features[Tag.type] = procedureType(member);
|
|
features[Tag.covariance] =
|
|
classMember.getCovariance(_classHierarchyBuilder).toString();
|
|
features[Tag.stubTarget] = memberQualifiedName(member.stubTarget!);
|
|
break;
|
|
case ProcedureStubKind.NoSuchMethodForwarder:
|
|
// TODO: Handle this case.
|
|
break;
|
|
case ProcedureStubKind.MemberSignature:
|
|
features.add(Tag.memberSignature);
|
|
features[Tag.type] = procedureType(member);
|
|
features[Tag.covariance] =
|
|
classMember.getCovariance(_classHierarchyBuilder).toString();
|
|
break;
|
|
case ProcedureStubKind.AbstractMixinStub:
|
|
features.add(Tag.abstractMixinStub);
|
|
break;
|
|
case ProcedureStubKind.ConcreteMixinStub:
|
|
features.add(Tag.concreteMixinStub);
|
|
features[Tag.stubTarget] = memberQualifiedName(member.stubTarget!);
|
|
break;
|
|
}
|
|
}
|
|
|
|
registerValue(nodeWithOffset.location!.file, nodeWithOffset.fileOffset,
|
|
id, features, member);
|
|
}
|
|
|
|
classHierarchyNode.classMemberMap
|
|
.forEach((Name name, ClassMember classMember) {
|
|
addMember(classMember, isSetter: false, isClassMember: true);
|
|
});
|
|
classHierarchyNode.classSetterMap
|
|
.forEach((Name name, ClassMember classMember) {
|
|
addMember(classMember, isSetter: true, isClassMember: true);
|
|
});
|
|
classHierarchyNode.interfaceMemberMap
|
|
?.forEach((Name name, ClassMember classMember) {
|
|
if (!identical(classMember, classHierarchyNode.classMemberMap[name])) {
|
|
addMember(classMember, isSetter: false, isClassMember: false);
|
|
}
|
|
});
|
|
classHierarchyNode.interfaceSetterMap
|
|
?.forEach((Name name, ClassMember classMember) {
|
|
if (!identical(classMember, classHierarchyNode.classSetterMap[name])) {
|
|
addMember(classMember, isSetter: true, isClassMember: false);
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
Features computeClassValue(Id id, Class node) {
|
|
Features features = new Features();
|
|
ClassHierarchyNode classHierarchyNode =
|
|
_classHierarchyBuilder.getNodeFromClass(node);
|
|
ClassHierarchyNodeDataForTesting data = classHierarchyNode.dataForTesting!;
|
|
classHierarchyNode.superclasses.forEach((Supertype supertype) {
|
|
features.addElement(Tag.superclasses, supertypeToText(supertype));
|
|
});
|
|
classHierarchyNode.interfaces.forEach((Supertype supertype) {
|
|
features.addElement(Tag.interfaces, supertypeToText(supertype));
|
|
});
|
|
if (data.abstractMembers.isNotEmpty) {
|
|
for (ClassMember abstractMember in data.abstractMembers) {
|
|
features.addElement(
|
|
Tag.abstractMembers, classMemberQualifiedName(abstractMember));
|
|
}
|
|
}
|
|
features[Tag.maxInheritancePath] =
|
|
'${classHierarchyNode.maxInheritancePath}';
|
|
if (classHierarchyNode.hasNoSuchMethod) {
|
|
features.add(Tag.hasNoSuchMethod);
|
|
}
|
|
return features;
|
|
}
|
|
}
|
|
|
|
String classMemberName(ClassMember classMember) {
|
|
String name = classMember.name.text;
|
|
if (classMember.forSetter) {
|
|
name += '=';
|
|
}
|
|
return name;
|
|
}
|
|
|
|
String classMemberQualifiedName(ClassMember classMember) {
|
|
return '${classMember.classBuilder.name}.${classMemberName(classMember)}';
|
|
}
|
|
|
|
String memberName(Member member) {
|
|
String name = member.name.text;
|
|
if (member is Procedure && member.isSetter) {
|
|
name += '=';
|
|
}
|
|
return name;
|
|
}
|
|
|
|
String memberQualifiedName(Member member) {
|
|
return '${member.enclosingClass!.name}.${memberName(member)}';
|
|
}
|
|
|
|
String procedureType(Procedure procedure) {
|
|
if (procedure.kind == ProcedureKind.Getter) {
|
|
return typeToText(procedure.function.returnType,
|
|
TypeRepresentation.analyzerNonNullableByDefault);
|
|
} else if (procedure.kind == ProcedureKind.Setter) {
|
|
return typeToText(procedure.function.positionalParameters.single.type,
|
|
TypeRepresentation.analyzerNonNullableByDefault);
|
|
} else {
|
|
Nullability functionTypeNullability;
|
|
if (procedure.enclosingLibrary.isNonNullableByDefault) {
|
|
functionTypeNullability = procedure.enclosingLibrary.nonNullable;
|
|
} else {
|
|
// We don't create a member signature when the member is just
|
|
// a substitution. We should still take the nullability to be
|
|
// legacy, though.
|
|
functionTypeNullability = procedure.enclosingLibrary.nonNullable;
|
|
}
|
|
return typeToText(
|
|
procedure.function.computeThisFunctionType(functionTypeNullability),
|
|
TypeRepresentation.analyzerNonNullableByDefault);
|
|
}
|
|
}
|