| // Copyright (c) 2016, 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 dart2js.serialization_helper; |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| import 'package:compiler/compiler_new.dart'; |
| import 'package:compiler/src/commandline_options.dart'; |
| import 'package:compiler/src/common/names.dart'; |
| import 'package:compiler/src/compiler.dart'; |
| import 'package:compiler/src/elements/elements.dart'; |
| import 'package:compiler/src/filenames.dart'; |
| |
| import '../memory_compiler.dart'; |
| import 'test_data.dart'; |
| |
| const String DEFAULT_DATA_FILE_NAME = 'out.data'; |
| |
| class Arguments { |
| final String filename; |
| final int start; |
| final int end; |
| final bool loadSerializedData; |
| final bool saveSerializedData; |
| final String serializedDataFileName; |
| final bool verbose; |
| |
| const Arguments( |
| {this.filename, |
| this.start, |
| this.end, |
| this.loadSerializedData: false, |
| this.saveSerializedData: false, |
| this.serializedDataFileName: DEFAULT_DATA_FILE_NAME, |
| this.verbose: false}); |
| |
| factory Arguments.from(List<String> arguments) { |
| String filename; |
| int start; |
| int end; |
| for (String arg in arguments) { |
| if (!arg.startsWith('-')) { |
| int index = int.parse(arg, onError: (_) => null); |
| if (index == null) { |
| filename = arg; |
| } else if (start == null) { |
| start = index; |
| } else { |
| end = index; |
| } |
| } |
| } |
| bool verbose = arguments.contains('-v'); |
| bool loadSerializedData = arguments.contains('-l'); |
| bool saveSerializedData = arguments.contains('-s'); |
| if (arguments.contains('--auto')) { |
| File file = new File(DEFAULT_DATA_FILE_NAME); |
| if (file.existsSync()) { |
| loadSerializedData = true; |
| } else { |
| saveSerializedData = true; |
| } |
| } |
| return new Arguments( |
| filename: filename, |
| start: start, |
| end: end, |
| verbose: verbose, |
| loadSerializedData: loadSerializedData, |
| saveSerializedData: saveSerializedData); |
| } |
| |
| Uri get uri { |
| if (filename != null) { |
| return Uri.base.resolve(nativeToUriPath(filename)); |
| } |
| return null; |
| } |
| |
| Future forEachTest(SerializedData serializedData, List<Test> tests, |
| TestFunction testFunction) async { |
| Uri entryPoint = Uri.parse('memory:main.dart'); |
| int first = start ?? 0; |
| int last = end ?? tests.length; |
| |
| for (int index = first; index < last; index++) { |
| Test test = TESTS[index]; |
| List<SerializedData> dataList = |
| await preserializeData(serializedData, test); |
| Map<String, String> sourceFiles = <String, String>{}; |
| sourceFiles.addAll(test.sourceFiles); |
| if (test.preserializedSourceFiles != null) { |
| sourceFiles.addAll(test.preserializedSourceFiles); |
| } |
| if (test.unserializedSourceFiles != null) { |
| sourceFiles.addAll(test.unserializedSourceFiles); |
| } |
| List<Uri> resolutionInputs = <Uri>[]; |
| for (SerializedData data in dataList) { |
| data.expandMemorySourceFiles(sourceFiles); |
| data.expandUris(resolutionInputs); |
| } |
| await testFunction(entryPoint, |
| sourceFiles: sourceFiles, |
| resolutionInputs: resolutionInputs, |
| index: index, |
| test: test, |
| verbose: verbose); |
| } |
| } |
| } |
| |
| typedef Future TestFunction(Uri entryPoint, |
| {Map<String, String> sourceFiles, |
| List<Uri> resolutionInputs, |
| int index, |
| Test test, |
| bool verbose}); |
| |
| Future<SerializedData> serializeDartCore( |
| {Arguments arguments: const Arguments()}) { |
| return measure('dart:core', 'serialize', () async { |
| Uri uri = Uri.parse('memory:${arguments.serializedDataFileName}'); |
| SerializedData serializedData; |
| if (arguments.loadSerializedData) { |
| File file = new File(arguments.serializedDataFileName); |
| if (file.existsSync()) { |
| print('Loading data from $file'); |
| serializedData = new SerializedData(uri, file.readAsStringSync()); |
| } |
| } else { |
| SerializationResult result = |
| await serialize(Uris.dart_core, dataUri: uri); |
| serializedData = result.serializedData; |
| } |
| if (arguments.saveSerializedData) { |
| File file = new File(arguments.serializedDataFileName); |
| print('Saving data to $file'); |
| file.writeAsStringSync(serializedData.data); |
| } |
| return serializedData; |
| }); |
| } |
| |
| class SerializationResult { |
| final Compiler compiler; |
| final SerializedData serializedData; |
| |
| SerializationResult(this.compiler, this.serializedData); |
| } |
| |
| Future<SerializationResult> serialize(Uri entryPoint, |
| {Map<String, String> memorySourceFiles: const <String, String>{}, |
| List<Uri> resolutionInputs: const <Uri>[], |
| Uri dataUri, |
| bool deserializeCompilationDataForTesting: false}) async { |
| if (dataUri == null) { |
| dataUri = Uri.parse('memory:${DEFAULT_DATA_FILE_NAME}'); |
| } |
| OutputCollector outputCollector = new OutputCollector(); |
| Compiler compiler = compilerFor( |
| options: [Flags.resolveOnly], |
| memorySourceFiles: memorySourceFiles, |
| resolutionInputs: resolutionInputs, |
| outputProvider: outputCollector); |
| compiler.serialization.deserializeCompilationDataForTesting = |
| deserializeCompilationDataForTesting; |
| await compiler.run(entryPoint); |
| SerializedData serializedData = new SerializedData( |
| dataUri, outputCollector.getOutput('', OutputType.serializationData)); |
| return new SerializationResult(compiler, serializedData); |
| } |
| |
| class SerializedData { |
| final Uri uri; |
| final String data; |
| |
| SerializedData(this.uri, this.data) { |
| assert(uri != null); |
| assert(data != null); |
| } |
| |
| Map<String, String> toMemorySourceFiles([Map<String, String> input]) { |
| Map<String, String> sourceFiles = <String, String>{}; |
| if (input != null) { |
| sourceFiles.addAll(input); |
| } |
| expandMemorySourceFiles(sourceFiles); |
| return sourceFiles; |
| } |
| |
| void expandMemorySourceFiles(Map<String, String> sourceFiles) { |
| if (uri.scheme == 'memory') { |
| sourceFiles[uri.path] = data; |
| } |
| } |
| |
| List<Uri> toUris([List<Uri> input]) { |
| List<Uri> uris = <Uri>[]; |
| if (input != null) { |
| uris.addAll(input); |
| } |
| expandUris(uris); |
| return uris; |
| } |
| |
| void expandUris(List<Uri> uris) { |
| uris.add(uri); |
| } |
| } |
| |
| Future<List<SerializedData>> preserializeData( |
| SerializedData serializedData, Test test) async { |
| if (test == null || |
| test.preserializedSourceFiles == null || |
| test.preserializedSourceFiles.isEmpty) { |
| return <SerializedData>[serializedData]; |
| } |
| |
| List<Uri> uriList = <Uri>[]; |
| for (String key in test.preserializedSourceFiles.keys) { |
| uriList.add(Uri.parse('memory:$key')); |
| } |
| Map<String, String> sourceFiles = serializedData.toMemorySourceFiles(); |
| sourceFiles.addAll(test.preserializedSourceFiles); |
| if (test.unserializedSourceFiles != null) { |
| sourceFiles.addAll(test.unserializedSourceFiles); |
| } |
| Uri additionalDataUri = Uri.parse('memory:additional.data'); |
| SerializedData additionalSerializedData; |
| if (test.sourceFiles.isEmpty) { |
| SerializationResult result = await serialize(uriList.first, |
| memorySourceFiles: sourceFiles, |
| resolutionInputs: serializedData.toUris(), |
| dataUri: additionalDataUri); |
| additionalSerializedData = result.serializedData; |
| } else { |
| OutputCollector outputCollector = new OutputCollector(); |
| Compiler compiler = compilerFor( |
| entryPoint: test.sourceFiles.isEmpty ? uriList.first : null, |
| memorySourceFiles: sourceFiles, |
| resolutionInputs: serializedData.toUris(), |
| options: [Flags.resolveOnly], |
| outputProvider: outputCollector); |
| compiler.librariesToAnalyzeWhenRun = uriList; |
| await compiler.run(null); |
| List<LibraryElement> libraries = <LibraryElement>[]; |
| for (Uri uri in uriList) { |
| libraries.add(compiler.libraryLoader.lookupLibrary(uri)); |
| } |
| additionalSerializedData = new SerializedData(additionalDataUri, |
| outputCollector.getOutput('', OutputType.serializationData)); |
| } |
| return <SerializedData>[serializedData, additionalSerializedData]; |
| } |
| |
| class MeasurementResult { |
| final String title; |
| final String taskTitle; |
| final int elapsedMilliseconds; |
| |
| MeasurementResult(this.title, this.taskTitle, this.elapsedMilliseconds); |
| } |
| |
| final List<MeasurementResult> measurementResults = <MeasurementResult>[]; |
| |
| /// Print all store [measurementResults] grouped by title and sorted by |
| /// decreasing execution time. |
| void printMeasurementResults() { |
| Map<String, int> totals = <String, int>{}; |
| |
| for (MeasurementResult result in measurementResults) { |
| totals.putIfAbsent(result.title, () => 0); |
| totals[result.title] += result.elapsedMilliseconds; |
| } |
| |
| List<String> sorted = totals.keys.toList(); |
| sorted.sort((a, b) => -totals[a].compareTo(totals[b])); |
| |
| int paddingLength = '${totals[sorted.first]}'.length; |
| |
| String pad(int value) { |
| String text = '$value'; |
| return '${' ' * (paddingLength - text.length)}$text'; |
| } |
| |
| print('================================================================'); |
| print('Summary:'); |
| for (String task in sorted) { |
| int time = totals[task]; |
| print('${pad(time)}ms $task'); |
| } |
| |
| measurementResults.clear(); |
| } |
| |
| /// Measure execution of [task], print the result and store it in |
| /// [measurementResults] for a summary. |
| Future measure(String title, String taskTitle, Future task()) async { |
| Stopwatch stopwatch = new Stopwatch()..start(); |
| print('================================================================'); |
| print('$taskTitle: $title'); |
| print('----------------------------------------------------------------'); |
| var result = await task(); |
| stopwatch.stop(); |
| int elapsedMilliseconds = stopwatch.elapsedMilliseconds; |
| print('$taskTitle: $title: ${elapsedMilliseconds}ms'); |
| measurementResults |
| .add(new MeasurementResult(title, taskTitle, elapsedMilliseconds)); |
| return result; |
| } |