8ddc7465a8
Change-Id: I2e8231dad00accafb09d1ec88416242d3d5815a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190483 Commit-Queue: Dmitry Stefantsov <dmitryas@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
365 lines
9.8 KiB
Dart
365 lines
9.8 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.
|
|
|
|
// @dart=2.9
|
|
|
|
library fasta.tool.entry_points;
|
|
|
|
import "dart:convert" show JsonEncoder;
|
|
|
|
import "dart:io" show File;
|
|
|
|
import "package:kernel/ast.dart";
|
|
|
|
import "package:front_end/src/fasta/type_inference/type_schema.dart"
|
|
show UnknownType;
|
|
|
|
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 Set<String> usedNames = new Set<String>();
|
|
|
|
final Iterator<String> names = nameGenerator().iterator..moveNext();
|
|
|
|
final List<String> classes = <String>[];
|
|
|
|
final List<TypeParameter> usedTypeParameters = <TypeParameter>[];
|
|
|
|
String serializeTypeChecks(List<Object> typeChecks) {
|
|
for (List<Object> list in typeChecks) {
|
|
writeTypeCheck(list[0], list[1], list[2]);
|
|
}
|
|
writeClasses();
|
|
return jsonEncode(this);
|
|
}
|
|
|
|
void writeTypeCheck(DartType s, DartType t, bool expected) {
|
|
assert(usedTypeParameters.isEmpty);
|
|
usedTypeParameters.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>();
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
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 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 writeClasses() {
|
|
Set<Class> writtenClasses = new Set<Class>();
|
|
int index = 0;
|
|
List<TreeNode> nodes = nodeNames.keys.toList();
|
|
while (index < nodes.length) {
|
|
for (; index < nodes.length; index++) {
|
|
TreeNode node = nodes[index];
|
|
if (node is Class) {
|
|
writeClass(node, writtenClasses);
|
|
}
|
|
}
|
|
nodes = nodeNames.keys.toList();
|
|
}
|
|
}
|
|
|
|
void writeClass(Class cls, Set<Class> writtenClasses) {
|
|
if (cls == null || !writtenClasses.add(cls)) return;
|
|
Supertype supertype = cls.supertype;
|
|
writeClass(supertype?.classNode, writtenClasses);
|
|
Supertype mixedInType = cls.mixedInType;
|
|
writeClass(mixedInType?.classNode, writtenClasses);
|
|
for (Supertype implementedType in cls.implementedTypes) {
|
|
writeClass(implementedType.classNode, writtenClasses);
|
|
}
|
|
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(";");
|
|
}
|
|
classes.add("$sb");
|
|
}
|
|
|
|
String computeName(TreeNode node) {
|
|
String name = nodeNames[node];
|
|
if (name != null) return name;
|
|
if (node is Class) {
|
|
Library library = node.enclosingLibrary;
|
|
String uriString = "${library?.importUri}";
|
|
if (uriString == "dart:core" || uriString == "dart:async") {
|
|
if (!usedNames.add(node.name)) {
|
|
throw "Class name conflict for $node";
|
|
}
|
|
return nodeNames[node] = node.name;
|
|
}
|
|
}
|
|
while (!usedNames.add(name = names.current)) {
|
|
names.moveNext();
|
|
}
|
|
names.moveNext();
|
|
return nodeNames[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:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void defaultDartType(DartType node, StringBuffer sb) {
|
|
if (node is UnknownType) {
|
|
sb.write("?");
|
|
} else {
|
|
throw "Unsupported: ${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.scheme == "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) {
|
|
writeTypeParameters(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 visitTypeParameterType(TypeParameterType node, StringBuffer sb) {
|
|
String name = computeName(node.parameter);
|
|
usedTypeParameters.add(node.parameter);
|
|
sb.write(name);
|
|
if (node.promotedBound != null) {
|
|
sb.write(" & ");
|
|
node.promotedBound.accept1(this, sb);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitTypedefType(TypedefType node, StringBuffer sb) {
|
|
throw "not implemented";
|
|
}
|
|
|
|
@override
|
|
void visitExtensionType(ExtensionType node, StringBuffer sb) {
|
|
throw "not implemented";
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return <String, dynamic>{
|
|
"classes": classes,
|
|
"checks": checks,
|
|
};
|
|
}
|
|
|
|
static void writeTypeChecks(String filename, List<Object> typeChecks) {
|
|
new File(filename)
|
|
.writeAsString(new BenchMaker().serializeTypeChecks(typeChecks));
|
|
}
|
|
}
|