| // Copyright (c) 2020, 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.md file. |
| |
| // @dart = 2.9 |
| |
| import 'dart:io' show File, Platform, stdin, exitCode; |
| |
| import 'package:front_end/src/api_prototype/compiler_options.dart'; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/class_hierarchy.dart'; |
| |
| import 'incremental_suite.dart' as helper; |
| |
| main(List<String> args) async { |
| exitCode = 1; |
| Map<Uri, List<Class>> classMap = {}; |
| Map<Uri, List<Class>> classMapWithOne = {}; |
| Component c; |
| int toGo = 0; |
| ClassHierarchy classHierarchy; |
| Class memberClass; |
| Class primitiveConstantClass; |
| |
| { |
| Uri input = Platform.script.resolve("../tool/_fasta/compile.dart"); |
| CompilerOptions options = helper.getOptions(); |
| helper.TestIncrementalCompiler compiler = |
| new helper.TestIncrementalCompiler( |
| options, |
| input, |
| /*Uri initializeFrom*/ null, |
| /*bool outlineOnly*/ true); |
| c = await compiler.computeDelta(); |
| classHierarchy = compiler.getClassHierarchy(); |
| List<Library> libraries = c.libraries |
| .where((Library lib) => |
| (lib.importUri.toString() == "package:kernel/ast.dart")) |
| .toList(); |
| Library astLibrary = libraries.single; |
| List<Class> classes = |
| astLibrary.classes.where((Class c) => c.name == "Node").toList(); |
| Class nodeClass = classes.single; |
| classes = |
| astLibrary.classes.where((Class c) => c.name == "Member").toList(); |
| memberClass = classes.single; |
| classes = astLibrary.classes |
| .where((Class c) => c.name == "PrimitiveConstant") |
| .toList(); |
| primitiveConstantClass = classes.single; |
| |
| for (Library library in c.libraries) { |
| for (Class c in library.classes) { |
| if (c.isAbstract) continue; |
| if (classHierarchy.isSubtypeOf(c, nodeClass)) { |
| List<Member> toStringList = classHierarchy |
| .getInterfaceMembers(c) |
| .where((Member m) => |
| !m.isAbstract && |
| m.name.text == "toString" && |
| m.enclosingLibrary.importUri.scheme != "dart") |
| .toList(); |
| if (toStringList.length > 1) throw "What?"; |
| if (toStringList.length == 1) { |
| classMapWithOne[c.fileUri] ??= <Class>[]; |
| classMapWithOne[c.fileUri].add(c); |
| continue; |
| } |
| toGo++; |
| |
| classMap[c.fileUri] ??= <Class>[]; |
| classMap[c.fileUri].add(c); |
| } |
| } |
| } |
| } |
| if (toGo == 0) { |
| print("OK"); |
| exitCode = 0; |
| } else { |
| String classes = classMap.values |
| .map((list) => list.map((cls) => cls.name).join(', ')) |
| .join(', '); |
| print("Missing toString() on $toGo class(es): ${classes}"); |
| |
| if (args.length == 1 && args.single == "--interactive") { |
| for (Uri uri in classMap.keys) { |
| List<Class> classes = classMap[uri]; |
| print("Would you like to update ${classes.length} classes in ${uri}?" |
| " (y/n)"); |
| if (stdin.readLineSync() != "y") { |
| print("Skipping $uri"); |
| continue; |
| } |
| print("Continuing on $uri"); |
| classes.sort((Class a, Class b) { |
| return a.fileEndOffset - b.fileEndOffset; |
| }); |
| File f = new File.fromUri(uri); |
| String src = f.readAsStringSync(); |
| StringBuffer newSrc = new StringBuffer(); |
| int from = 0; |
| for (Class c in classes) { |
| String innerContent = ""; |
| if (classHierarchy.isSubtypeOf(c, memberClass)) { |
| innerContent = "\$name"; |
| } else if (classHierarchy.isSubtypeOf(c, primitiveConstantClass)) { |
| innerContent = "\$value"; |
| } |
| int to = c.fileEndOffset; |
| newSrc.write(src.substring(from, to)); |
| // We're just before the final "}". |
| newSrc.write(""" |
| |
| @override |
| String toString() { |
| return "${c.name}(\${toStringInternal()})"; |
| } |
| |
| @override |
| String toStringInternal() { |
| return "${innerContent}"; |
| } |
| """); |
| from = to; |
| } |
| newSrc.write(src.substring(from)); |
| f.writeAsStringSync(newSrc.toString()); |
| } |
| } |
| } |
| |
| if (args.length == 1 && args.single == "--interactive") { |
| for (Uri uri in classMapWithOne.keys) { |
| List<Class> classes = classMapWithOne[uri]; |
| print("Would you like to update toString for ${classes.length} " |
| "classes in ${uri}? (y/n)"); |
| if (stdin.readLineSync() != "y") { |
| print("Skipping $uri"); |
| continue; |
| } |
| print("Continuing on $uri"); |
| classes.sort((Class a, Class b) { |
| return a.fileEndOffset - b.fileEndOffset; |
| }); |
| File f = new File.fromUri(uri); |
| String src = f.readAsStringSync(); |
| StringBuffer newSrc = new StringBuffer(); |
| int from = 0; |
| for (Class c in classes) { |
| String innerContent = "()"; |
| if (classHierarchy.isSubtypeOf(c, memberClass)) { |
| innerContent = r"($name)"; |
| } else if (classHierarchy.isSubtypeOf(c, primitiveConstantClass)) { |
| innerContent = r"($value)"; |
| } |
| |
| List<Member> toStringList = classHierarchy |
| .getInterfaceMembers(c) |
| .where((Member m) => |
| !m.isAbstract && |
| m.name.text == "toString" && |
| m.enclosingLibrary.importUri.scheme != "dart") |
| .toList(); |
| Member toString = toStringList.single; |
| if (toString.fileUri != uri) continue; |
| int end = toString.fileEndOffset + 1; |
| String existing = src.substring(toString.fileOffset, end).trim(); |
| if (!existing.contains('return "${c.name}${innerContent}";')) { |
| continue; |
| } |
| |
| innerContent = ""; |
| if (classHierarchy.isSubtypeOf(c, memberClass)) { |
| innerContent = "\$name"; |
| } else if (classHierarchy.isSubtypeOf(c, primitiveConstantClass)) { |
| innerContent = "\$value"; |
| } |
| |
| int to = toString.fileOffset; |
| newSrc.write(src.substring(from, to)); |
| // We're just before the final "}". |
| newSrc.write(""" |
| toString() { |
| return "${c.name}(\${toStringInternal()})"; |
| } |
| |
| @override |
| String toStringInternal() { |
| return "${innerContent}"; |
| }"""); |
| from = toString.fileEndOffset + 1; |
| } |
| newSrc.write(src.substring(from)); |
| f.writeAsStringSync(newSrc.toString()); |
| } |
| } |
| } |