Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Unified Diff: lib/src/codegen/js_module_item_order.dart

Issue 1133593004: fixes #131, use before define from variables to classes (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/codegen/js_codegen.dart ('k') | lib/src/codegen/js_names.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
new file mode 100644
index 0000000000000000000000000000000000000000..eadf6716f5578d1803c6d9a92bf87b085bf49435
--- /dev/null
+++ b/lib/src/codegen/js_module_item_order.dart
@@ -0,0 +1,227 @@
+// 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/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:dev_compiler/src/dependency_graph.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>();
+
+ final ModuleItemEmitter _emitModuleItem;
+
+ LibraryElement _currentLibrary;
+
+ ModuleItemLoadOrder(this._emitModuleItem);
+
+ bool isLoaded(Element e) => _loaded[e] == true;
+
+ /// 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 ClassDeclaration) {
+ for (var member in decl.members) {
+ if (member is FieldDeclaration && member.isStatic) {
+ _collectElementsForVariable(member.fields);
+ }
+ }
+ } else 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));
+ // Assume loading will succeed until proven otherwise.
+ _loaded[e] = true;
+ _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 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 || _topLevelElements.isEmpty) return;
+ if (!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);
+
+ // The SDK is a special case: we optimize the order to prevent laziness.
+ if (library.isInSdk) {
+ // SDK is loaded before non-SDK libraries
+ if (!_currentLibrary.isInSdk) return true;
+
+ // Compute the order of both SDK libraries. If unknown, assume it's after.
+ var order = corelibOrder.indexOf(library.name);
+ if (order == -1) order = corelibOrder.length;
+
+ var currentOrder = corelibOrder.indexOf(_currentLibrary.name);
+ 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.isInSdk && !_currentLibrary.isInSdk) 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;
+ }
+}
« no previous file with comments | « lib/src/codegen/js_codegen.dart ('k') | lib/src/codegen/js_names.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698