blob: 5fb09fd393156e28a6c84033fd5cdecfb04d3b05 [file] [log] [blame]
// 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.
// @dart=2.9
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';
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.
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,
{bool isSetter, 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 != null) {
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);
}
}