Files
sdk/pkg/front_end/tool/bench_maker.dart
T
Chloe Stefantsova 00acecdfbe [model] Upate bench maker and parser for recent type system changes
This CL includes changes to BenchMaker and test type parser that
include recent developments in the DartType system, such as extension
types and record types, outstanding existing features, such as
typedefs, and recent changes in the CFE type model, such as structural
parameters.

Change-Id: I16ca13273e83de7942abad67b8a2b67146fb24bf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/430661
Reviewed-by: Johnni Winther <johnniwinther@google.com>
2025-05-23 01:57:10 -07:00

581 lines
17 KiB
Dart

// Copyright (c) 2019, 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:convert" show JsonEncoder;
import "dart:io" show File;
import "package:front_end/src/type_inference/type_schema.dart" show UnknownType;
import "package:kernel/ast.dart";
String jsonEncode(Object object) {
return const JsonEncoder.withIndent(" ").convert(object);
}
Iterable<String> nameGenerator() sync* {
int i = 0;
while (true) {
List<int> characters = <int>[];
int j = i;
while (j > 25) {
int c = j % 26;
j = (j ~/ 26) - 1;
characters.add(c + 65);
}
characters.add(j + 65);
yield new String.fromCharCodes(characters.reversed);
i++;
}
}
class BenchMaker implements DartTypeVisitor1<void, StringBuffer> {
final List<Object> checks = <Object>[];
final Map<TreeNode, String> nodeNames = <TreeNode, String>{};
final Map<StructuralParameter, String> structuralParameterNames =
<StructuralParameter, String>{};
final Set<String> usedNames = new Set<String>();
final Iterator<String> names = nameGenerator().iterator..moveNext();
final List<String> declarations = <String>[];
final List<TypeParameter> usedTypeParameters = <TypeParameter>[];
final List<StructuralParameter> usedStructuralParameters =
<StructuralParameter>[];
String serializeTypeChecks(List<Object> typeChecks) {
for (Object list in typeChecks) {
List<Object> typeCheck = list as List<Object>;
writeTypeCheck(typeCheck[0] as DartType, typeCheck[1] as DartType,
typeCheck[2] as bool);
}
writeDeclarations();
return jsonEncode(this);
}
void writeTypeCheck(DartType s, DartType t, bool expected) {
assert(usedTypeParameters.isEmpty);
assert(usedStructuralParameters.isEmpty);
usedTypeParameters.clear();
usedStructuralParameters.clear();
StringBuffer sb = new StringBuffer();
s.accept1(this, sb);
String sString = "$sb";
sb.clear();
t.accept1(this, sb);
String tString = "$sb";
List<Object> arguments = <Object>[sString, tString];
Set<TypeParameter> seenTypeParameters = new Set<TypeParameter>();
Set<StructuralParameter> seenStructuralParameters =
new Set<StructuralParameter>();
List<String> parameterStrings = <String>[];
while (usedTypeParameters.isNotEmpty) {
List<TypeParameter> typeParameters = usedTypeParameters.toList();
usedTypeParameters.clear();
for (TypeParameter parameter in typeParameters) {
if (seenTypeParameters.add(parameter)) {
sb.clear();
writeTypeParameter(parameter, sb);
parameterStrings.add("$sb");
}
}
}
while (usedStructuralParameters.isNotEmpty) {
List<StructuralParameter> structuralParameters =
usedStructuralParameters.toList();
usedStructuralParameters.clear();
for (StructuralParameter parameter in structuralParameters) {
if (seenStructuralParameters.add(parameter)) {
sb.clear();
writeStructuralParameter(parameter, sb);
parameterStrings.add("$sb");
}
}
}
if (parameterStrings.isNotEmpty) {
arguments.add(parameterStrings);
}
checks.add(<String, dynamic>{
"kind": expected ? "isSubtype" : "isNotSubtype",
"arguments": arguments,
});
}
void writeTypeParameter(TypeParameter parameter, StringBuffer sb) {
sb.write(computeName(parameter));
DartType bound = parameter.bound;
DartType defaultType = parameter.defaultType;
bool hasExplicitBound = true;
if (bound is InterfaceType && defaultType is DynamicType) {
if (bound.classNode.supertype == null) {
hasExplicitBound = false;
}
}
if (hasExplicitBound) {
sb.write(" extends ");
bound.accept1(this, sb);
}
}
void writeStructuralParameter(
StructuralParameter parameter, StringBuffer sb) {
sb.write(computeStructuralParameterName(parameter));
DartType bound = parameter.bound;
DartType defaultType = parameter.defaultType;
bool hasExplicitBound = true;
if (bound is InterfaceType && defaultType is DynamicType) {
if (bound.classNode.supertype == null) {
hasExplicitBound = false;
}
}
if (hasExplicitBound) {
sb.write(" extends ");
bound.accept1(this, sb);
}
}
void writeTypeParameters(
List<TypeParameter> typeParameters, StringBuffer sb) {
if (typeParameters.isNotEmpty) {
sb.write("<");
bool first = true;
for (TypeParameter p in typeParameters) {
if (!first) sb.write(", ");
writeTypeParameter(p, sb);
first = false;
}
sb.write(">");
}
}
void writeStructuralParameters(
List<StructuralParameter> typeParameters, StringBuffer sb) {
if (typeParameters.isNotEmpty) {
sb.write("<");
bool first = true;
for (StructuralParameter p in typeParameters) {
if (!first) sb.write(", ");
writeStructuralParameter(p, sb);
first = false;
}
sb.write(">");
}
}
void writeDeclarations() {
Set<TreeNode> writtenDeclarations = new Set<TreeNode>();
int index = 0;
List<TreeNode> nodes = nodeNames.keys.toList();
while (index < nodes.length) {
for (; index < nodes.length; index++) {
TreeNode node = nodes[index];
writeDeclaration(node, writtenDeclarations);
}
nodes = nodeNames.keys.toList();
}
}
void writeDeclaration(
TreeNode? declaration, Set<TreeNode> writtenDeclarations) {
if (declaration is Class) {
writeClass(declaration, writtenDeclarations);
} else if (declaration is Typedef) {
writeTypedef(declaration, writtenDeclarations);
} else if (declaration is ExtensionTypeDeclaration) {
writeExtensionTypeDeclaration(declaration, writtenDeclarations);
}
}
void writeDeclarationForType(
DartType type, Set<TreeNode> writtenDeclarations) {
if (type is InterfaceType) {
writeClass(type.classNode, writtenDeclarations);
} else if (type is TypedefType) {
writeTypedef(type.typedefNode, writtenDeclarations);
} else if (type is ExtensionType) {
writeExtensionTypeDeclaration(
type.extensionTypeDeclaration, writtenDeclarations);
}
}
void writeClass(Class? cls, Set<TreeNode> writtenDeclarations) {
if (cls == null || !writtenDeclarations.add(cls)) {
return;
}
Supertype? supertype = cls.supertype;
writeClass(supertype?.classNode, writtenDeclarations);
Supertype? mixedInType = cls.mixedInType;
writeClass(mixedInType?.classNode, writtenDeclarations);
for (Supertype implementedType in cls.implementedTypes) {
writeClass(implementedType.classNode, writtenDeclarations);
}
StringBuffer sb = new StringBuffer();
sb.write("class ");
sb.write(computeName(cls));
writeTypeParameters(cls.typeParameters, sb);
if (supertype != null) {
sb.write(" extends ");
supertype.asInterfaceType.accept1(this, sb);
}
if (mixedInType != null) {
sb.write(" with ");
mixedInType.asInterfaceType.accept1(this, sb);
}
bool first = true;
for (Supertype implementedType in cls.implementedTypes) {
if (first) {
sb.write(" implements ");
} else {
sb.write(", ");
}
implementedType.asInterfaceType.accept1(this, sb);
first = false;
}
Procedure? callOperator;
for (Procedure procedure in cls.procedures) {
if (procedure.name.text == "call") {
callOperator = procedure;
}
}
if (callOperator != null) {
sb.write("{ ");
callOperator.function
.computeFunctionType(cls.enclosingLibrary.nonNullable)
.accept1(this, sb);
sb.write(" }");
} else {
sb.write(";");
}
declarations.add("$sb");
}
void writeTypedef(Typedef? typedefNode, Set<TreeNode> writtenDeclarations) {
if (typedefNode == null || !writtenDeclarations.add(typedefNode)) {
return;
}
DartType? rhsType = typedefNode.type;
if (rhsType != null) {
writeDeclarationForType(rhsType, writtenDeclarations);
}
StringBuffer sb = new StringBuffer();
sb.write("typedef ");
sb.write(computeName(typedefNode));
writeTypeParameters(typedefNode.typeParameters, sb);
sb.write(" ");
rhsType?.accept1(this, sb);
sb.write(";");
declarations.add("$sb");
}
void writeExtensionTypeDeclaration(
ExtensionTypeDeclaration? extensionTypeDeclaration,
Set<TreeNode> writtenDeclarations) {
if (extensionTypeDeclaration == null ||
!writtenDeclarations.add(extensionTypeDeclaration)) {
return;
}
writeDeclarationForType(extensionTypeDeclaration.declaredRepresentationType,
writtenDeclarations);
for (TypeDeclarationType implementedType
in extensionTypeDeclaration.implements) {
writeDeclaration(implementedType.typeDeclaration, writtenDeclarations);
}
StringBuffer sb = new StringBuffer();
sb.write("extension type ");
sb.write(computeName(extensionTypeDeclaration));
writeTypeParameters(extensionTypeDeclaration.typeParameters, sb);
sb.write("(");
extensionTypeDeclaration.declaredRepresentationType.accept1(this, sb);
sb.write(" it)");
bool first = true;
for (TypeDeclarationType implementedType
in extensionTypeDeclaration.implements) {
if (first) {
sb.write(" implements ");
} else {
sb.write(", ");
}
implementedType.accept1(this, sb);
first = false;
}
sb.write(";");
declarations.add("$sb");
}
String computeName(TreeNode node) {
String? name = nodeNames[node];
if (name != null) return name;
if (node
case Class(:var name, :var enclosingLibrary) ||
Typedef(:var name, :var enclosingLibrary) ||
ExtensionTypeDeclaration(:var name, :var enclosingLibrary)) {
Library library = enclosingLibrary;
String uriString = "${library.importUri}";
if (uriString == "dart:core" || uriString == "dart:async") {
if (!usedNames.add(name)) {
throw "Class name conflict for $node";
}
return nodeNames[node] = name;
}
}
while (!usedNames.add(name = names.current)) {
names.moveNext();
}
names.moveNext();
return nodeNames[node] = name;
}
String computeStructuralParameterName(StructuralParameter node) {
String? name = structuralParameterNames[node];
if (name != null) return name;
while (!usedNames.add(name = names.current)) {
names.moveNext();
}
names.moveNext();
return structuralParameterNames[node] = name;
}
void writeNullability(Nullability nullability, StringBuffer sb) {
switch (nullability) {
case Nullability.nullable:
sb.write("?");
break;
case Nullability.legacy:
sb.write("*");
break;
case Nullability.undetermined:
sb.write("%");
break;
case Nullability.nonNullable:
break;
}
}
@override
void visitAuxiliaryType(AuxiliaryType node, StringBuffer sb) {
if (node is UnknownType) {
sb.write("?");
} else {
throw "Unsupported auxiliary type ${node} (${node.runtimeType}).";
}
}
@override
void visitInvalidType(InvalidType node, StringBuffer sb) {
throw "not implemented";
}
@override
void visitDynamicType(DynamicType node, StringBuffer sb) {
sb.write("dynamic");
}
@override
void visitVoidType(VoidType node, StringBuffer sb) {
sb.write("void");
}
@override
void visitNeverType(NeverType node, StringBuffer sb) {
sb.write("Never");
writeNullability(node.nullability, sb);
}
@override
void visitNullType(NullType node, StringBuffer sb) {
sb.write("Null");
}
@override
void visitInterfaceType(InterfaceType node, StringBuffer sb) {
Class cls = node.classNode;
sb.write(computeName(cls));
if (node.typeArguments.isNotEmpty) {
sb.write("<");
bool first = true;
for (DartType type in node.typeArguments) {
if (!first) sb.write(", ");
type.accept1(this, sb);
first = false;
}
sb.write(">");
}
Uri clsImportUri = cls.enclosingLibrary.importUri;
bool isNull = cls.name == "Null" &&
clsImportUri.isScheme("dart") &&
clsImportUri.path == "core";
if (!isNull) {
writeNullability(node.nullability, sb);
}
}
@override
void visitFutureOrType(FutureOrType node, StringBuffer sb) {
sb.write("FutureOr<");
node.typeArgument.accept1(this, sb);
sb.write(">");
writeNullability(node.declaredNullability, sb);
}
@override
void visitFunctionType(FunctionType node, StringBuffer sb) {
writeStructuralParameters(node.typeParameters, sb);
sb.write("(");
bool first = true;
for (int i = 0; i < node.requiredParameterCount; i++) {
if (!first) sb.write(", ");
node.positionalParameters[i].accept1(this, sb);
first = false;
}
if (node.requiredParameterCount != node.positionalParameters.length) {
if (!first) sb.write(", ");
sb.write("[");
first = true;
for (int i = node.requiredParameterCount;
i < node.positionalParameters.length;
i++) {
if (!first) sb.write(", ");
node.positionalParameters[i].accept1(this, sb);
first = false;
}
sb.write("]");
first = false;
}
if (node.namedParameters.isNotEmpty) {
if (!first) sb.write(", ");
sb.write("{");
first = true;
for (NamedType named in node.namedParameters) {
if (!first) sb.write(", ");
named.type.accept1(this, sb);
sb.write(" ");
sb.write(named.name);
first = false;
}
sb.write("}");
first = false;
}
sb.write(") ->");
writeNullability(node.nullability, sb);
sb.write(" ");
node.returnType.accept1(this, sb);
}
@override
void visitRecordType(RecordType node, StringBuffer sb) {
sb.write("(");
bool first = true;
for (int i = 0; i < node.positional.length; i++) {
if (!first) sb.write(", ");
node.positional[i].accept1(this, sb);
first = false;
}
if (node.named.isNotEmpty) {
if (!first) sb.write(", ");
sb.write("{");
first = true;
for (NamedType named in node.named) {
if (!first) sb.write(", ");
named.type.accept1(this, sb);
sb.write(" ");
sb.write(named.name);
first = false;
}
sb.write("}");
first = false;
}
sb.write(")");
writeNullability(node.nullability, sb);
}
@override
void visitTypeParameterType(TypeParameterType node, StringBuffer sb) {
String name = computeName(node.parameter);
usedTypeParameters.add(node.parameter);
sb.write(name);
writeNullability(node.nullability, sb);
}
@override
void visitStructuralParameterType(
StructuralParameterType node, StringBuffer sb) {
String name = computeStructuralParameterName(node.parameter);
usedStructuralParameters.add(node.parameter);
sb.write(name);
writeNullability(node.nullability, sb);
}
@override
void visitIntersectionType(IntersectionType node, StringBuffer sb) {
node.left.accept1(this, sb);
sb.write(" & ");
node.right.accept1(this, sb);
}
@override
void visitTypedefType(TypedefType node, StringBuffer sb) {
Typedef typedefNode = node.typedefNode;
sb.write(computeName(typedefNode));
if (node.typeArguments.isNotEmpty) {
sb.write("<");
bool first = true;
for (DartType type in node.typeArguments) {
if (!first) sb.write(", ");
type.accept1(this, sb);
first = false;
}
sb.write(">");
}
Uri clsImportUri = typedefNode.enclosingLibrary.importUri;
bool isNull = typedefNode.name == "Null" &&
clsImportUri.isScheme("dart") &&
clsImportUri.path == "core";
if (!isNull) {
writeNullability(node.nullability, sb);
}
}
@override
void visitExtensionType(ExtensionType node, StringBuffer sb) {
ExtensionTypeDeclaration extensionTypeDeclaration =
node.extensionTypeDeclaration;
sb.write(computeName(extensionTypeDeclaration));
if (node.typeArguments.isNotEmpty) {
sb.write("<");
bool first = true;
for (DartType type in node.typeArguments) {
if (!first) sb.write(", ");
type.accept1(this, sb);
first = false;
}
sb.write(">");
}
Uri clsImportUri = extensionTypeDeclaration.enclosingLibrary.importUri;
bool isNull = extensionTypeDeclaration.name == "Null" &&
clsImportUri.isScheme("dart") &&
clsImportUri.path == "core";
if (!isNull) {
writeNullability(node.nullability, sb);
}
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
"classes": declarations,
"checks": checks,
};
}
static void writeTypeChecks(String filename, List<Object> typeChecks) {
new File(filename)
.writeAsString(new BenchMaker().serializeTypeChecks(typeChecks));
}
}