| // 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. | 
 |  | 
 | library fasta.tool.entry_points; | 
 |  | 
 | import "dart:convert" show JsonEncoder; | 
 |  | 
 | import "dart:io" show File; | 
 |  | 
 | import "package:kernel/ast.dart" hide MapEntry; | 
 |  | 
 | 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.name == "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 visitBottomType(BottomType node, StringBuffer sb) { | 
 |     sb.write("⊥"); | 
 |   } | 
 |  | 
 |   @override | 
 |   void visitNeverType(NeverType node, StringBuffer sb) { | 
 |     sb.write("Never"); | 
 |     writeNullability(node.nullability, sb); | 
 |   } | 
 |  | 
 |   @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"; | 
 |   } | 
 |  | 
 |   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)); | 
 |   } | 
 | } |