Index: lib/src/codegen/js_module_item_order.dart |
diff --git a/lib/src/codegen/js_module_item_order.dart b/lib/src/codegen/js_module_item_order.dart |
deleted file mode 100644 |
index bbb9c6ce0cc12d2c2040911aebcbc99a993acb38..0000000000000000000000000000000000000000 |
--- a/lib/src/codegen/js_module_item_order.dart |
+++ /dev/null |
@@ -1,260 +0,0 @@ |
-// Copyright (c) 2015, 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. |
- |
-import 'dart:collection' show HashMap; |
- |
-import 'package:analyzer/dart/ast/ast.dart'; |
-import 'package:analyzer/dart/element/element.dart'; |
- |
-import '../compiler.dart' show corelibOrder; |
- |
-typedef void ModuleItemEmitter(AstNode item); |
- |
-/// Helper that tracks order of elements visited by the compiler, detecting |
-/// if the top level item can be loaded eagerly or not. |
-class ModuleItemLoadOrder { |
- /// The order that elements should be emitted in, with a bit indicating if |
- /// the element should be generated lazily. The value will be `false` if |
- /// the item could not be loaded, and needs to be made lazy. |
- /// |
- /// The order will match the original source order, unless something needed to |
- /// be moved sooner to satisfy dependencies. |
- /// |
- /// Sometimes it's impossible to ensure an ordering when confronting library |
- /// cycles. In that case, we mark the definition as lazy. |
- final _loaded = new Map<Element, bool>(); |
- |
- final _declarationNodes = new HashMap<Element, AstNode>(); |
- |
- /// The stack of currently emitting elements, if generating top-level code |
- /// for them. This is not used when inside method bodies, because order does |
- /// not matter for those. |
- final _topLevelElements = new List<Element>(); |
- |
- /// The current element being loaded. |
- /// We can use this to determine if we're loading top-level code or not: |
- /// |
- /// _currentElements.last == _topLevelElements.last |
- final _currentElements = new List<Element>(); |
- |
- /// Memoized results of [_inLibraryCycle]. |
- final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
- |
- bool _checkReferences; |
- |
- final ModuleItemEmitter _emitModuleItem; |
- |
- LibraryElement _currentLibrary; |
- |
- ModuleItemLoadOrder(this._emitModuleItem); |
- |
- bool isLoaded(Element e) => (e.library == _currentLibrary) |
- ? _loaded[e] == true |
- : libraryIsLoaded(e.library); |
- |
- /// True if the element is currently being loaded. |
- bool _isLoading(Element e) => _currentElements.contains(e); |
- |
- /// Collect top-level elements and nodes we need to emit. |
- void collectElements( |
- LibraryElement library, Iterable<CompilationUnit> partsThenLibrary) { |
- assert(_currentLibrary == null); |
- _currentLibrary = library; |
- |
- for (var unit in partsThenLibrary) { |
- for (var decl in unit.declarations) { |
- _declarationNodes[decl.element] = decl; |
- |
- if (decl is TopLevelVariableDeclaration) { |
- _collectElementsForVariable(decl.variables); |
- } |
- } |
- } |
- } |
- |
- void _collectElementsForVariable(VariableDeclarationList fields) { |
- for (var field in fields.variables) { |
- _declarationNodes[field.element] = field; |
- } |
- } |
- |
- /// Start generating top-level code for the element [e]. |
- /// Subsequent [loadElement] calls will cause those elements to be generated |
- /// before this one, until [finishElement] is called. |
- void startTopLevel(Element e) { |
- assert(isCurrentElement(e)); |
- _topLevelElements.add(e); |
- } |
- |
- /// Finishes the top-level code for the element [e]. |
- void finishTopLevel(Element e) { |
- var last = _topLevelElements.removeLast(); |
- assert(identical(e, last)); |
- } |
- |
- /// Starts recording calls to [declareBeforeUse], until |
- /// [finishCheckingReferences] is called. |
- void startCheckingReferences() { |
- // This function should not be reentrant, and we should not current be |
- // emitting top-level code. |
- assert(_checkReferences == null); |
- assert( |
- _topLevelElements.isEmpty || !isCurrentElement(_topLevelElements.last)); |
- // Assume true until proven otherwise |
- _checkReferences = true; |
- } |
- |
- /// Finishes recording references, and returns `true` if all referenced |
- /// items were loaded (or if no items were referenced). |
- bool finishCheckingReferences() { |
- var result = _checkReferences; |
- _checkReferences = null; |
- return result; |
- } |
- |
- // Starts generating code for the declaration element [e]. |
- // |
- // Normally this is called implicitly by [loadDeclaration] and/or |
- // [loadElement]. However, for synthetic elements (like temporary variables) |
- // it must be called explicitly, and paired with a call to [finishElement]. |
- void startDeclaration(Element e) { |
- // Assume load will succeed until proven otherwise. |
- _loaded[e] = true; |
- _currentElements.add(e); |
- } |
- |
- /// Returns true if all dependencies were loaded successfully. |
- bool finishDeclaration(Element e) { |
- var last = _currentElements.removeLast(); |
- assert(identical(e, last)); |
- return _loaded[e]; |
- } |
- |
- /// Loads a top-level declaration. This is similar to [loadElement], but it |
- /// ensures we always visit the node if it has not been emitted yet. In other |
- /// words, it handles nodes that do not need dependency resolution like |
- /// top-level functions. |
- bool loadDeclaration(AstNode node, Element e) { |
- assert(e.library == _currentLibrary); |
- |
- // If we already tried to load it, see if we succeeded or not. |
- // Otherwise try and load now. |
- var loaded = _loaded[e]; |
- if (loaded != null) return loaded; |
- |
- // Otherwise, try to load it. |
- startDeclaration(e); |
- _emitModuleItem(node); |
- return finishDeclaration(e); |
- } |
- |
- bool isCurrentElement(Element e) => |
- _currentElements.isNotEmpty && identical(e, _currentElements.last); |
- |
- /// To emit top-level module items, we sometimes need to reorder them. |
- /// |
- /// This function takes care of that, and also detects cases where reordering |
- /// failed, and we need to resort to lazy loading, by marking the element as |
- /// lazy. All elements need to be aware of this possibility and generate code |
- /// accordingly. |
- /// |
- /// If we are not emitting top-level code, this does nothing, because all |
- /// declarations are assumed to be available before we start execution. |
- /// See [startTopLevel]. |
- void declareBeforeUse(Element e) { |
- if (e == null) return; |
- |
- if (_checkReferences != null) { |
- _checkReferences = _checkReferences && isLoaded(e) && !_isLoading(e); |
- return; |
- } |
- |
- if (_topLevelElements.isEmpty || |
- !isCurrentElement(_topLevelElements.last)) { |
- return; |
- } |
- |
- // If the item is from our library, try to emit it now. |
- bool loaded; |
- if (e.library == _currentLibrary) { |
- // Type parameters are not in scope when generating hoisted fields. |
- if (e is TypeParameterElement && |
- _currentElements.last is VariableElement) { |
- loaded = false; |
- } else { |
- var node = _declarationNodes[e]; |
- loaded = node == null || loadDeclaration(node, e); |
- } |
- } else { |
- // We can't force things from other libraries to be emitted in a different |
- // order. Instead, we see if the library itself can be loaded before the |
- // current library. Generally that is possible, unless we have cyclic |
- // imports. |
- loaded = libraryIsLoaded(e.library); |
- } |
- |
- if (loaded) return; |
- |
- // If we failed to emit it, then we need to make sure all currently emitting |
- // elements are generated in a lazy way. |
- for (var current in _topLevelElements) { |
- // TODO(jmesserly): if we want to log what triggered laziness, this is |
- // the place to do so. |
- _loaded[current] = false; |
- } |
- } |
- |
- bool libraryIsLoaded(LibraryElement library) { |
- assert(library != _currentLibrary); |
- |
- // DynamicElementImpl has no library. |
- if (library == null) return true; |
- |
- // The SDK is a special case: we optimize the order to prevent laziness. |
- if (library.source.isInSystemLibrary) { |
- // SDK is loaded before non-SDK libraries |
- if (!_currentLibrary.source.isInSystemLibrary) return true; |
- |
- // Compute the order of both SDK libraries. If unknown, assume it's after. |
- var order = corelibOrder.indexOf(library.source.uri); |
- if (order == -1) order = corelibOrder.length; |
- |
- var currentOrder = corelibOrder.indexOf(_currentLibrary.source.uri); |
- if (currentOrder == -1) currentOrder = corelibOrder.length; |
- |
- // If the dart:* library we are currently compiling is loaded after the |
- // class's library, then we know the class is available. |
- if (order != currentOrder) return currentOrder > order; |
- |
- // If we don't know the order of the class's library or the current |
- // library, do the normal cycle check. (Not all SDK libs are cycles.) |
- } |
- |
- return !_inLibraryCycle(library); |
- } |
- |
- /// Returns true if [library] depends on the [currentLibrary] via some |
- /// transitive import. |
- bool _inLibraryCycle(LibraryElement library) { |
- // SDK libs don't depend on things outside the SDK. |
- // (We can reach this via the recursive call below.) |
- if (library.source.isInSystemLibrary && |
- !_currentLibrary.source.isInSystemLibrary) return false; |
- |
- var result = _libraryCycleMemo[library]; |
- if (result != null) return result; |
- |
- result = library == _currentLibrary; |
- _libraryCycleMemo[library] = result; |
- for (var e in library.imports) { |
- if (result) break; |
- result = _inLibraryCycle(e.importedLibrary); |
- } |
- for (var e in library.exports) { |
- if (result) break; |
- result = _inLibraryCycle(e.exportedLibrary); |
- } |
- return _libraryCycleMemo[library] = result; |
- } |
-} |