| // 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. |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:compiler/compiler_new.dart'; |
| import 'package:compiler/src/apiimpl.dart'; |
| import 'package:compiler/src/commandline_options.dart'; |
| import 'package:compiler/src/dart2js.dart' as entry; |
| import 'package:expect/expect.dart'; |
| import 'package:source_maps/source_maps.dart'; |
| import 'package:source_maps/src/utils.dart'; |
| |
| import '../annotated_code_helper.dart'; |
| import '../memory_compiler.dart'; |
| import '../source_map_validator_helper.dart'; |
| |
| const List<String> TESTS = const <String>[ |
| ''' |
| @{main}main() { |
| @{main}} |
| ''', |
| ''' |
| @{main}main() { |
| @{+main}throw ''; |
| @{main}} |
| ''', |
| ''' |
| @{main}main() { |
| @{main}return 0; |
| @{main}} |
| ''', |
| ''' |
| import 'package:expect/expect.dart'; |
| @{main}main() { |
| @{main}test(); |
| @{main}} |
| |
| @NoInline() |
| @{test}test() { |
| @{test}} |
| ''', |
| ]; |
| |
| class Test { |
| final String annotatedCode; |
| final String code; |
| final List<SourceLocation> expectedLocations; |
| |
| Test(this.annotatedCode, this.code, this.expectedLocations); |
| } |
| |
| Test processTestCode(String code, {bool useNewSourceInfo}) { |
| List<SourceLocation> expectedLocations = <SourceLocation>[]; |
| AnnotatedCode annotatedCode = new AnnotatedCode.fromText(code); |
| for (Annotation annotation in annotatedCode.annotations) { |
| String methodName; |
| if (annotation.text.startsWith('-')) { |
| // Expect only in old source maps |
| if (useNewSourceInfo) continue; |
| methodName = annotation.text.substring(1); |
| } else if (annotation.text.startsWith('+')) { |
| // Expect only in new source maps |
| if (!useNewSourceInfo) continue; |
| methodName = annotation.text.substring(1); |
| } else { |
| methodName = annotation.text; |
| } |
| expectedLocations.add( |
| new SourceLocation(methodName, annotation.lineNo, annotation.columnNo)); |
| } |
| return new Test(code, annotatedCode.sourceCode, expectedLocations); |
| } |
| |
| void main(List<String> arguments) { |
| bool verbose = false; |
| bool printJs = false; |
| bool writeJs = false; |
| List<int> indices; |
| for (String arg in arguments) { |
| if (arg == '-v') { |
| verbose = true; |
| } else if (arg == '--print-js') { |
| printJs = true; |
| } else if (arg == '--write-js') { |
| writeJs = true; |
| } else { |
| int index = int.parse(arg, onError: (_) => null); |
| if (index != null) { |
| indices ??= <int>[]; |
| if (index < 0 || index >= TESTS.length * 2) { |
| print('Index $index out of bounds: [0;${TESTS.length - 1}]'); |
| } else { |
| indices.add(index); |
| } |
| } |
| } |
| } |
| if (indices == null) { |
| indices = new List<int>.generate(TESTS.length * 2, (i) => i); |
| } |
| asyncTest(() async { |
| for (int index in indices) { |
| bool useNewSourceInfo = index % 2 == 1; |
| await runTest( |
| index, |
| processTestCode(TESTS[index ~/ 2], |
| useNewSourceInfo: useNewSourceInfo), |
| printJs: printJs, |
| writeJs: writeJs, |
| verbose: verbose, |
| useNewSourceInfo: useNewSourceInfo); |
| } |
| }); |
| } |
| |
| Future runTest(int index, Test test, |
| {bool printJs: false, |
| bool writeJs, |
| bool verbose: false, |
| bool useNewSourceInfo: false}) async { |
| print("--$index------------------------------------------------------------"); |
| print("Compiling dart2js ${useNewSourceInfo ? Flags.useNewSourceInfo : ''}\n" |
| "${test.annotatedCode}"); |
| OutputCollector collector = new OutputCollector(); |
| List<String> options = <String>['--out=out.js', '--source-map=out.js.map']; |
| if (useNewSourceInfo) { |
| options.add(Flags.useNewSourceInfo); |
| } |
| CompilationResult compilationResult = await runCompiler( |
| entryPoint: Uri.parse('memory:main.dart'), |
| memorySourceFiles: {'main.dart': test.code}, |
| outputProvider: collector, |
| options: options); |
| Expect.isTrue(compilationResult.isSuccess, |
| "Unsuccessful compilation of test:\n${test.code}"); |
| String sourceMapText = collector.getOutput('', OutputType.sourceMap); |
| SingleMapping sourceMap = parse(sourceMapText); |
| if (writeJs) { |
| new File('out.js') |
| .writeAsStringSync(collector.getOutput('', OutputType.js)); |
| new File('out.js.map').writeAsStringSync(sourceMapText); |
| } |
| |
| Set<SourceLocation> expectedLocations = test.expectedLocations.toSet(); |
| List<SourceLocation> actualLocations = <SourceLocation>[]; |
| List<SourceLocation> extraLocations = <SourceLocation>[]; |
| for (TargetLineEntry targetLineEntry in sourceMap.lines) { |
| for (TargetEntry targetEntry in targetLineEntry.entries) { |
| if (targetEntry.sourceUrlId != null && |
| sourceMap.urls[targetEntry.sourceUrlId] == 'memory:main.dart') { |
| String methodName; |
| if (targetEntry.sourceNameId != null) { |
| methodName = sourceMap.names[targetEntry.sourceNameId]; |
| } |
| SourceLocation location = new SourceLocation(methodName, |
| targetEntry.sourceLine + 1, targetEntry.sourceColumn + 1); |
| actualLocations.add(location); |
| if (!expectedLocations.remove(location)) { |
| extraLocations.add(location); |
| } |
| } |
| } |
| } |
| |
| if (expectedLocations.isNotEmpty) { |
| print('--Missing source locations:---------------------------------------'); |
| AnnotatedCode annotatedCode = new AnnotatedCode(test.code, []); |
| expectedLocations.forEach( |
| (l) => annotatedCode.addAnnotation(l.lineNo, l.columnNo, l.methodName)); |
| print(annotatedCode.toText()); |
| print('------------------------------------------------------------------'); |
| Expect.isTrue( |
| expectedLocations.isEmpty, |
| "Missing source locations:\n${test.code}\n" |
| "Actual:\n${actualLocations.join('\n')}\n" |
| "Missing:\n${expectedLocations.join('\n')}\n"); |
| } |
| if (extraLocations.isNotEmpty) { |
| print('--Extra source locations:-----------------------------------------'); |
| AnnotatedCode annotatedCode = new AnnotatedCode(test.code, []); |
| extraLocations.forEach( |
| (l) => annotatedCode.addAnnotation(l.lineNo, l.columnNo, l.methodName)); |
| print(annotatedCode.toText()); |
| print('------------------------------------------------------------------'); |
| Expect.isTrue( |
| extraLocations.isEmpty, |
| "Extra source locations:\n${test.code}\n" |
| "Actual:\n${actualLocations.join('\n')}\n" |
| "Extra:\n${extraLocations.join('\n')}\n"); |
| } |
| } |
| |
| class SourceLocation { |
| final String methodName; |
| final int lineNo; |
| final int columnNo; |
| |
| SourceLocation(this.methodName, this.lineNo, this.columnNo); |
| |
| int get hashCode => |
| methodName.hashCode * 13 + lineNo.hashCode * 17 + columnNo.hashCode * 19; |
| |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| if (other is! SourceLocation) return false; |
| return methodName == other.methodName && |
| lineNo == other.lineNo && |
| columnNo == other.columnNo; |
| } |
| |
| String toString() => '$methodName:$lineNo:$columnNo'; |
| } |