Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:convert'; | 6 import 'dart:convert'; |
| 7 import 'dart:io'; | 7 import 'dart:io'; |
| 8 | 8 |
| 9 import 'package:async_helper/async_helper.dart'; | 9 import 'package:async_helper/async_helper.dart'; |
| 10 import 'package:compiler/compiler_new.dart'; | 10 import 'package:compiler/compiler_new.dart'; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 } | 62 } |
| 63 } | 63 } |
| 64 ''', | 64 ''', |
| 65 ''' | 65 ''' |
| 66 import 'package:expect/expect.dart'; | 66 import 'package:expect/expect.dart'; |
| 67 main() { | 67 main() { |
| 68 var c = @{1:main}new Class(); | 68 var c = @{1:main}new Class(); |
| 69 } | 69 } |
| 70 class Class { | 70 class Class { |
| 71 @NoInline() | 71 @NoInline() |
| 72 Class() { | 72 @{2:Class}Class() { |
| 73 @{2:Class}throw '$EXCEPTION_MARKER'; | 73 @{3:Class}throw '$EXCEPTION_MARKER'; |
| 74 } | 74 } |
| 75 } | 75 } |
| 76 ''', | 76 ''', |
| 77 ''' | 77 ''' |
| 78 import 'package:expect/expect.dart'; | 78 import 'package:expect/expect.dart'; |
| 79 main() { | 79 main() { |
| 80 @{1:main}test(); | 80 @{1:main}test(); |
| 81 } | 81 } |
| 82 @NoInline() | 82 @NoInline() |
| 83 test() { | 83 test() { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 @NoInline() | 122 @NoInline() |
| 123 test(c) { | 123 test(c) { |
| 124 @{2:test}c.field.method(); | 124 @{2:test}c.field.method(); |
| 125 } | 125 } |
| 126 class Class { | 126 class Class { |
| 127 var field; | 127 var field; |
| 128 } | 128 } |
| 129 ''', | 129 ''', |
| 130 ''' | 130 ''' |
| 131 import 'package:expect/expect.dart'; | 131 import 'package:expect/expect.dart'; |
| 132 class MyType { | |
| 133 get length => 3; // ensures we build an interceptor for `.length` | |
| 134 } | |
| 135 | |
| 136 main() { | |
| 137 confuse('').trim(); // includes some code above the interceptors | |
| 138 confuse([]).length; | |
| 139 confuse(new MyType()).length; | |
| 140 // TODO(johnniwinther): Intercepted access should point to 'length': | |
| 141 @{1:main}confuse(null).length; // called through the interceptor | |
| 142 } | |
| 143 | |
| 144 @NoInline() | |
| 145 confuse(x) => x;''', | |
| 146 ''' | |
| 147 import 'package:expect/expect.dart'; | |
| 132 main() { | 148 main() { |
| 133 // This call is no longer on the stack when the error is thrown. | 149 // This call is no longer on the stack when the error is thrown. |
| 134 @{:main}test(); | 150 @{:main}test(); |
| 135 } | 151 } |
| 136 @NoInline() | 152 @NoInline() |
| 137 test() async { | 153 test() async { |
| 138 @{1:test}throw '$EXCEPTION_MARKER'; | 154 @{1:test}throw '$EXCEPTION_MARKER'; |
| 139 } | 155 } |
| 140 ''', | 156 ''', |
| 141 ''' | 157 ''' |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 171 import 'package:expect/expect.dart'; | 187 import 'package:expect/expect.dart'; |
| 172 main() { | 188 main() { |
| 173 // This call is no longer on the stack when the error is thrown. | 189 // This call is no longer on the stack when the error is thrown. |
| 174 @{:main}test(); | 190 @{:main}test(); |
| 175 } | 191 } |
| 176 test() async { | 192 test() async { |
| 177 var c = @{1:test}new Class(); | 193 var c = @{1:test}new Class(); |
| 178 } | 194 } |
| 179 class Class { | 195 class Class { |
| 180 @NoInline() | 196 @NoInline() |
| 181 Class() { | 197 @{2:Class}Class() { |
| 182 @{2:Class}throw '$EXCEPTION_MARKER'; | 198 @{3:Class}throw '$EXCEPTION_MARKER'; |
| 183 } | 199 } |
| 184 } | 200 } |
| 185 ''', | 201 ''', |
| 186 ]; | 202 ]; |
| 187 | 203 |
| 188 class Test { | 204 class Test { |
| 189 final String code; | 205 final String code; |
| 190 final List<StackTraceLine> expectedLines; | 206 final List<StackTraceLine> expectedLines; |
| 191 final List<StackTraceLine> unexpectedLines; | 207 final List<StackTraceLine> unexpectedLines; |
| 192 | 208 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 218 List<StackTraceLine> expectedLines = <StackTraceLine>[]; | 234 List<StackTraceLine> expectedLines = <StackTraceLine>[]; |
| 219 for (int stackTraceIndex in (stackTraceMap.keys.toList()..sort()).reversed) { | 235 for (int stackTraceIndex in (stackTraceMap.keys.toList()..sort()).reversed) { |
| 220 expectedLines.add(stackTraceMap[stackTraceIndex]); | 236 expectedLines.add(stackTraceMap[stackTraceIndex]); |
| 221 } | 237 } |
| 222 return new Test(annotatedCode.sourceCode, expectedLines, unexpectedLines); | 238 return new Test(annotatedCode.sourceCode, expectedLines, unexpectedLines); |
| 223 } | 239 } |
| 224 | 240 |
| 225 void main(List<String> arguments) { | 241 void main(List<String> arguments) { |
| 226 bool verbose = false; | 242 bool verbose = false; |
| 227 bool printJs = false; | 243 bool printJs = false; |
| 244 bool writeJs = false; | |
| 228 List<int> indices; | 245 List<int> indices; |
| 229 for (String arg in arguments) { | 246 for (String arg in arguments) { |
| 230 if (arg == '-v') { | 247 if (arg == '-v') { |
| 231 verbose = true; | 248 verbose = true; |
| 232 } else if (arg == '--print-js') { | 249 } else if (arg == '--print-js') { |
| 233 printJs = true; | 250 printJs = true; |
| 251 } else if (arg == '--write-js') { | |
| 252 writeJs = true; | |
| 234 } else { | 253 } else { |
| 235 int index = int.parse(arg, onError: (_) => null); | 254 int index = int.parse(arg, onError: (_) => null); |
| 236 if (index != null) { | 255 if (index != null) { |
| 237 indices ??= <int>[]; | 256 indices ??= <int>[]; |
| 238 if (index < 0 || index >= TESTS.length) { | 257 if (index < 0 || index >= TESTS.length) { |
| 239 print('Index $index out of bounds: [0;${TESTS.length - 1}]'); | 258 print('Index $index out of bounds: [0;${TESTS.length - 1}]'); |
| 240 } else { | 259 } else { |
| 241 indices.add(index); | 260 indices.add(index); |
| 242 } | 261 } |
| 243 } | 262 } |
| 244 } | 263 } |
| 245 } | 264 } |
| 246 if (indices == null) { | 265 if (indices == null) { |
| 247 indices = new List<int>.generate(TESTS.length, (i) => i); | 266 indices = new List<int>.generate(TESTS.length, (i) => i); |
| 248 } | 267 } |
| 249 asyncTest(() async { | 268 asyncTest(() async { |
| 250 for (int index in indices) { | 269 for (int index in indices) { |
| 251 await runTest(index, processTestCode(TESTS[index]), | 270 await runTest(index, processTestCode(TESTS[index]), |
| 252 printJs: printJs, verbose: verbose); | 271 printJs: printJs, writeJs: writeJs, verbose: verbose); |
| 253 } | 272 } |
| 254 }); | 273 }); |
| 255 } | 274 } |
| 256 | 275 |
| 257 Future runTest(int index, Test test, | 276 Future runTest(int index, Test test, |
| 258 {bool printJs: false, bool verbose: false}) async { | 277 {bool printJs: false, bool writeJs, bool verbose: false}) async { |
| 259 Directory tmpDir = await createTempDir(); | 278 Directory tmpDir = await createTempDir(); |
| 260 String input = '${tmpDir.path}/$INPUT_FILE_NAME'; | 279 String input = '${tmpDir.path}/$INPUT_FILE_NAME'; |
| 261 new File(input).writeAsStringSync(test.code); | 280 new File(input).writeAsStringSync(test.code); |
| 262 String output = '${tmpDir.path}/out.js'; | 281 String output = '${tmpDir.path}/out.js'; |
| 263 List<String> arguments = [ | 282 List<String> arguments = [ |
| 264 '-o$output', | 283 '-o$output', |
| 265 '--library-root=sdk', | 284 '--library-root=sdk', |
| 266 '--packages=${Platform.packageConfig}', | 285 '--packages=${Platform.packageConfig}', |
| 267 Flags.useNewSourceInfo, | 286 Flags.useNewSourceInfo, |
| 268 input, | 287 input, |
| 269 ]; | 288 ]; |
| 270 print("--$index------------------------------------------------------------"); | 289 print("--$index------------------------------------------------------------"); |
| 271 print("Compiling dart2js ${arguments.join(' ')}\n${test.code}"); | 290 print("Compiling dart2js ${arguments.join(' ')}\n${test.code}"); |
| 272 CompilationResult compilationResult = await entry.internalMain(arguments); | 291 CompilationResult compilationResult = await entry.internalMain(arguments); |
| 273 Expect.isTrue(compilationResult.isSuccess, | 292 Expect.isTrue(compilationResult.isSuccess, |
| 274 "Unsuccessful compilation of test:\n${test.code}"); | 293 "Unsuccessful compilation of test:\n${test.code}"); |
| 275 CompilerImpl compiler = compilationResult.compiler; | 294 String sourceMapText = new File('$output.map').readAsStringSync(); |
| 276 SingleMapping sourceMap = new SingleMapping.fromJson( | 295 SingleMapping sourceMap = |
| 277 JSON.decode(new File('$output.map').readAsStringSync())); | 296 new SingleMapping.fromJson(JSON.decode(sourceMapText)); |
|
Siggi Cherem (dart-lang)
2017/01/26 19:22:18
nit: you should be able to do:
SingleMapping sour
Johnni Winther
2017/01/27 08:08:13
Done.
| |
| 278 | 297 |
| 279 if (printJs) { | 298 if (printJs) { |
| 280 print('JavaScript output:'); | 299 print('JavaScript output:'); |
| 281 print(new File(output).readAsStringSync()); | 300 print(new File(output).readAsStringSync()); |
| 282 } | 301 } |
| 302 if (writeJs) { | |
| 303 new File('out.js').writeAsStringSync(new File(output).readAsStringSync()); | |
| 304 new File('out.js.map').writeAsStringSync(sourceMapText); | |
| 305 } | |
| 283 print("Running d8 $output"); | 306 print("Running d8 $output"); |
| 284 ProcessResult runResult = Process.runSync(d8executable, | 307 ProcessResult runResult = Process.runSync(d8executable, |
| 285 ['sdk/lib/_internal/js_runtime/lib/preambles/d8.js', output]); | 308 ['sdk/lib/_internal/js_runtime/lib/preambles/d8.js', output]); |
| 286 String out = '${runResult.stderr}\n${runResult.stdout}'; | 309 String out = '${runResult.stderr}\n${runResult.stdout}'; |
| 287 if (verbose) { | 310 if (verbose) { |
| 288 print('d8 output:'); | 311 print('d8 output:'); |
| 289 print(out); | 312 print(out); |
| 290 } | 313 } |
| 291 List<String> lines = out.split(new RegExp(r'(\r|\n|\r\n)')); | 314 List<String> lines = out.split(new RegExp(r'(\r|\n|\r\n)')); |
| 292 List<StackTraceLine> jsStackTrace = <StackTraceLine>[]; | 315 List<StackTraceLine> jsStackTrace = <StackTraceLine>[]; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 305 } else { | 328 } else { |
| 306 String methodName; | 329 String methodName; |
| 307 if (targetEntry.sourceNameId != null) { | 330 if (targetEntry.sourceNameId != null) { |
| 308 methodName = sourceMap.names[targetEntry.sourceNameId]; | 331 methodName = sourceMap.names[targetEntry.sourceNameId]; |
| 309 } | 332 } |
| 310 String fileName; | 333 String fileName; |
| 311 if (targetEntry.sourceUrlId != null) { | 334 if (targetEntry.sourceUrlId != null) { |
| 312 fileName = sourceMap.urls[targetEntry.sourceUrlId]; | 335 fileName = sourceMap.urls[targetEntry.sourceUrlId]; |
| 313 } | 336 } |
| 314 dartStackTrace.add(new StackTraceLine(methodName, fileName, | 337 dartStackTrace.add(new StackTraceLine(methodName, fileName, |
| 315 targetEntry.sourceLine + 1, targetEntry.sourceColumn + 1)); | 338 targetEntry.sourceLine + 1, targetEntry.sourceColumn + 1, |
| 339 isMapped: true)); | |
| 316 } | 340 } |
| 317 } | 341 } |
| 318 | 342 |
| 319 int expectedIndex = 0; | 343 int expectedIndex = 0; |
| 320 List<StackTraceLine> unexpectedLines = <StackTraceLine>[]; | 344 List<StackTraceLine> unexpectedLines = <StackTraceLine>[]; |
| 345 List<StackTraceLine> unexpectedBeforeLines = <StackTraceLine>[]; | |
| 346 List<StackTraceLine> unexpectedAfterLines = <StackTraceLine>[]; | |
| 321 for (StackTraceLine line in dartStackTrace) { | 347 for (StackTraceLine line in dartStackTrace) { |
| 348 bool found = false; | |
| 322 if (expectedIndex < test.expectedLines.length) { | 349 if (expectedIndex < test.expectedLines.length) { |
| 323 StackTraceLine expectedLine = test.expectedLines[expectedIndex]; | 350 StackTraceLine expectedLine = test.expectedLines[expectedIndex]; |
| 324 if (line.methodName == expectedLine.methodName && | 351 if (line.methodName == expectedLine.methodName && |
| 325 line.lineNo == expectedLine.lineNo && | 352 line.lineNo == expectedLine.lineNo && |
| 326 line.columnNo == expectedLine.columnNo) { | 353 line.columnNo == expectedLine.columnNo) { |
| 354 found = true; | |
| 327 expectedIndex++; | 355 expectedIndex++; |
| 328 } | 356 } |
| 329 } | 357 } |
| 330 for (StackTraceLine unexpectedLine in test.unexpectedLines) { | 358 for (StackTraceLine unexpectedLine in test.unexpectedLines) { |
| 331 if (line.methodName == unexpectedLine.methodName && | 359 if (line.methodName == unexpectedLine.methodName && |
| 332 line.lineNo == unexpectedLine.lineNo && | 360 line.lineNo == unexpectedLine.lineNo && |
| 333 line.columnNo == unexpectedLine.columnNo) { | 361 line.columnNo == unexpectedLine.columnNo) { |
| 334 unexpectedLines.add(line); | 362 unexpectedLines.add(line); |
| 335 } | 363 } |
| 336 } | 364 } |
| 365 if (line.isMapped && !found) { | |
| 366 List<LineException> exceptions = | |
| 367 expectedIndex == 0 ? beforeExceptions : afterExceptions; | |
| 368 for (LineException exception in exceptions) { | |
| 369 if (line.methodName == exception.methodName && | |
| 370 line.fileName.endsWith(exception.fileName)) { | |
| 371 found = true; | |
| 372 } | |
| 373 } | |
| 374 if (!found) { | |
| 375 if (expectedIndex == 0) { | |
| 376 unexpectedBeforeLines.add(line); | |
| 377 } else { | |
| 378 unexpectedAfterLines.add(line); | |
| 379 } | |
| 380 } | |
| 381 } | |
| 337 } | 382 } |
| 338 if (verbose) { | 383 if (verbose) { |
| 339 print('JavaScript stacktrace:'); | 384 print('JavaScript stacktrace:'); |
| 340 print(jsStackTrace.join('\n')); | 385 print(jsStackTrace.join('\n')); |
| 341 print('Dart stacktrace:'); | 386 print('Dart stacktrace:'); |
| 342 print(dartStackTrace.join('\n')); | 387 print(dartStackTrace.join('\n')); |
| 343 } | 388 } |
| 344 Expect.equals( | 389 Expect.equals( |
| 345 expectedIndex, | 390 expectedIndex, |
| 346 test.expectedLines.length, | 391 test.expectedLines.length, |
| 347 "Missing stack trace lines for test:\n${test.code}\n" | 392 "Missing stack trace lines for test:\n${test.code}\n" |
| 348 "Actual:\n${dartStackTrace.join('\n')}\n" | 393 "Actual:\n${dartStackTrace.join('\n')}\n" |
| 349 "Expected:\n${test.expectedLines.join('\n')}\n"); | 394 "Expected:\n${test.expectedLines.join('\n')}\n"); |
| 350 Expect.isTrue( | 395 Expect.isTrue( |
| 351 unexpectedLines.isEmpty, | 396 unexpectedLines.isEmpty, |
| 352 "Unexpected stack trace lines for test:\n${test.code}\n" | 397 "Unexpected stack trace lines for test:\n${test.code}\n" |
| 353 "Actual:\n${dartStackTrace.join('\n')}\n" | 398 "Actual:\n${dartStackTrace.join('\n')}\n" |
| 354 "Unexpected:\n${test.unexpectedLines.join('\n')}\n"); | 399 "Unexpected:\n${test.unexpectedLines.join('\n')}\n"); |
| 400 Expect.isTrue( | |
| 401 unexpectedBeforeLines.isEmpty && unexpectedAfterLines.isEmpty, | |
| 402 "Unexpected stack trace lines:\n${test.code}\n" | |
| 403 "Actual:\n${dartStackTrace.join('\n')}\n" | |
| 404 "Unexpected before:\n${unexpectedBeforeLines.join('\n')}\n" | |
| 405 "Unexpected after:\n${unexpectedAfterLines.join('\n')}\n"); | |
| 355 | 406 |
| 356 print("Deleting '${tmpDir.path}'."); | 407 print("Deleting '${tmpDir.path}'."); |
| 357 tmpDir.deleteSync(recursive: true); | 408 tmpDir.deleteSync(recursive: true); |
| 358 } | 409 } |
| 359 | 410 |
| 360 class StackTraceLine { | 411 class StackTraceLine { |
| 361 String methodName; | 412 String methodName; |
| 362 String fileName; | 413 String fileName; |
| 363 int lineNo; | 414 int lineNo; |
| 364 int columnNo; | 415 int columnNo; |
| 416 bool isMapped; | |
| 365 | 417 |
| 366 StackTraceLine(this.methodName, this.fileName, this.lineNo, this.columnNo); | 418 StackTraceLine(this.methodName, this.fileName, this.lineNo, this.columnNo, |
| 419 {this.isMapped: false}); | |
| 367 | 420 |
| 368 /// Creates a [StackTraceLine] by parsing a d8 stack trace line [text]. The | 421 /// Creates a [StackTraceLine] by parsing a d8 stack trace line [text]. The |
| 369 /// expected formats are | 422 /// expected formats are |
| 370 /// | 423 /// |
| 371 /// at <methodName>(<fileName>:<lineNo>:<columnNo>) | 424 /// at <methodName>(<fileName>:<lineNo>:<columnNo>) |
| 372 /// at <methodName>(<fileName>:<lineNo>) | 425 /// at <methodName>(<fileName>:<lineNo>) |
| 373 /// at <methodName>(<fileName>) | 426 /// at <methodName>(<fileName>) |
| 374 /// at <fileName>:<lineNo>:<columnNo> | 427 /// at <fileName>:<lineNo>:<columnNo> |
| 375 /// at <fileName>:<lineNo> | 428 /// at <fileName>:<lineNo> |
| 376 /// at <fileName> | 429 /// at <fileName> |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 471 String get d8executable { | 524 String get d8executable { |
| 472 if (Platform.isWindows) { | 525 if (Platform.isWindows) { |
| 473 return 'third_party/d8/windows/d8.exe'; | 526 return 'third_party/d8/windows/d8.exe'; |
| 474 } else if (Platform.isLinux) { | 527 } else if (Platform.isLinux) { |
| 475 return 'third_party/d8/linux/d8'; | 528 return 'third_party/d8/linux/d8'; |
| 476 } else if (Platform.isMacOS) { | 529 } else if (Platform.isMacOS) { |
| 477 return 'third_party/d8/macos/d8'; | 530 return 'third_party/d8/macos/d8'; |
| 478 } | 531 } |
| 479 throw new UnsupportedError('Unsupported platform.'); | 532 throw new UnsupportedError('Unsupported platform.'); |
| 480 } | 533 } |
| 534 | |
| 535 /// A line allowed in the mapped stack trace. | |
| 536 class LineException { | |
| 537 final String methodName; | |
| 538 final String fileName; | |
| 539 | |
| 540 const LineException(this.methodName, this.fileName); | |
| 541 } | |
| 542 | |
| 543 /// Lines allowed before the intended stack trace. Typically from helper | |
| 544 /// methods. | |
| 545 const List<LineException> beforeExceptions = const [ | |
| 546 const LineException('wrapException', 'js_helper.dart'), | |
| 547 ]; | |
| 548 | |
| 549 /// Lines allowed after the intended stack trace. Typically from the event | |
| 550 /// queue. | |
| 551 const List<LineException> afterExceptions = const [ | |
| 552 const LineException('_wrapJsFunctionForAsync', 'async_patch.dart'), | |
| 553 const LineException( | |
| 554 '_wrapJsFunctionForAsync.<anonymous function>', 'async_patch.dart'), | |
| 555 const LineException( | |
| 556 '_awaitOnObject.<anonymous function>', 'async_patch.dart'), | |
| 557 const LineException('_RootZone.runUnary', 'zone.dart'), | |
| 558 const LineException('_FutureListener.handleValue', 'future_impl.dart'), | |
| 559 const LineException( | |
| 560 '_Future._propagateToListeners.handleValueCallback', 'future_impl.dart'), | |
| 561 const LineException('_Future._propagateToListeners', 'future_impl.dart'), | |
| 562 const LineException( | |
| 563 '_Future._addListener.<anonymous function>', 'future_impl.dart'), | |
| 564 ]; | |
| OLD | NEW |