Index: frog/leg/compiler.dart |
=================================================================== |
--- frog/leg/compiler.dart (revision 5925) |
+++ frog/leg/compiler.dart (working copy) |
@@ -1,552 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-class WorkItem { |
- final Element element; |
- TreeElements resolutionTree; |
- Function run; |
- bool allowSpeculativeOptimization = true; |
- List<HTypeGuard> guards = const <HTypeGuard>[]; |
- |
- WorkItem.toCompile(this.element) : resolutionTree = null { |
- run = this.compile; |
- } |
- |
- WorkItem.toCodegen(this.element, this.resolutionTree) { |
- run = this.codegen; |
- } |
- |
- bool isAnalyzed() => resolutionTree != null; |
- |
- int hashCode() => element.hashCode(); |
- |
- String compile(Compiler compiler) { |
- return compiler.compile(this); |
- } |
- |
- String codegen(Compiler compiler) { |
- return compiler.codegen(this); |
- } |
-} |
- |
-class Compiler implements DiagnosticListener { |
- Queue<WorkItem> worklist; |
- Universe universe; |
- String assembledCode; |
- Namer namer; |
- Types types; |
- final String currentDirectory; |
- |
- final Tracer tracer; |
- |
- CompilerTask measuredTask; |
- Element _currentElement; |
- LibraryElement coreLibrary; |
- LibraryElement coreImplLibrary; |
- LibraryElement isolateLibrary; |
- LibraryElement jsHelperLibrary; |
- LibraryElement mainApp; |
- ClassElement objectClass; |
- ClassElement closureClass; |
- ClassElement dynamicClass; |
- ClassElement boolClass; |
- ClassElement numClass; |
- ClassElement intClass; |
- ClassElement doubleClass; |
- ClassElement stringClass; |
- ClassElement functionClass; |
- ClassElement nullClass; |
- ClassElement listClass; |
- |
- Element get currentElement() => _currentElement; |
- withCurrentElement(Element element, f()) { |
- Element old = currentElement; |
- _currentElement = element; |
- try { |
- return f(); |
- } finally { |
- _currentElement = old; |
- } |
- } |
- |
- List<CompilerTask> tasks; |
- ScannerTask scanner; |
- DietParserTask dietParser; |
- ParserTask parser; |
- TreeValidatorTask validator; |
- ResolverTask resolver; |
- TypeCheckerTask checker; |
- SsaBuilderTask builder; |
- SsaOptimizerTask optimizer; |
- SsaCodeGeneratorTask generator; |
- CodeEmitterTask emitter; |
- ConstantHandler constantHandler; |
- EnqueueTask enqueuer; |
- |
- static final SourceString MAIN = const SourceString('main'); |
- static final SourceString NO_SUCH_METHOD = const SourceString('noSuchMethod'); |
- static final SourceString NO_SUCH_METHOD_EXCEPTION = |
- const SourceString('NoSuchMethodException'); |
- static final SourceString START_ROOT_ISOLATE = |
- const SourceString('startRootIsolate'); |
- bool enabledNoSuchMethod = false; |
- |
- bool workListIsClosed = false; |
- |
- Stopwatch codegenProgress; |
- |
- Compiler.withCurrentDirectory(String this.currentDirectory, |
- [this.tracer = const Tracer()]) |
- : types = new Types(), |
- universe = new Universe(), |
- worklist = new Queue<WorkItem>(), |
- codegenProgress = new Stopwatch.start() { |
- namer = new Namer(this); |
- constantHandler = new ConstantHandler(this); |
- scanner = new ScannerTask(this); |
- dietParser = new DietParserTask(this); |
- parser = new ParserTask(this); |
- validator = new TreeValidatorTask(this); |
- resolver = new ResolverTask(this); |
- checker = new TypeCheckerTask(this); |
- builder = new SsaBuilderTask(this); |
- optimizer = new SsaOptimizerTask(this); |
- generator = new SsaCodeGeneratorTask(this); |
- emitter = new CodeEmitterTask(this); |
- enqueuer = new EnqueueTask(this); |
- tasks = [scanner, dietParser, parser, resolver, checker, |
- builder, optimizer, generator, |
- emitter, constantHandler, enqueuer]; |
- } |
- |
- void ensure(bool condition) { |
- if (!condition) cancel('failed assertion in leg'); |
- } |
- |
- void unimplemented(String methodName, |
- [Node node, Token token, HInstruction instruction, |
- Element element]) { |
- internalError("$methodName not implemented", |
- node, token, instruction, element); |
- } |
- |
- void internalError(String message, |
- [Node node, Token token, HInstruction instruction, |
- Element element]) { |
- cancel("${red('internal error:')} $message", |
- node, token, instruction, element); |
- } |
- |
- void internalErrorOnElement(Element element, String message) { |
- withCurrentElement(element, () { |
- internalError(message, element: element); |
- }); |
- } |
- |
- void cancel([String reason, Node node, Token token, |
- HInstruction instruction, Element element]) { |
- SourceSpan span = const SourceSpan(null, null, null); |
- if (node !== null) { |
- span = spanFromNode(node); |
- } else if (token !== null) { |
- span = spanFromTokens(token, token); |
- } else if (instruction !== null) { |
- span = spanFromElement(currentElement); |
- } else if (element !== null) { |
- span = spanFromElement(element); |
- } |
- reportDiagnostic(span, red(reason), true); |
- throw new CompilerCancelledException(reason); |
- } |
- |
- void log(message) { |
- reportDiagnostic(null, message, false); |
- } |
- |
- void enqueue(WorkItem work) { |
- if (workListIsClosed) { |
- internalErrorOnElement(work.element, "work list is closed"); |
- } |
- worklist.add(work); |
- } |
- |
- bool run(Uri uri) { |
- try { |
- runCompiler(uri); |
- } catch (CompilerCancelledException exception) { |
- log(exception.toString()); |
- log('compilation failed'); |
- return false; |
- } |
- tracer.close(); |
- log('compilation succeeded'); |
- return true; |
- } |
- |
- void enableNoSuchMethod(Element element) { |
- if (enabledNoSuchMethod) return; |
- if (element.enclosingElement == objectClass) return; |
- enabledNoSuchMethod = true; |
- enqueuer.registerInvocation(NO_SUCH_METHOD, new Invocation(2)); |
- } |
- |
- void enableIsolateSupport(LibraryElement element) { |
- isolateLibrary = element; |
- addToWorkList(element.find(START_ROOT_ISOLATE)); |
- } |
- |
- bool hasIsolateSupport() => isolateLibrary !== null; |
- |
- void onLibraryLoaded(LibraryElement library, Uri uri) { |
- if (uri.toString() == 'dart:isolate') { |
- enableIsolateSupport(library); |
- } |
- if (dynamicClass !== null) { |
- // When loading the built-in libraries, dynamicClass is null. We |
- // take advantage of this as core and coreimpl import js_helper |
- // and see Dynamic this way. |
- withCurrentElement(dynamicClass, () { |
- library.define(dynamicClass, this); |
- }); |
- } |
- } |
- |
- abstract LibraryElement scanBuiltinLibrary(String filename); |
- |
- void initializeSpecialClasses() { |
- objectClass = coreLibrary.find(const SourceString('Object')); |
- boolClass = coreLibrary.find(const SourceString('bool')); |
- numClass = coreLibrary.find(const SourceString('num')); |
- intClass = coreLibrary.find(const SourceString('int')); |
- doubleClass = coreLibrary.find(const SourceString('double')); |
- stringClass = coreLibrary.find(const SourceString('String')); |
- functionClass = coreLibrary.find(const SourceString('Function')); |
- listClass = coreLibrary.find(const SourceString('List')); |
- closureClass = jsHelperLibrary.find(const SourceString('Closure')); |
- dynamicClass = jsHelperLibrary.find(const SourceString('Dynamic')); |
- nullClass = jsHelperLibrary.find(const SourceString('Null')); |
- } |
- |
- void scanBuiltinLibraries() { |
- coreImplLibrary = scanBuiltinLibrary('coreimpl.dart'); |
- jsHelperLibrary = scanBuiltinLibrary('js_helper.dart'); |
- coreLibrary = scanBuiltinLibrary('core.dart'); |
- |
- // Since coreLibrary import the libraries "coreimpl", and |
- // "js_helper", coreLibrary is null when they are being built. So |
- // we add the implicit import of coreLibrary now. This can be |
- // cleaned up when we have proper support for "dart:core" and |
- // don't need to access it through the field "coreLibrary". |
- // TODO(ahe): Clean this up as described above. |
- scanner.importLibrary(coreImplLibrary, coreLibrary, null); |
- scanner.importLibrary(jsHelperLibrary, coreLibrary, null); |
- addForeignFunctions(jsHelperLibrary); |
- |
- universe.libraries['dart:core'] = coreLibrary; |
- universe.libraries['dart:coreimpl'] = coreImplLibrary; |
- |
- initializeSpecialClasses(); |
- } |
- |
- /** Define the JS helper functions in the given library. */ |
- void addForeignFunctions(LibraryElement library) { |
- library.define(new ForeignElement( |
- const SourceString('JS'), library), this); |
- library.define(new ForeignElement( |
- const SourceString('UNINTERCEPTED'), library), this); |
- library.define(new ForeignElement( |
- const SourceString('JS_HAS_EQUALS'), library), this); |
- library.define(new ForeignElement( |
- const SourceString('JS_CURRENT_ISOLATE'), library), this); |
- library.define(new ForeignElement( |
- const SourceString('JS_CALL_IN_ISOLATE'), library), this); |
- library.define(new ForeignElement( |
- const SourceString('DART_CLOSURE_TO_JS'), library), this); |
- } |
- |
- void runCompiler(Uri uri) { |
- scanBuiltinLibraries(); |
- mainApp = scanner.loadLibrary(uri, null); |
- final Element mainMethod = mainApp.find(MAIN); |
- if (mainMethod === null) { |
- withCurrentElement(mainApp, () => cancel('Could not find $MAIN')); |
- } else { |
- withCurrentElement(mainMethod, () { |
- if (!mainMethod.isFunction()) { |
- cancel('main is not a function', element: mainMethod); |
- } |
- FunctionParameters parameters = mainMethod.computeParameters(this); |
- if (parameters.parameterCount > 0) { |
- cancel('main cannot have parameters', element: mainMethod); |
- } |
- }); |
- } |
- native.processNativeClasses(this, universe.libraries.getValues()); |
- enqueue(new WorkItem.toCompile(mainMethod)); |
- codegenProgress.reset(); |
- while (!worklist.isEmpty()) { |
- WorkItem work = worklist.removeLast(); |
- withCurrentElement(work.element, () => (work.run)(this)); |
- } |
- workListIsClosed = true; |
- assert(enqueuer.checkNoEnqueuedInvokedInstanceMethods()); |
- enqueuer.registerFieldClosureInvocations(); |
- emitter.assembleProgram(); |
- if (!worklist.isEmpty()) { |
- internalErrorOnElement(worklist.first().element, |
- "work list is not empty"); |
- } |
- } |
- |
- TreeElements analyzeElement(Element element) { |
- assert(parser !== null); |
- Node tree = parser.parse(element); |
- validator.validate(tree); |
- TreeElements elements = resolver.resolve(element); |
- checker.check(tree, elements); |
- return elements; |
- } |
- |
- TreeElements analyze(WorkItem work) { |
- work.resolutionTree = analyzeElement(work.element); |
- return work.resolutionTree; |
- } |
- |
- String codegen(WorkItem work) { |
- if (codegenProgress.elapsedInMs() > 500) { |
- // TODO(ahe): Add structured diagnostics to the compiler API and |
- // use it to separate this from the --verbose option. |
- log('compiled ${universe.generatedCode.length} methods'); |
- codegenProgress.reset(); |
- } |
- if (work.element.kind.category == ElementCategory.VARIABLE) { |
- constantHandler.compileWorkItem(work); |
- return null; |
- } else { |
- HGraph graph = builder.build(work); |
- optimizer.optimize(work, graph); |
- if (work.allowSpeculativeOptimization |
- && optimizer.trySpeculativeOptimizations(work, graph)) { |
- String code = generator.generateBailoutMethod(work, graph); |
- universe.addBailoutCode(work, code); |
- optimizer.prepareForSpeculativeOptimizations(work, graph); |
- optimizer.optimize(work, graph); |
- code = generator.generateMethod(work, graph); |
- universe.addGeneratedCode(work, code); |
- return code; |
- } else { |
- String code = generator.generateMethod(work, graph); |
- universe.addGeneratedCode(work, code); |
- return code; |
- } |
- } |
- } |
- |
- String compile(WorkItem work) { |
- String code = universe.generatedCode[work.element]; |
- if (code !== null) return code; |
- analyze(work); |
- return codegen(work); |
- } |
- |
- void addToWorkList(Element element) { |
- if (workListIsClosed) { |
- internalErrorOnElement(element, "work list is closed"); |
- } |
- if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { |
- registerInstantiatedClass(element.enclosingElement); |
- } |
- worklist.add(new WorkItem.toCompile(element)); |
- } |
- |
- void registerStaticUse(Element element) { |
- addToWorkList(element); |
- } |
- |
- void registerGetOfStaticFunction(FunctionElement element) { |
- registerStaticUse(element); |
- universe.staticFunctionsNeedingGetter.add(element); |
- } |
- |
- void registerDynamicInvocation(SourceString methodName, Selector selector) { |
- assert(selector !== null); |
- enqueuer.registerInvocation(methodName, selector); |
- } |
- |
- void registerDynamicGetter(SourceString methodName) { |
- enqueuer.registerGetter(methodName); |
- } |
- |
- void registerDynamicSetter(SourceString methodName) { |
- enqueuer.registerSetter(methodName); |
- } |
- |
- void registerInstantiatedClass(ClassElement element) { |
- universe.instantiatedClasses.add(element); |
- enqueuer.onRegisterInstantiatedClass(element); |
- } |
- |
- // TODO(ngeoffray): This should get a type. |
- void registerIsCheck(Element element) { |
- universe.isChecks.add(element); |
- } |
- |
- Type resolveType(ClassElement element) { |
- return withCurrentElement(element, () => resolver.resolveType(element)); |
- } |
- |
- FunctionParameters resolveSignature(FunctionElement element) { |
- return withCurrentElement(element, |
- () => resolver.resolveSignature(element)); |
- } |
- |
- Constant compileVariable(VariableElement element) { |
- return withCurrentElement(element, () { |
- return constantHandler.compileVariable(element); |
- }); |
- } |
- |
- reportWarning(Node node, var message) { |
- if (message is ResolutionWarning) { |
- // TODO(ahe): Don't supress this warning when we support type variables. |
- if (message.message.kind === MessageKind.CANNOT_RESOLVE_TYPE) return; |
- } else if (message is TypeWarning) { |
- // TODO(ahe): Don't supress these warning when the type checker |
- // is more complete. |
- if (message.message.kind === MessageKind.NOT_ASSIGNABLE) return; |
- if (message.message.kind === MessageKind.MISSING_RETURN) return; |
- if (message.message.kind === MessageKind.ADDITIONAL_ARGUMENT) return; |
- if (message.message.kind === MessageKind.METHOD_NOT_FOUND) return; |
- } |
- SourceSpan span = spanFromNode(node); |
- reportDiagnostic(span, "${magenta('warning:')} $message", false); |
- } |
- |
- reportError(Node node, var message) { |
- SourceSpan span = spanFromNode(node); |
- reportDiagnostic(span, "${red('error:')} $message", true); |
- throw new CompilerCancelledException(message.toString()); |
- } |
- |
- abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); |
- |
- SourceSpan spanFromTokens(Token begin, Token end) { |
- if (begin === null || end === null) { |
- // TODO(ahe): We can almost always do better. Often it is only |
- // end that is null. Otherwise, we probably know the current |
- // URI. |
- throw 'cannot find tokens to produce error message'; |
- } |
- final startOffset = begin.charOffset; |
- // TODO(ahe): Compute proper end offset in token. Right now we use |
- // the position of the next token. We want to preserve the |
- // invariant that endOffset > startOffset, but for EOF the |
- // charoffset of the next token may be [startOffset]. This can |
- // also happen for synthetized tokens that are produced during |
- // error handling. |
- final endOffset = |
- Math.max((end.next !== null) ? end.next.charOffset : 0, startOffset + 1); |
- assert(endOffset > startOffset); |
- Uri uri = currentElement.getCompilationUnit().script.uri; |
- return new SourceSpan(uri, startOffset, endOffset); |
- } |
- |
- SourceSpan spanFromNode(Node node) { |
- return spanFromTokens(node.getBeginToken(), node.getEndToken()); |
- } |
- |
- SourceSpan spanFromElement(Element element) { |
- if (element.position() === null) { |
- // Sometimes, the backend fakes up elements that have no |
- // position. So we use the enclosing element instead. It is |
- // not a good error location, but cancel really is "internal |
- // error" or "not implemented yet", so the vicinity is good |
- // enough for now. |
- element = element.enclosingElement; |
- // TODO(ahe): I plan to overhaul this infrastructure anyways. |
- } |
- if (element === null) { |
- element = currentElement; |
- } |
- Token position = element.position(); |
- if (position === null) { |
- // TODO(ahe): Find the enclosing library. |
- return const SourceSpan(null, null, null); |
- } |
- return spanFromTokens(position, position); |
- } |
- |
- Script readScript(Uri uri, [ScriptTag node]) { |
- unimplemented('Compiler.readScript'); |
- } |
- |
- String get legDirectory() { |
- unimplemented('Compiler.legDirectory'); |
- } |
- |
- Element findHelper(SourceString name) => jsHelperLibrary.find(name); |
- |
- bool get isMockCompilation() => false; |
-} |
- |
-class CompilerTask { |
- final Compiler compiler; |
- final Stopwatch watch; |
- |
- CompilerTask(this.compiler) : watch = new Stopwatch(); |
- |
- String get name() => 'Unknown task'; |
- int get timing() => watch.elapsedInMs(); |
- |
- measure(Function action) { |
- // TODO(kasperl): Do we have to worry about exceptions here? |
- CompilerTask previous = compiler.measuredTask; |
- compiler.measuredTask = this; |
- if (previous !== null) previous.watch.stop(); |
- watch.start(); |
- var result = action(); |
- watch.stop(); |
- if (previous !== null) previous.watch.start(); |
- compiler.measuredTask = previous; |
- return result; |
- } |
-} |
- |
-class CompilerCancelledException implements Exception { |
- final String reason; |
- CompilerCancelledException(this.reason); |
- |
- String toString() { |
- String banner = 'compiler cancelled'; |
- return (reason !== null) ? '$banner: $reason' : '$banner'; |
- } |
-} |
- |
-interface Tracer default LTracer { |
- const Tracer(); |
- final bool enabled; |
- void traceCompilation(String methodName); |
- void traceGraph(String name, var graph); |
- void close(); |
-} |
- |
-// TODO(ahe): Remove when the VM supports implicit interfaces. |
-class LTracer implements Tracer { |
- const LTracer(); |
- final bool enabled = false; |
- void traceCompilation(String methodName) { |
- } |
- void traceGraph(String name, var graph) { |
- } |
- void close() { |
- } |
-} |
- |
-class SourceSpan { |
- final Uri uri; |
- final int begin; |
- final int end; |
- |
- const SourceSpan(this.uri, this.begin, this.end); |
-} |