Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 | |
| 6 /** | |
| 7 * If true, print a warning for each method that was resolved, but not | |
| 8 * compiled. | |
| 9 */ | |
| 10 final bool REPORT_EXCESS_RESOLUTION = false; | |
| 11 | |
| 5 class WorkItem { | 12 class WorkItem { |
| 6 final Element element; | 13 final Element element; |
| 7 TreeElements resolutionTree; | 14 TreeElements resolutionTree; |
| 8 bool allowSpeculativeOptimization = true; | 15 bool allowSpeculativeOptimization = true; |
| 9 List<HTypeGuard> guards = const <HTypeGuard>[]; | 16 List<HTypeGuard> guards = const <HTypeGuard>[]; |
| 10 | 17 |
| 11 WorkItem(this.element, this.resolutionTree); | 18 WorkItem(this.element, this.resolutionTree); |
| 12 | 19 |
| 13 bool isAnalyzed() => resolutionTree !== null; | 20 bool isAnalyzed() => resolutionTree !== null; |
| 14 | 21 |
| 15 String run(Compiler compiler, Enqueuer world) { | 22 String run(Compiler compiler, Enqueuer world) { |
| 16 String code = world.universe.generatedCode[element]; | 23 String code = world.universe.generatedCode[element]; |
| 17 if (code !== null) return code; | 24 if (code !== null) return code; |
| 18 if (!isAnalyzed()) compiler.analyze(this); | 25 resolutionTree = compiler.analyze(this, world); |
| 19 return compiler.codegen(this); | 26 return compiler.codegen(this, world); |
| 20 } | 27 } |
| 21 } | 28 } |
| 22 | 29 |
| 23 class Backend { | 30 class Backend { |
| 24 final Compiler compiler; | 31 final Compiler compiler; |
| 25 | 32 |
| 26 Backend(this.compiler); | 33 Backend(this.compiler); |
| 27 | 34 |
| 35 void enqueueAllTopLevelFunctions(LibraryElement lib, Enqueuer world) { | |
| 36 lib.forEachExport((Element e) { | |
| 37 if (e.isFunction()) world.addToWorkList(e); | |
| 38 }); | |
| 39 } | |
| 40 | |
| 41 abstract void enqueueHelpers(Enqueuer world); | |
| 28 abstract String codegen(WorkItem work); | 42 abstract String codegen(WorkItem work); |
| 29 abstract void processNativeClasses(world, libraries); | 43 abstract void processNativeClasses(world, libraries); |
| 30 abstract void assembleProgram(); | 44 abstract void assembleProgram(); |
| 31 abstract List<CompilerTask> get tasks(); | 45 abstract List<CompilerTask> get tasks(); |
| 32 } | 46 } |
| 33 | 47 |
| 34 class JavaScriptBackend extends Backend { | 48 class JavaScriptBackend extends Backend { |
| 35 SsaBuilderTask builder; | 49 SsaBuilderTask builder; |
| 36 SsaOptimizerTask optimizer; | 50 SsaOptimizerTask optimizer; |
| 37 SsaCodeGeneratorTask generator; | 51 SsaCodeGeneratorTask generator; |
| 38 CodeEmitterTask emitter; | 52 CodeEmitterTask emitter; |
| 39 | 53 |
| 40 List<CompilerTask> get tasks() { | 54 List<CompilerTask> get tasks() { |
| 41 return <CompilerTask>[builder, optimizer, generator, emitter]; | 55 return <CompilerTask>[builder, optimizer, generator, emitter]; |
| 42 } | 56 } |
| 43 | 57 |
| 44 JavaScriptBackend(Compiler compiler) | 58 JavaScriptBackend(Compiler compiler) |
| 45 : emitter = new CodeEmitterTask(compiler), | 59 : emitter = new CodeEmitterTask(compiler), |
| 46 super(compiler) { | 60 super(compiler) { |
| 47 builder = new SsaBuilderTask(this); | 61 builder = new SsaBuilderTask(this); |
| 48 optimizer = new SsaOptimizerTask(this); | 62 optimizer = new SsaOptimizerTask(this); |
| 49 generator = new SsaCodeGeneratorTask(this); | 63 generator = new SsaCodeGeneratorTask(this); |
| 50 } | 64 } |
| 51 | 65 |
| 66 void enqueueHelpers(Enqueuer world) { | |
| 67 enqueueAllTopLevelFunctions(compiler.jsHelperLibrary, world); | |
| 68 enqueueAllTopLevelFunctions(compiler.interceptorsLibrary, world); | |
| 69 for (var helper in [const SourceString('Closure'), | |
| 70 const SourceString('ConstantMap'), | |
| 71 const SourceString('ConstantProtoMap')]) { | |
| 72 var e = compiler.findHelper(helper); | |
| 73 if (e !== null) world.registerInstantiatedClass(e); | |
| 74 } | |
| 75 } | |
| 76 | |
| 52 String codegen(WorkItem work) { | 77 String codegen(WorkItem work) { |
| 53 HGraph graph = builder.build(work); | 78 HGraph graph = builder.build(work); |
| 54 optimizer.optimize(work, graph); | 79 optimizer.optimize(work, graph); |
| 55 if (work.allowSpeculativeOptimization | 80 if (work.allowSpeculativeOptimization |
| 56 && optimizer.trySpeculativeOptimizations(work, graph)) { | 81 && optimizer.trySpeculativeOptimizations(work, graph)) { |
| 57 String code = generator.generateBailoutMethod(work, graph); | 82 String code = generator.generateBailoutMethod(work, graph); |
| 58 compiler.codegenWorld.addBailoutCode(work, code); | 83 compiler.codegenWorld.addBailoutCode(work, code); |
| 59 optimizer.prepareForSpeculativeOptimizations(work, graph); | 84 optimizer.prepareForSpeculativeOptimizations(work, graph); |
| 60 optimizer.optimize(work, graph); | 85 optimizer.optimize(work, graph); |
| 61 } | 86 } |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 198 reportDiagnostic(spanFromElement(element), | 223 reportDiagnostic(spanFromElement(element), |
| 199 MessageKind.COMPILER_CRASHED.error().toString(), | 224 MessageKind.COMPILER_CRASHED.error().toString(), |
| 200 false); | 225 false); |
| 201 // TODO(ahe): Obtain the build ID. | 226 // TODO(ahe): Obtain the build ID. |
| 202 var buildId = 'build number could not be determined'; | 227 var buildId = 'build number could not be determined'; |
| 203 print(MessageKind.PLEASE_REPORT_THE_CRASH.message([buildId])); | 228 print(MessageKind.PLEASE_REPORT_THE_CRASH.message([buildId])); |
| 204 } | 229 } |
| 205 | 230 |
| 206 void cancel([String reason, Node node, Token token, | 231 void cancel([String reason, Node node, Token token, |
| 207 HInstruction instruction, Element element]) { | 232 HInstruction instruction, Element element]) { |
| 233 assembledCode = null; // Compilation failed. Make sure that we | |
| 234 // don't return a bogus result. | |
| 208 SourceSpan span = const SourceSpan(null, null, null); | 235 SourceSpan span = const SourceSpan(null, null, null); |
| 209 if (node !== null) { | 236 if (node !== null) { |
| 210 span = spanFromNode(node); | 237 span = spanFromNode(node); |
| 211 } else if (token !== null) { | 238 } else if (token !== null) { |
| 212 span = spanFromTokens(token, token); | 239 span = spanFromTokens(token, token); |
| 213 } else if (instruction !== null) { | 240 } else if (instruction !== null) { |
| 214 span = spanFromElement(currentElement); | 241 span = spanFromElement(currentElement); |
| 215 } else if (element !== null) { | 242 } else if (element !== null) { |
| 216 span = spanFromElement(element); | 243 span = spanFromElement(element); |
| 217 } else { | 244 } else { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 241 return false; | 268 return false; |
| 242 } | 269 } |
| 243 tracer.close(); | 270 tracer.close(); |
| 244 log('compilation succeeded'); | 271 log('compilation succeeded'); |
| 245 return true; | 272 return true; |
| 246 } | 273 } |
| 247 | 274 |
| 248 void enableNoSuchMethod(Element element) { | 275 void enableNoSuchMethod(Element element) { |
| 249 // TODO(ahe): Move this method to Enqueuer. | 276 // TODO(ahe): Move this method to Enqueuer. |
| 250 if (enabledNoSuchMethod) return; | 277 if (enabledNoSuchMethod) return; |
| 251 if (element.enclosingElement == objectClass) return; | 278 if (element.enclosingElement === objectClass) { |
| 279 enqueuer.resolution.registerDynamicInvocationOf(element); | |
| 280 return; | |
| 281 } | |
| 252 enabledNoSuchMethod = true; | 282 enabledNoSuchMethod = true; |
| 283 enqueuer.resolution.registerInvocation(NO_SUCH_METHOD, | |
| 284 Selector.INVOCATION_2); | |
| 253 enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, | 285 enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, |
| 254 new Selector.invocation(2)); | 286 Selector.INVOCATION_2); |
| 255 } | 287 } |
| 256 | 288 |
| 257 void enableIsolateSupport(LibraryElement element) { | 289 void enableIsolateSupport(LibraryElement element) { |
| 258 // TODO(ahe): Move this method to Enqueuer. | 290 // TODO(ahe): Move this method to Enqueuer. |
| 259 isolateLibrary = element; | 291 isolateLibrary = element; |
| 292 enqueuer.resolution.addToWorkList(element.find(START_ROOT_ISOLATE)); | |
| 293 enqueuer.resolution.addToWorkList( | |
| 294 element.find(const SourceString('_currentIsolate'))); | |
| 295 enqueuer.resolution.addToWorkList( | |
| 296 element.find(const SourceString('_callInIsolate'))); | |
| 260 enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); | 297 enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); |
| 261 } | 298 } |
| 262 | 299 |
| 263 bool hasIsolateSupport() => isolateLibrary !== null; | 300 bool hasIsolateSupport() => isolateLibrary !== null; |
| 264 | 301 |
| 265 void onLibraryLoaded(LibraryElement library, Uri uri) { | 302 void onLibraryLoaded(LibraryElement library, Uri uri) { |
| 266 if (uri.toString() == 'dart:isolate') { | 303 if (uri.toString() == 'dart:isolate') { |
| 267 enableIsolateSupport(library); | 304 enableIsolateSupport(library); |
| 268 } | 305 } |
| 269 if (dynamicClass !== null) { | 306 if (dynamicClass !== null) { |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 359 FunctionSignature parameters = mainMethod.computeSignature(this); | 396 FunctionSignature parameters = mainMethod.computeSignature(this); |
| 360 parameters.forEachParameter((Element parameter) { | 397 parameters.forEachParameter((Element parameter) { |
| 361 reportFatalError('main cannot have parameters', parameter); | 398 reportFatalError('main cannot have parameters', parameter); |
| 362 }); | 399 }); |
| 363 } | 400 } |
| 364 | 401 |
| 365 // TODO(ahe): Remove this line. Eventually, enqueuer.resolution | 402 // TODO(ahe): Remove this line. Eventually, enqueuer.resolution |
| 366 // should know this. | 403 // should know this. |
| 367 world.populate(this, libraries.getValues()); | 404 world.populate(this, libraries.getValues()); |
| 368 | 405 |
| 369 // Not yet ready to process the enqueuer.resolution queue... | 406 log('Resolving...'); |
| 370 // processQueue(enqueuer.resolution); | 407 backend.enqueueHelpers(enqueuer.resolution); |
| 408 processQueue(enqueuer.resolution, main); | |
| 409 log('Resolved ${enqueuer.resolution.resolvedElements.length} elements.'); | |
| 410 | |
| 411 log('Compiling...'); | |
| 371 processQueue(enqueuer.codegen, main); | 412 processQueue(enqueuer.codegen, main); |
| 413 log('Compiled ${codegenWorld.generatedCode.length} methods.'); | |
| 372 | 414 |
| 373 backend.assembleProgram(); | 415 backend.assembleProgram(); |
| 374 if (!enqueuer.codegen.queue.isEmpty()) { | 416 |
| 375 internalErrorOnElement(enqueuer.codegen.queue.first().element, | 417 checkQueues(); |
| 376 "work list is not empty"); | |
| 377 } | |
| 378 } | 418 } |
| 379 | 419 |
| 380 processQueue(Enqueuer world, Element main) { | 420 processQueue(Enqueuer world, Element main) { |
| 381 backend.processNativeClasses(world, libraries.getValues()); | 421 backend.processNativeClasses(world, libraries.getValues()); |
| 382 world.addToWorkList(main); | 422 world.addToWorkList(main); |
| 383 codegenProgress.reset(); | 423 codegenProgress.reset(); |
| 384 while (!world.queue.isEmpty()) { | 424 world.forEach((WorkItem work) { |
| 385 WorkItem work = world.queue.removeLast(); | |
| 386 withCurrentElement(work.element, () => work.run(this, world)); | 425 withCurrentElement(work.element, () => work.run(this, world)); |
| 387 } | 426 }); |
| 388 world.queueIsClosed = true; | 427 world.queueIsClosed = true; |
| 389 assert(world.checkNoEnqueuedInvokedInstanceMethods()); | 428 assert(world.checkNoEnqueuedInvokedInstanceMethods()); |
| 390 world.registerFieldClosureInvocations(); | 429 world.registerFieldClosureInvocations(); |
| 391 } | 430 } |
| 392 | 431 |
| 432 /** | |
| 433 * Perform various checks of the queues. This includes checking that | |
| 434 * the queues are empty (nothing was added after we stopped | |
| 435 * processing the quese). Also compute the number of methods that | |
|
ngeoffray
2012/06/12 12:44:14
quese -> queues
ahe
2012/06/12 18:47:13
Done.
| |
| 436 * were resolved, but not compiled (aka excess resolution). | |
| 437 */ | |
| 438 checkQueues() { | |
| 439 for (var world in [enqueuer.resolution, enqueuer.codegen]) { | |
| 440 world.forEach((WorkItem work) { | |
| 441 internalErrorOnElement(work.element, "Work list is not empty."); | |
| 442 }); | |
| 443 } | |
| 444 var resolved = new Set.from(enqueuer.resolution.resolvedElements.getKeys()); | |
| 445 for (Element e in codegenWorld.generatedCode.getKeys()) { | |
| 446 resolved.remove(e); | |
| 447 } | |
| 448 for (Element e in new Set.from(resolved)) { | |
| 449 if (e.isClass() || | |
| 450 e.isField() || | |
| 451 e.isTypeVariable() || | |
| 452 e.isTypedef() || | |
| 453 e.kind === ElementKind.ABSTRACT_FIELD) { | |
| 454 resolved.remove(e); | |
| 455 } | |
| 456 if (e.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 457 if (e.enclosingElement.isInterface()) { | |
| 458 resolved.remove(e); | |
| 459 } | |
| 460 resolved.remove(e); | |
| 461 | |
| 462 } | |
| 463 if (e.getLibrary() === jsHelperLibrary) { | |
| 464 resolved.remove(e); | |
| 465 } | |
| 466 if (e.getLibrary() === interceptorsLibrary) { | |
| 467 resolved.remove(e); | |
| 468 } | |
| 469 } | |
| 470 log('Excess resolution work: ${resolved.length}.'); | |
| 471 if (!REPORT_EXCESS_RESOLUTION) return; | |
| 472 for (Element e in resolved) { | |
| 473 SourceSpan span = spanFromElement(e); | |
| 474 reportDiagnostic(span, 'Warning: $e resolved but not compiled.', false); | |
| 475 } | |
| 476 } | |
| 477 | |
| 393 TreeElements analyzeElement(Element element) { | 478 TreeElements analyzeElement(Element element) { |
| 479 TreeElements elements = enqueuer.resolution.getCachedElements(element); | |
| 480 if (elements !== null) return elements; | |
| 481 if (element is AbstractFieldElement) { | |
|
ngeoffray
2012/06/12 12:44:14
Maybe add this check together with the allowed che
ahe
2012/06/12 18:47:13
Done.
| |
| 482 return null; | |
| 483 } | |
| 484 final int allowed = ElementCategory.VARIABLE | ElementCategory.FUNCTION | |
| 485 | ElementCategory.FACTORY; | |
| 486 if (!element.isAccessor() && (element.kind.category & allowed) == 0) { | |
| 487 return null; | |
| 488 } | |
| 394 assert(parser !== null); | 489 assert(parser !== null); |
| 395 Node tree = parser.parse(element); | 490 Node tree = parser.parse(element); |
| 396 validator.validate(tree); | 491 validator.validate(tree); |
| 397 unparseValidator.check(element); | 492 unparseValidator.check(element); |
| 398 TreeElements elements = resolver.resolve(element); | 493 elements = resolver.resolve(element); |
| 399 checker.check(tree, elements); | 494 checker.check(tree, elements); |
| 400 return elements; | 495 return elements; |
| 401 } | 496 } |
| 402 | 497 |
| 403 TreeElements analyze(WorkItem work) { | 498 TreeElements analyze(WorkItem work, Enqueuer world) { |
| 404 work.resolutionTree = analyzeElement(work.element); | 499 if (work.isAnalyzed()) return work.resolutionTree; |
| 405 return work.resolutionTree; | 500 Element element = work.element; |
| 501 TreeElements result = world.getCachedElements(element); | |
| 502 if (result !== null) return result; | |
| 503 if (world !== enqueuer.resolution) { | |
| 504 internalErrorOnElement(element, | |
| 505 'Internal error: unresolved element: $element.'); | |
| 506 } | |
| 507 result = analyzeElement(element); | |
| 508 enqueuer.resolution.resolvedElements[element] = result; | |
| 509 return result; | |
| 406 } | 510 } |
| 407 | 511 |
| 408 String codegen(WorkItem work) { | 512 String codegen(WorkItem work, Enqueuer world) { |
| 513 if (world !== enqueuer.codegen) return null; | |
|
ngeoffray
2012/06/12 12:44:14
Can we actually prevent this situation? Or is that
ahe
2012/06/12 18:47:13
I'm not sure I understand your question.
ngeoffray
2012/06/13 07:31:57
It does not feel right that the resolver queue end
ahe
2012/06/13 08:20:41
I have some ideas for cleaning this up. If enqueue
| |
| 409 if (codegenProgress.elapsedInMs() > 500) { | 514 if (codegenProgress.elapsedInMs() > 500) { |
| 410 // TODO(ahe): Add structured diagnostics to the compiler API and | 515 // TODO(ahe): Add structured diagnostics to the compiler API and |
| 411 // use it to separate this from the --verbose option. | 516 // use it to separate this from the --verbose option. |
| 412 log('compiled ${codegenWorld.generatedCode.length} methods'); | 517 log('Compiled ${codegenWorld.generatedCode.length} methods.'); |
| 413 codegenProgress.reset(); | 518 codegenProgress.reset(); |
| 414 } | 519 } |
| 415 if (work.element.kind.category == ElementCategory.VARIABLE) { | 520 if (work.element.kind.category == ElementCategory.VARIABLE) { |
| 416 constantHandler.compileWorkItem(work); | 521 constantHandler.compileWorkItem(work); |
| 417 return null; | 522 return null; |
| 418 } else { | 523 } else { |
| 419 String code = backend.codegen(work); | 524 String code = backend.codegen(work); |
| 420 codegenWorld.addGeneratedCode(work, code); | 525 codegenWorld.addGeneratedCode(work, code); |
| 421 return code; | 526 return code; |
| 422 } | 527 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 472 } | 577 } |
| 473 | 578 |
| 474 reportError(Node node, var message) { | 579 reportError(Node node, var message) { |
| 475 SourceSpan span = spanFromNode(node); | 580 SourceSpan span = spanFromNode(node); |
| 476 reportDiagnostic(span, "${red('error:')} $message", true); | 581 reportDiagnostic(span, "${red('error:')} $message", true); |
| 477 throw new CompilerCancelledException(message.toString()); | 582 throw new CompilerCancelledException(message.toString()); |
| 478 } | 583 } |
| 479 | 584 |
| 480 abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); | 585 abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); |
| 481 | 586 |
| 482 SourceSpan spanFromTokens(Token begin, Token end) { | 587 SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { |
| 483 if (begin === null || end === null) { | 588 if (begin === null || end === null) { |
| 484 // TODO(ahe): We can almost always do better. Often it is only | 589 // TODO(ahe): We can almost always do better. Often it is only |
| 485 // end that is null. Otherwise, we probably know the current | 590 // end that is null. Otherwise, we probably know the current |
| 486 // URI. | 591 // URI. |
| 487 throw 'cannot find tokens to produce error message'; | 592 throw 'Cannot find tokens to produce error message.'; |
| 488 } | 593 } |
| 489 Uri uri = currentElement.getCompilationUnit().script.uri; | 594 if (uri === null) { |
| 595 uri = currentElement.getCompilationUnit().script.uri; | |
| 596 } | |
| 490 return SourceSpan.withOffsets(begin, end, (beginOffset, endOffset) => | 597 return SourceSpan.withOffsets(begin, end, (beginOffset, endOffset) => |
| 491 new SourceSpan(uri, beginOffset, endOffset)); | 598 new SourceSpan(uri, beginOffset, endOffset)); |
| 492 } | 599 } |
| 493 | 600 |
| 494 SourceSpan spanFromNode(Node node) { | 601 SourceSpan spanFromNode(Node node) { |
| 495 return spanFromTokens(node.getBeginToken(), node.getEndToken()); | 602 return spanFromTokens(node.getBeginToken(), node.getEndToken()); |
| 496 } | 603 } |
| 497 | 604 |
| 498 SourceSpan spanFromElement(Element element) { | 605 SourceSpan spanFromElement(Element element) { |
| 499 if (element.position() === null) { | 606 if (element.position() === null) { |
| 500 // Sometimes, the backend fakes up elements that have no | 607 // Sometimes, the backend fakes up elements that have no |
| 501 // position. So we use the enclosing element instead. It is | 608 // position. So we use the enclosing element instead. It is |
| 502 // not a good error location, but cancel really is "internal | 609 // not a good error location, but cancel really is "internal |
| 503 // error" or "not implemented yet", so the vicinity is good | 610 // error" or "not implemented yet", so the vicinity is good |
| 504 // enough for now. | 611 // enough for now. |
| 505 element = element.enclosingElement; | 612 element = element.enclosingElement; |
| 506 // TODO(ahe): I plan to overhaul this infrastructure anyways. | 613 // TODO(ahe): I plan to overhaul this infrastructure anyways. |
| 507 } | 614 } |
| 508 if (element === null) { | 615 if (element === null) { |
| 509 element = currentElement; | 616 element = currentElement; |
| 510 } | 617 } |
| 511 Token position = element.position(); | 618 Token position = element.position(); |
| 512 if (position === null) { | 619 Uri uri = element.getCompilationUnit().script.uri; |
| 513 Uri uri = element.getCompilationUnit().script.uri; | 620 return (position === null) |
| 514 return new SourceSpan(uri, 0, 0); | 621 ? new SourceSpan(uri, 0, 0) |
| 515 } | 622 : spanFromTokens(position, position, uri); |
| 516 return spanFromTokens(position, position); | |
| 517 } | 623 } |
| 518 | 624 |
| 519 Script readScript(Uri uri, [ScriptTag node]) { | 625 Script readScript(Uri uri, [ScriptTag node]) { |
| 520 unimplemented('Compiler.readScript'); | 626 unimplemented('Compiler.readScript'); |
| 521 } | 627 } |
| 522 | 628 |
| 523 String get legDirectory() { | 629 String get legDirectory() { |
| 524 unimplemented('Compiler.legDirectory'); | 630 unimplemented('Compiler.legDirectory'); |
| 525 } | 631 } |
| 526 | 632 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 595 // invariant that endOffset > beginOffset, but for EOF the | 701 // invariant that endOffset > beginOffset, but for EOF the |
| 596 // charoffset of the next token may be [beginOffset]. This can | 702 // charoffset of the next token may be [beginOffset]. This can |
| 597 // also happen for synthetized tokens that are produced during | 703 // also happen for synthetized tokens that are produced during |
| 598 // error handling. | 704 // error handling. |
| 599 final endOffset = | 705 final endOffset = |
| 600 Math.max((end.next !== null) ? end.next.charOffset : 0, beginOffset + 1); | 706 Math.max((end.next !== null) ? end.next.charOffset : 0, beginOffset + 1); |
| 601 assert(endOffset > beginOffset); | 707 assert(endOffset > beginOffset); |
| 602 return f(beginOffset, endOffset); | 708 return f(beginOffset, endOffset); |
| 603 } | 709 } |
| 604 } | 710 } |
| OLD | NEW |