// 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 nameGenerator() sync* { int i = 0; while (true) { List characters = []; 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 { final List checks = []; final Map nodeNames = {}; final Set usedNames = new Set(); final Iterator names = nameGenerator().iterator..moveNext(); final List classes = []; final List usedTypeParameters = []; String serializeTypeChecks(List typeChecks) { for (List 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 arguments = [sString, tString]; Set seenTypeParameters = new Set(); List parameterStrings = []; while (usedTypeParameters.isNotEmpty) { List 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({ "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 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 writtenClasses = new Set(); int index = 0; List 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 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 toJson() { return { "classes": classes, "checks": checks, }; } static void writeTypeChecks(String filename, List typeChecks) { new File(filename) .writeAsString(new BenchMaker().serializeTypeChecks(typeChecks)); } }