| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 class WorkItem { | |
| 6 final Element element; | |
| 7 TreeElements resolutionTree; | |
| 8 Function run; | |
| 9 bool allowSpeculativeOptimization = true; | |
| 10 List<HTypeGuard> guards = const <HTypeGuard>[]; | |
| 11 | |
| 12 WorkItem.toCompile(this.element) : resolutionTree = null { | |
| 13 run = this.compile; | |
| 14 } | |
| 15 | |
| 16 WorkItem.toCodegen(this.element, this.resolutionTree) { | |
| 17 run = this.codegen; | |
| 18 } | |
| 19 | |
| 20 bool isAnalyzed() => resolutionTree != null; | |
| 21 | |
| 22 int hashCode() => element.hashCode(); | |
| 23 | |
| 24 String compile(Compiler compiler) { | |
| 25 return compiler.compile(this); | |
| 26 } | |
| 27 | |
| 28 String codegen(Compiler compiler) { | |
| 29 return compiler.codegen(this); | |
| 30 } | |
| 31 } | |
| 32 | |
| 33 class Compiler implements DiagnosticListener { | |
| 34 Queue<WorkItem> worklist; | |
| 35 Universe universe; | |
| 36 String assembledCode; | |
| 37 Namer namer; | |
| 38 Types types; | |
| 39 final String currentDirectory; | |
| 40 | |
| 41 final Tracer tracer; | |
| 42 | |
| 43 CompilerTask measuredTask; | |
| 44 Element _currentElement; | |
| 45 LibraryElement coreLibrary; | |
| 46 LibraryElement coreImplLibrary; | |
| 47 LibraryElement isolateLibrary; | |
| 48 LibraryElement jsHelperLibrary; | |
| 49 LibraryElement mainApp; | |
| 50 ClassElement objectClass; | |
| 51 ClassElement closureClass; | |
| 52 ClassElement dynamicClass; | |
| 53 ClassElement boolClass; | |
| 54 ClassElement numClass; | |
| 55 ClassElement intClass; | |
| 56 ClassElement doubleClass; | |
| 57 ClassElement stringClass; | |
| 58 ClassElement functionClass; | |
| 59 ClassElement nullClass; | |
| 60 ClassElement listClass; | |
| 61 | |
| 62 Element get currentElement() => _currentElement; | |
| 63 withCurrentElement(Element element, f()) { | |
| 64 Element old = currentElement; | |
| 65 _currentElement = element; | |
| 66 try { | |
| 67 return f(); | |
| 68 } finally { | |
| 69 _currentElement = old; | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 List<CompilerTask> tasks; | |
| 74 ScannerTask scanner; | |
| 75 DietParserTask dietParser; | |
| 76 ParserTask parser; | |
| 77 TreeValidatorTask validator; | |
| 78 ResolverTask resolver; | |
| 79 TypeCheckerTask checker; | |
| 80 SsaBuilderTask builder; | |
| 81 SsaOptimizerTask optimizer; | |
| 82 SsaCodeGeneratorTask generator; | |
| 83 CodeEmitterTask emitter; | |
| 84 ConstantHandler constantHandler; | |
| 85 EnqueueTask enqueuer; | |
| 86 | |
| 87 static final SourceString MAIN = const SourceString('main'); | |
| 88 static final SourceString NO_SUCH_METHOD = const SourceString('noSuchMethod'); | |
| 89 static final SourceString NO_SUCH_METHOD_EXCEPTION = | |
| 90 const SourceString('NoSuchMethodException'); | |
| 91 static final SourceString START_ROOT_ISOLATE = | |
| 92 const SourceString('startRootIsolate'); | |
| 93 bool enabledNoSuchMethod = false; | |
| 94 | |
| 95 bool workListIsClosed = false; | |
| 96 | |
| 97 Stopwatch codegenProgress; | |
| 98 | |
| 99 Compiler.withCurrentDirectory(String this.currentDirectory, | |
| 100 [this.tracer = const Tracer()]) | |
| 101 : types = new Types(), | |
| 102 universe = new Universe(), | |
| 103 worklist = new Queue<WorkItem>(), | |
| 104 codegenProgress = new Stopwatch.start() { | |
| 105 namer = new Namer(this); | |
| 106 constantHandler = new ConstantHandler(this); | |
| 107 scanner = new ScannerTask(this); | |
| 108 dietParser = new DietParserTask(this); | |
| 109 parser = new ParserTask(this); | |
| 110 validator = new TreeValidatorTask(this); | |
| 111 resolver = new ResolverTask(this); | |
| 112 checker = new TypeCheckerTask(this); | |
| 113 builder = new SsaBuilderTask(this); | |
| 114 optimizer = new SsaOptimizerTask(this); | |
| 115 generator = new SsaCodeGeneratorTask(this); | |
| 116 emitter = new CodeEmitterTask(this); | |
| 117 enqueuer = new EnqueueTask(this); | |
| 118 tasks = [scanner, dietParser, parser, resolver, checker, | |
| 119 builder, optimizer, generator, | |
| 120 emitter, constantHandler, enqueuer]; | |
| 121 } | |
| 122 | |
| 123 void ensure(bool condition) { | |
| 124 if (!condition) cancel('failed assertion in leg'); | |
| 125 } | |
| 126 | |
| 127 void unimplemented(String methodName, | |
| 128 [Node node, Token token, HInstruction instruction, | |
| 129 Element element]) { | |
| 130 internalError("$methodName not implemented", | |
| 131 node, token, instruction, element); | |
| 132 } | |
| 133 | |
| 134 void internalError(String message, | |
| 135 [Node node, Token token, HInstruction instruction, | |
| 136 Element element]) { | |
| 137 cancel("${red('internal error:')} $message", | |
| 138 node, token, instruction, element); | |
| 139 } | |
| 140 | |
| 141 void internalErrorOnElement(Element element, String message) { | |
| 142 withCurrentElement(element, () { | |
| 143 internalError(message, element: element); | |
| 144 }); | |
| 145 } | |
| 146 | |
| 147 void cancel([String reason, Node node, Token token, | |
| 148 HInstruction instruction, Element element]) { | |
| 149 SourceSpan span = const SourceSpan(null, null, null); | |
| 150 if (node !== null) { | |
| 151 span = spanFromNode(node); | |
| 152 } else if (token !== null) { | |
| 153 span = spanFromTokens(token, token); | |
| 154 } else if (instruction !== null) { | |
| 155 span = spanFromElement(currentElement); | |
| 156 } else if (element !== null) { | |
| 157 span = spanFromElement(element); | |
| 158 } | |
| 159 reportDiagnostic(span, red(reason), true); | |
| 160 throw new CompilerCancelledException(reason); | |
| 161 } | |
| 162 | |
| 163 void log(message) { | |
| 164 reportDiagnostic(null, message, false); | |
| 165 } | |
| 166 | |
| 167 void enqueue(WorkItem work) { | |
| 168 if (workListIsClosed) { | |
| 169 internalErrorOnElement(work.element, "work list is closed"); | |
| 170 } | |
| 171 worklist.add(work); | |
| 172 } | |
| 173 | |
| 174 bool run(Uri uri) { | |
| 175 try { | |
| 176 runCompiler(uri); | |
| 177 } catch (CompilerCancelledException exception) { | |
| 178 log(exception.toString()); | |
| 179 log('compilation failed'); | |
| 180 return false; | |
| 181 } | |
| 182 tracer.close(); | |
| 183 log('compilation succeeded'); | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 void enableNoSuchMethod(Element element) { | |
| 188 if (enabledNoSuchMethod) return; | |
| 189 if (element.enclosingElement == objectClass) return; | |
| 190 enabledNoSuchMethod = true; | |
| 191 enqueuer.registerInvocation(NO_SUCH_METHOD, new Invocation(2)); | |
| 192 } | |
| 193 | |
| 194 void enableIsolateSupport(LibraryElement element) { | |
| 195 isolateLibrary = element; | |
| 196 addToWorkList(element.find(START_ROOT_ISOLATE)); | |
| 197 } | |
| 198 | |
| 199 bool hasIsolateSupport() => isolateLibrary !== null; | |
| 200 | |
| 201 void onLibraryLoaded(LibraryElement library, Uri uri) { | |
| 202 if (uri.toString() == 'dart:isolate') { | |
| 203 enableIsolateSupport(library); | |
| 204 } | |
| 205 if (dynamicClass !== null) { | |
| 206 // When loading the built-in libraries, dynamicClass is null. We | |
| 207 // take advantage of this as core and coreimpl import js_helper | |
| 208 // and see Dynamic this way. | |
| 209 withCurrentElement(dynamicClass, () { | |
| 210 library.define(dynamicClass, this); | |
| 211 }); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 abstract LibraryElement scanBuiltinLibrary(String filename); | |
| 216 | |
| 217 void initializeSpecialClasses() { | |
| 218 objectClass = coreLibrary.find(const SourceString('Object')); | |
| 219 boolClass = coreLibrary.find(const SourceString('bool')); | |
| 220 numClass = coreLibrary.find(const SourceString('num')); | |
| 221 intClass = coreLibrary.find(const SourceString('int')); | |
| 222 doubleClass = coreLibrary.find(const SourceString('double')); | |
| 223 stringClass = coreLibrary.find(const SourceString('String')); | |
| 224 functionClass = coreLibrary.find(const SourceString('Function')); | |
| 225 listClass = coreLibrary.find(const SourceString('List')); | |
| 226 closureClass = jsHelperLibrary.find(const SourceString('Closure')); | |
| 227 dynamicClass = jsHelperLibrary.find(const SourceString('Dynamic')); | |
| 228 nullClass = jsHelperLibrary.find(const SourceString('Null')); | |
| 229 } | |
| 230 | |
| 231 void scanBuiltinLibraries() { | |
| 232 coreImplLibrary = scanBuiltinLibrary('coreimpl.dart'); | |
| 233 jsHelperLibrary = scanBuiltinLibrary('js_helper.dart'); | |
| 234 coreLibrary = scanBuiltinLibrary('core.dart'); | |
| 235 | |
| 236 // Since coreLibrary import the libraries "coreimpl", and | |
| 237 // "js_helper", coreLibrary is null when they are being built. So | |
| 238 // we add the implicit import of coreLibrary now. This can be | |
| 239 // cleaned up when we have proper support for "dart:core" and | |
| 240 // don't need to access it through the field "coreLibrary". | |
| 241 // TODO(ahe): Clean this up as described above. | |
| 242 scanner.importLibrary(coreImplLibrary, coreLibrary, null); | |
| 243 scanner.importLibrary(jsHelperLibrary, coreLibrary, null); | |
| 244 addForeignFunctions(jsHelperLibrary); | |
| 245 | |
| 246 universe.libraries['dart:core'] = coreLibrary; | |
| 247 universe.libraries['dart:coreimpl'] = coreImplLibrary; | |
| 248 | |
| 249 initializeSpecialClasses(); | |
| 250 } | |
| 251 | |
| 252 /** Define the JS helper functions in the given library. */ | |
| 253 void addForeignFunctions(LibraryElement library) { | |
| 254 library.define(new ForeignElement( | |
| 255 const SourceString('JS'), library), this); | |
| 256 library.define(new ForeignElement( | |
| 257 const SourceString('UNINTERCEPTED'), library), this); | |
| 258 library.define(new ForeignElement( | |
| 259 const SourceString('JS_HAS_EQUALS'), library), this); | |
| 260 library.define(new ForeignElement( | |
| 261 const SourceString('JS_CURRENT_ISOLATE'), library), this); | |
| 262 library.define(new ForeignElement( | |
| 263 const SourceString('JS_CALL_IN_ISOLATE'), library), this); | |
| 264 library.define(new ForeignElement( | |
| 265 const SourceString('DART_CLOSURE_TO_JS'), library), this); | |
| 266 } | |
| 267 | |
| 268 void runCompiler(Uri uri) { | |
| 269 scanBuiltinLibraries(); | |
| 270 mainApp = scanner.loadLibrary(uri, null); | |
| 271 final Element mainMethod = mainApp.find(MAIN); | |
| 272 if (mainMethod === null) { | |
| 273 withCurrentElement(mainApp, () => cancel('Could not find $MAIN')); | |
| 274 } else { | |
| 275 withCurrentElement(mainMethod, () { | |
| 276 if (!mainMethod.isFunction()) { | |
| 277 cancel('main is not a function', element: mainMethod); | |
| 278 } | |
| 279 FunctionParameters parameters = mainMethod.computeParameters(this); | |
| 280 if (parameters.parameterCount > 0) { | |
| 281 cancel('main cannot have parameters', element: mainMethod); | |
| 282 } | |
| 283 }); | |
| 284 } | |
| 285 native.processNativeClasses(this, universe.libraries.getValues()); | |
| 286 enqueue(new WorkItem.toCompile(mainMethod)); | |
| 287 codegenProgress.reset(); | |
| 288 while (!worklist.isEmpty()) { | |
| 289 WorkItem work = worklist.removeLast(); | |
| 290 withCurrentElement(work.element, () => (work.run)(this)); | |
| 291 } | |
| 292 workListIsClosed = true; | |
| 293 assert(enqueuer.checkNoEnqueuedInvokedInstanceMethods()); | |
| 294 enqueuer.registerFieldClosureInvocations(); | |
| 295 emitter.assembleProgram(); | |
| 296 if (!worklist.isEmpty()) { | |
| 297 internalErrorOnElement(worklist.first().element, | |
| 298 "work list is not empty"); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 TreeElements analyzeElement(Element element) { | |
| 303 assert(parser !== null); | |
| 304 Node tree = parser.parse(element); | |
| 305 validator.validate(tree); | |
| 306 TreeElements elements = resolver.resolve(element); | |
| 307 checker.check(tree, elements); | |
| 308 return elements; | |
| 309 } | |
| 310 | |
| 311 TreeElements analyze(WorkItem work) { | |
| 312 work.resolutionTree = analyzeElement(work.element); | |
| 313 return work.resolutionTree; | |
| 314 } | |
| 315 | |
| 316 String codegen(WorkItem work) { | |
| 317 if (codegenProgress.elapsedInMs() > 500) { | |
| 318 // TODO(ahe): Add structured diagnostics to the compiler API and | |
| 319 // use it to separate this from the --verbose option. | |
| 320 log('compiled ${universe.generatedCode.length} methods'); | |
| 321 codegenProgress.reset(); | |
| 322 } | |
| 323 if (work.element.kind.category == ElementCategory.VARIABLE) { | |
| 324 constantHandler.compileWorkItem(work); | |
| 325 return null; | |
| 326 } else { | |
| 327 HGraph graph = builder.build(work); | |
| 328 optimizer.optimize(work, graph); | |
| 329 if (work.allowSpeculativeOptimization | |
| 330 && optimizer.trySpeculativeOptimizations(work, graph)) { | |
| 331 String code = generator.generateBailoutMethod(work, graph); | |
| 332 universe.addBailoutCode(work, code); | |
| 333 optimizer.prepareForSpeculativeOptimizations(work, graph); | |
| 334 optimizer.optimize(work, graph); | |
| 335 code = generator.generateMethod(work, graph); | |
| 336 universe.addGeneratedCode(work, code); | |
| 337 return code; | |
| 338 } else { | |
| 339 String code = generator.generateMethod(work, graph); | |
| 340 universe.addGeneratedCode(work, code); | |
| 341 return code; | |
| 342 } | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 String compile(WorkItem work) { | |
| 347 String code = universe.generatedCode[work.element]; | |
| 348 if (code !== null) return code; | |
| 349 analyze(work); | |
| 350 return codegen(work); | |
| 351 } | |
| 352 | |
| 353 void addToWorkList(Element element) { | |
| 354 if (workListIsClosed) { | |
| 355 internalErrorOnElement(element, "work list is closed"); | |
| 356 } | |
| 357 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 358 registerInstantiatedClass(element.enclosingElement); | |
| 359 } | |
| 360 worklist.add(new WorkItem.toCompile(element)); | |
| 361 } | |
| 362 | |
| 363 void registerStaticUse(Element element) { | |
| 364 addToWorkList(element); | |
| 365 } | |
| 366 | |
| 367 void registerGetOfStaticFunction(FunctionElement element) { | |
| 368 registerStaticUse(element); | |
| 369 universe.staticFunctionsNeedingGetter.add(element); | |
| 370 } | |
| 371 | |
| 372 void registerDynamicInvocation(SourceString methodName, Selector selector) { | |
| 373 assert(selector !== null); | |
| 374 enqueuer.registerInvocation(methodName, selector); | |
| 375 } | |
| 376 | |
| 377 void registerDynamicGetter(SourceString methodName) { | |
| 378 enqueuer.registerGetter(methodName); | |
| 379 } | |
| 380 | |
| 381 void registerDynamicSetter(SourceString methodName) { | |
| 382 enqueuer.registerSetter(methodName); | |
| 383 } | |
| 384 | |
| 385 void registerInstantiatedClass(ClassElement element) { | |
| 386 universe.instantiatedClasses.add(element); | |
| 387 enqueuer.onRegisterInstantiatedClass(element); | |
| 388 } | |
| 389 | |
| 390 // TODO(ngeoffray): This should get a type. | |
| 391 void registerIsCheck(Element element) { | |
| 392 universe.isChecks.add(element); | |
| 393 } | |
| 394 | |
| 395 Type resolveType(ClassElement element) { | |
| 396 return withCurrentElement(element, () => resolver.resolveType(element)); | |
| 397 } | |
| 398 | |
| 399 FunctionParameters resolveSignature(FunctionElement element) { | |
| 400 return withCurrentElement(element, | |
| 401 () => resolver.resolveSignature(element)); | |
| 402 } | |
| 403 | |
| 404 Constant compileVariable(VariableElement element) { | |
| 405 return withCurrentElement(element, () { | |
| 406 return constantHandler.compileVariable(element); | |
| 407 }); | |
| 408 } | |
| 409 | |
| 410 reportWarning(Node node, var message) { | |
| 411 if (message is ResolutionWarning) { | |
| 412 // TODO(ahe): Don't supress this warning when we support type variables. | |
| 413 if (message.message.kind === MessageKind.CANNOT_RESOLVE_TYPE) return; | |
| 414 } else if (message is TypeWarning) { | |
| 415 // TODO(ahe): Don't supress these warning when the type checker | |
| 416 // is more complete. | |
| 417 if (message.message.kind === MessageKind.NOT_ASSIGNABLE) return; | |
| 418 if (message.message.kind === MessageKind.MISSING_RETURN) return; | |
| 419 if (message.message.kind === MessageKind.ADDITIONAL_ARGUMENT) return; | |
| 420 if (message.message.kind === MessageKind.METHOD_NOT_FOUND) return; | |
| 421 } | |
| 422 SourceSpan span = spanFromNode(node); | |
| 423 reportDiagnostic(span, "${magenta('warning:')} $message", false); | |
| 424 } | |
| 425 | |
| 426 reportError(Node node, var message) { | |
| 427 SourceSpan span = spanFromNode(node); | |
| 428 reportDiagnostic(span, "${red('error:')} $message", true); | |
| 429 throw new CompilerCancelledException(message.toString()); | |
| 430 } | |
| 431 | |
| 432 abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); | |
| 433 | |
| 434 SourceSpan spanFromTokens(Token begin, Token end) { | |
| 435 if (begin === null || end === null) { | |
| 436 // TODO(ahe): We can almost always do better. Often it is only | |
| 437 // end that is null. Otherwise, we probably know the current | |
| 438 // URI. | |
| 439 throw 'cannot find tokens to produce error message'; | |
| 440 } | |
| 441 final startOffset = begin.charOffset; | |
| 442 // TODO(ahe): Compute proper end offset in token. Right now we use | |
| 443 // the position of the next token. We want to preserve the | |
| 444 // invariant that endOffset > startOffset, but for EOF the | |
| 445 // charoffset of the next token may be [startOffset]. This can | |
| 446 // also happen for synthetized tokens that are produced during | |
| 447 // error handling. | |
| 448 final endOffset = | |
| 449 Math.max((end.next !== null) ? end.next.charOffset : 0, startOffset + 1); | |
| 450 assert(endOffset > startOffset); | |
| 451 Uri uri = currentElement.getCompilationUnit().script.uri; | |
| 452 return new SourceSpan(uri, startOffset, endOffset); | |
| 453 } | |
| 454 | |
| 455 SourceSpan spanFromNode(Node node) { | |
| 456 return spanFromTokens(node.getBeginToken(), node.getEndToken()); | |
| 457 } | |
| 458 | |
| 459 SourceSpan spanFromElement(Element element) { | |
| 460 if (element.position() === null) { | |
| 461 // Sometimes, the backend fakes up elements that have no | |
| 462 // position. So we use the enclosing element instead. It is | |
| 463 // not a good error location, but cancel really is "internal | |
| 464 // error" or "not implemented yet", so the vicinity is good | |
| 465 // enough for now. | |
| 466 element = element.enclosingElement; | |
| 467 // TODO(ahe): I plan to overhaul this infrastructure anyways. | |
| 468 } | |
| 469 if (element === null) { | |
| 470 element = currentElement; | |
| 471 } | |
| 472 Token position = element.position(); | |
| 473 if (position === null) { | |
| 474 // TODO(ahe): Find the enclosing library. | |
| 475 return const SourceSpan(null, null, null); | |
| 476 } | |
| 477 return spanFromTokens(position, position); | |
| 478 } | |
| 479 | |
| 480 Script readScript(Uri uri, [ScriptTag node]) { | |
| 481 unimplemented('Compiler.readScript'); | |
| 482 } | |
| 483 | |
| 484 String get legDirectory() { | |
| 485 unimplemented('Compiler.legDirectory'); | |
| 486 } | |
| 487 | |
| 488 Element findHelper(SourceString name) => jsHelperLibrary.find(name); | |
| 489 | |
| 490 bool get isMockCompilation() => false; | |
| 491 } | |
| 492 | |
| 493 class CompilerTask { | |
| 494 final Compiler compiler; | |
| 495 final Stopwatch watch; | |
| 496 | |
| 497 CompilerTask(this.compiler) : watch = new Stopwatch(); | |
| 498 | |
| 499 String get name() => 'Unknown task'; | |
| 500 int get timing() => watch.elapsedInMs(); | |
| 501 | |
| 502 measure(Function action) { | |
| 503 // TODO(kasperl): Do we have to worry about exceptions here? | |
| 504 CompilerTask previous = compiler.measuredTask; | |
| 505 compiler.measuredTask = this; | |
| 506 if (previous !== null) previous.watch.stop(); | |
| 507 watch.start(); | |
| 508 var result = action(); | |
| 509 watch.stop(); | |
| 510 if (previous !== null) previous.watch.start(); | |
| 511 compiler.measuredTask = previous; | |
| 512 return result; | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 class CompilerCancelledException implements Exception { | |
| 517 final String reason; | |
| 518 CompilerCancelledException(this.reason); | |
| 519 | |
| 520 String toString() { | |
| 521 String banner = 'compiler cancelled'; | |
| 522 return (reason !== null) ? '$banner: $reason' : '$banner'; | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 interface Tracer default LTracer { | |
| 527 const Tracer(); | |
| 528 final bool enabled; | |
| 529 void traceCompilation(String methodName); | |
| 530 void traceGraph(String name, var graph); | |
| 531 void close(); | |
| 532 } | |
| 533 | |
| 534 // TODO(ahe): Remove when the VM supports implicit interfaces. | |
| 535 class LTracer implements Tracer { | |
| 536 const LTracer(); | |
| 537 final bool enabled = false; | |
| 538 void traceCompilation(String methodName) { | |
| 539 } | |
| 540 void traceGraph(String name, var graph) { | |
| 541 } | |
| 542 void close() { | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 class SourceSpan { | |
| 547 final Uri uri; | |
| 548 final int begin; | |
| 549 final int end; | |
| 550 | |
| 551 const SourceSpan(this.uri, this.begin, this.end); | |
| 552 } | |
| OLD | NEW |