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 |