Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 import 'dart:async'; | |
| 6 import 'dart:convert'; | |
| 7 import 'dart:io'; | |
| 8 | |
| 9 import 'package:async_helper/async_helper.dart'; | |
| 10 import 'package:compiler/compiler_new.dart'; | |
| 11 import 'package:compiler/src/apiimpl.dart'; | |
| 12 import 'package:compiler/src/commandline_options.dart'; | |
| 13 import 'package:compiler/src/dart2js.dart' as entry; | |
| 14 import 'package:expect/expect.dart'; | |
| 15 import 'package:source_maps/source_maps.dart'; | |
| 16 import 'package:source_maps/src/utils.dart'; | |
| 17 | |
| 18 import '../source_map_validator_helper.dart'; | |
| 19 | |
| 20 const String EXCEPTION_MARKER = '>ExceptionMarker<'; | |
| 21 const String INPUT_FILE_NAME = 'in.dart'; | |
| 22 | |
| 23 const List<String> TESTS = const <String>[ | |
| 24 ''' | |
| 25 main() { | |
| 26 @{1:main}throw '$EXCEPTION_MARKER'; | |
| 27 } | |
| 28 ''', | |
| 29 ''' | |
| 30 main() { | |
| 31 @{1:main}test(); | |
| 32 } | |
| 33 test() { | |
| 34 @{2:test}throw '$EXCEPTION_MARKER'; | |
| 35 } | |
| 36 ''', | |
| 37 ''' | |
| 38 main() { | |
| 39 @{1:main}Class.test(); | |
| 40 } | |
| 41 class Class { | |
| 42 static test() { | |
| 43 @{2:Class.test}throw '$EXCEPTION_MARKER'; | |
| 44 } | |
| 45 } | |
| 46 ''', | |
| 47 ''' | |
| 48 main() { | |
| 49 var c = new Class(); | |
| 50 c.@{1:main}test(); | |
| 51 } | |
| 52 class Class { | |
| 53 test() { | |
| 54 @{2:Class.test}throw '$EXCEPTION_MARKER'; | |
| 55 } | |
| 56 } | |
| 57 ''', | |
| 58 ''' | |
| 59 import 'package:expect/expect.dart'; | |
| 60 main() { | |
| 61 var c = @{1:main}new Class(); | |
| 62 } | |
| 63 class Class { | |
| 64 @NoInline() | |
| 65 Class() { | |
| 66 @{2:Class}throw '$EXCEPTION_MARKER'; | |
| 67 } | |
| 68 } | |
| 69 ''', | |
| 70 ]; | |
| 71 | |
| 72 class Test { | |
| 73 final String code; | |
| 74 final List<StackTraceLine> expectedLines; | |
| 75 | |
| 76 Test(this.code, this.expectedLines); | |
| 77 } | |
| 78 | |
| 79 Test processTestCode(String code) { | |
| 80 StringBuffer codeBuffer = new StringBuffer(); | |
| 81 Map<int, StackTraceLine> stackTraceMap = <int, StackTraceLine>{}; | |
| 82 int index = 0; | |
| 83 int lineNo = 1; | |
| 84 int columnNo = 1; | |
| 85 while (index < code.length) { | |
|
Siggi Cherem (dart-lang)
2016/10/27 13:25:19
the code here to compute start-lines seems very si
Johnni Winther
2016/11/02 14:34:30
This is not just for extracting the values but to
| |
| 86 int charCode = code.codeUnitAt(index); | |
| 87 switch (charCode) { | |
| 88 case 0x0A: | |
|
Siggi Cherem (dart-lang)
2016/10/27 13:25:19
can you define a couple const variables to give th
Johnni Winther
2016/11/02 14:34:30
Done.
| |
| 89 codeBuffer.write('\n'); | |
| 90 lineNo++; | |
| 91 columnNo = 1; | |
| 92 break; | |
| 93 case 0x0D: | |
| 94 if (index + 1 < code.length && code.codeUnitAt(index + 1) == 0x0A) { | |
| 95 index++; | |
| 96 } | |
| 97 codeBuffer.write('\n'); | |
| 98 lineNo++; | |
| 99 columnNo = 1; | |
| 100 break; | |
| 101 case 0x40: | |
| 102 if (index + 1 < code.length && code.codeUnitAt(index + 1) == 0x7B) { | |
| 103 int colonIndex = code.indexOf(':', index); | |
| 104 int endIndex = code.indexOf('}', index); | |
| 105 int stackTraceIndex = | |
| 106 int.parse(code.substring(index + 2, colonIndex)); | |
| 107 String methodName = code.substring(colonIndex + 1, endIndex); | |
| 108 assert(!stackTraceMap.containsKey(stackTraceIndex)); | |
| 109 stackTraceMap[stackTraceIndex] = | |
| 110 new StackTraceLine(methodName, INPUT_FILE_NAME, lineNo, columnNo); | |
| 111 index = endIndex; | |
| 112 } else { | |
| 113 codeBuffer.writeCharCode(charCode); | |
| 114 columnNo++; | |
| 115 } | |
| 116 break; | |
| 117 default: | |
| 118 codeBuffer.writeCharCode(charCode); | |
| 119 columnNo++; | |
| 120 } | |
| 121 index++; | |
| 122 } | |
| 123 List<StackTraceLine> expectedLines = <StackTraceLine>[]; | |
| 124 for (int stackTraceIndex in (stackTraceMap.keys.toList()..sort()).reversed) { | |
| 125 expectedLines.add(stackTraceMap[stackTraceIndex]); | |
| 126 } | |
| 127 return new Test(codeBuffer.toString(), expectedLines); | |
| 128 } | |
| 129 | |
| 130 void main(List<String> arguments) { | |
| 131 asyncTest(() async { | |
| 132 for (String code in TESTS) { | |
| 133 await runTest(processTestCode(code)); | |
| 134 } | |
| 135 }); | |
| 136 } | |
| 137 | |
| 138 Future runTest(Test test) async { | |
| 139 Directory tmpDir = await createTempDir(); | |
| 140 String input = '${tmpDir.path}/$INPUT_FILE_NAME'; | |
| 141 new File(input).writeAsStringSync(test.code); | |
| 142 String output = '${tmpDir.path}/out.js'; | |
| 143 List<String> arguments = [ | |
| 144 '-o$output', | |
| 145 '--library-root=sdk', | |
| 146 '--packages=${Platform.packageConfig}', | |
| 147 Flags.useNewSourceInfo, | |
| 148 input, | |
| 149 ]; | |
| 150 print("--------------------------------------------------------------------"); | |
| 151 print("Compiling dart2js ${arguments.join(' ')}\n${test.code}"); | |
| 152 CompilationResult compilationResult = await entry.internalMain(arguments); | |
| 153 Expect.isTrue(compilationResult.isSuccess, | |
| 154 "Unsuccessful compilation of test:\n${test.code}"); | |
| 155 CompilerImpl compiler = compilationResult.compiler; | |
| 156 SingleMapping sourceMap = new SingleMapping.fromJson( | |
| 157 JSON.decode(new File('$output.map').readAsStringSync())); | |
| 158 | |
| 159 print("Running d8 $output"); | |
| 160 ProcessResult runResult = | |
| 161 Process.runSync(d8executable, [output]); | |
|
Siggi Cherem (dart-lang)
2016/10/27 13:25:19
when running test.py, are we guaranteed that the c
Johnni Winther
2016/11/02 14:34:30
We are always running from the root (other unittes
| |
| 162 String out = '${runResult.stderr}\n${runResult.stdout}'; | |
| 163 List<String> lines = out.split(new RegExp(r'(\r|\n|\r\n)')); | |
| 164 List<StackTraceLine> jsStackTrace = <StackTraceLine>[]; | |
| 165 bool seenMarker = false; | |
| 166 for (String line in lines) { | |
| 167 if (seenMarker) { | |
| 168 line = line.trim(); | |
| 169 if (line.startsWith('at ')) { | |
| 170 jsStackTrace.add(new StackTraceLine.fromText(line)); | |
| 171 } | |
| 172 } else if (line == EXCEPTION_MARKER) { | |
| 173 seenMarker = true; | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 List<StackTraceLine> dartStackTrace = <StackTraceLine>[]; | |
| 178 for (StackTraceLine line in jsStackTrace) { | |
| 179 TargetEntry targetEntry = _findColumn(line.lineNo - 1, line.columnNo - 1, | |
| 180 _findLine(sourceMap, line.lineNo - 1)); | |
| 181 if (targetEntry == null) { | |
| 182 dartStackTrace.add(line); | |
| 183 } else { | |
| 184 String methodName; | |
| 185 if (targetEntry.sourceNameId != 0) { | |
| 186 methodName = sourceMap.names[targetEntry.sourceNameId]; | |
| 187 } | |
| 188 String fileName; | |
| 189 if (targetEntry.sourceUrlId != 0) { | |
| 190 fileName = sourceMap.urls[targetEntry.sourceUrlId]; | |
| 191 } | |
| 192 dartStackTrace.add(new StackTraceLine(methodName, fileName, | |
| 193 targetEntry.sourceLine + 1, targetEntry.sourceColumn + 1)); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 int expectedIndex = 0; | |
| 198 for (StackTraceLine line in dartStackTrace) { | |
| 199 if (expectedIndex < test.expectedLines.length) { | |
| 200 StackTraceLine expectedLine = test.expectedLines[expectedIndex]; | |
| 201 if (line.methodName == expectedLine.methodName && | |
| 202 line.lineNo == expectedLine.lineNo && | |
| 203 line.columnNo == expectedLine.columnNo) { | |
| 204 expectedIndex++; | |
| 205 } | |
| 206 } | |
| 207 } | |
| 208 Expect.equals( | |
| 209 expectedIndex, | |
| 210 test.expectedLines.length, | |
| 211 "Missing stack trace lines for test:\n${test.code}\n" | |
| 212 "Actual:\n${dartStackTrace.join('\n')}\n" | |
| 213 "Expected:\n${test.expectedLines.join('\n')}\n"); | |
| 214 | |
| 215 print("Deleting '${tmpDir.path}'."); | |
| 216 tmpDir.deleteSync(recursive: true); | |
| 217 } | |
| 218 | |
| 219 class StackTraceLine { | |
| 220 String methodName; | |
| 221 String fileName; | |
| 222 int lineNo; | |
| 223 int columnNo; | |
| 224 | |
| 225 StackTraceLine(this.methodName, this.fileName, this.lineNo, this.columnNo); | |
| 226 | |
| 227 factory StackTraceLine.fromText(String text) { | |
|
Siggi Cherem (dart-lang)
2016/10/27 13:25:19
it might help to add a dartdoc with an example sta
Johnni Winther
2016/11/02 14:34:30
Done.
| |
| 228 text = text.trim(); | |
| 229 assert(text.startsWith('at ')); | |
| 230 text = text.substring('at '.length); | |
| 231 String methodName; | |
| 232 if (text.endsWith(')')) { | |
| 233 int nameEnd = text.indexOf(' ('); | |
| 234 methodName = text.substring(0, nameEnd); | |
| 235 text = text.substring(nameEnd + 2, text.length - 1); | |
| 236 } | |
| 237 int lineNo; | |
| 238 int columnNo; | |
| 239 String fileName; | |
| 240 int lastColon = text.lastIndexOf(':'); | |
|
Siggi Cherem (dart-lang)
2016/10/27 13:25:19
idea: consider splitting text by ':' first, then t
Johnni Winther
2016/11/02 14:34:30
Doesn't work on Windows - file names may contain :
Siggi Cherem (dart-lang)
2016/11/02 15:25:24
I see, for this specific case, is it because the d
Johnni Winther
2016/11/03 09:20:18
Alas `t.lastIndexOf(':', i1 - 1);` throws if `i1 -
Johnni Winther
2016/11/03 11:21:10
Should have been: That's why we need to go backwar
| |
| 241 if (lastColon != -1) { | |
| 242 int lastValue = | |
| 243 int.parse(text.substring(lastColon + 1), onError: (_) => null); | |
| 244 if (lastValue != null) { | |
| 245 int secondToLastColon = text.lastIndexOf(':', lastColon - 1); | |
| 246 if (secondToLastColon != -1) { | |
| 247 int secondToLastValue = int.parse( | |
| 248 text.substring(secondToLastColon + 1, lastColon), | |
| 249 onError: (_) => null); | |
| 250 if (secondToLastValue != null) { | |
| 251 lineNo = secondToLastValue; | |
| 252 columnNo = lastValue; | |
| 253 fileName = text.substring(0, secondToLastColon); | |
| 254 } else { | |
| 255 lineNo = lastValue; | |
| 256 fileName = text.substring(0, lastColon); | |
| 257 } | |
| 258 } else { | |
| 259 lineNo = lastValue; | |
| 260 fileName = text.substring(0, lastColon); | |
| 261 } | |
| 262 } else { | |
| 263 fileName = text; | |
| 264 } | |
| 265 } else { | |
| 266 fileName = text; | |
| 267 } | |
| 268 return new StackTraceLine(methodName, fileName, lineNo, columnNo); | |
| 269 } | |
| 270 | |
| 271 String toString() { | |
| 272 StringBuffer sb = new StringBuffer(); | |
| 273 sb.write(' at '); | |
| 274 if (methodName != null) { | |
| 275 sb.write(methodName); | |
| 276 sb.write(' ('); | |
| 277 sb.write(fileName ?? '?'); | |
| 278 sb.write(':'); | |
| 279 sb.write(lineNo); | |
| 280 sb.write(':'); | |
| 281 sb.write(columnNo); | |
| 282 sb.write(')'); | |
| 283 } else { | |
| 284 sb.write(fileName ?? '?'); | |
| 285 sb.write(':'); | |
| 286 sb.write(lineNo); | |
| 287 sb.write(':'); | |
| 288 sb.write(columnNo); | |
| 289 } | |
| 290 return sb.toString(); | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 /// Returns [TargetLineEntry] which includes the location in the target [line] | |
| 295 /// number. In particular, the resulting entry is the last entry whose line | |
| 296 /// number is lower or equal to [line]. | |
| 297 /// | |
| 298 /// Copied from [SingleMapping._findLine]. | |
| 299 TargetLineEntry _findLine(SingleMapping sourceMap, int line) { | |
| 300 int index = binarySearch(sourceMap.lines, (e) => e.line > line); | |
| 301 return (index <= 0) ? null : sourceMap.lines[index - 1]; | |
| 302 } | |
| 303 | |
| 304 /// Returns [TargetEntry] which includes the location denoted by | |
| 305 /// [line], [column]. If [lineEntry] corresponds to [line], then this will be | |
| 306 /// the last entry whose column is lower or equal than [column]. If | |
| 307 /// [lineEntry] corresponds to a line prior to [line], then the result will be | |
| 308 /// the very last entry on that line. | |
| 309 /// | |
| 310 /// Copied from [SingleMapping._findColumn]. | |
| 311 TargetEntry _findColumn(int line, int column, TargetLineEntry lineEntry) { | |
| 312 if (lineEntry == null || lineEntry.entries.length == 0) return null; | |
| 313 if (lineEntry.line != line) return lineEntry.entries.last; | |
| 314 var entries = lineEntry.entries; | |
| 315 int index = binarySearch(entries, (e) => e.column > column); | |
| 316 return (index <= 0) ? null : entries[index - 1]; | |
| 317 } | |
| 318 | |
| 319 /// Returns the path of the d8 executable. | |
| 320 String get d8executable { | |
| 321 if (Platform.isWindows) { | |
| 322 return 'third_party/d8/windows/d8.exe'; | |
| 323 } else if (Platform.isLinux) { | |
| 324 return 'third_party/d8/linux/d8'; | |
| 325 } else if (Platform.isMacOS) { | |
| 326 return 'third_party/d8/macos/d8'; | |
| 327 } | |
| 328 throw new UnsupportedError('Unsupported platform.'); | |
| 329 } | |
| OLD | NEW |