Index: lib/src/codegen/js_codegen.dart |
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
index 8a769573489637c8247f625e89d4fc13b699bf65..1207e484d88d6e003e85c478586638b43c04e840 100644 |
--- a/lib/src/codegen/js_codegen.dart |
+++ b/lib/src/codegen/js_codegen.dart |
@@ -13,6 +13,7 @@ import 'package:analyzer/src/generated/element.dart'; |
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
import 'package:analyzer/src/generated/scanner.dart' |
show StringToken, Token, TokenType; |
+import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; |
import 'package:dev_compiler/src/codegen/reify_coercions.dart' |
@@ -95,6 +96,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
/// _interceptors.JSArray<E>, used for List literals. |
ClassElement _jsArray; |
+ /// The default value of the module object. See [visitLibraryDirective]. |
+ String _jsModuleValue; |
+ |
Map<String, DartType> _objectMembers; |
JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, |
@@ -115,23 +119,24 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
TypeProvider get types => rules.provider; |
JS.Program emitLibrary(LibraryUnit library) { |
- String jsDefaultValue = null; |
- |
// Modify the AST to make coercions explicit. |
new CoercionReifier(library, compiler).reify(); |
- var unit = library.library; |
- if (unit.directives.isNotEmpty) { |
- var libraryDir = unit.directives.first; |
- if (libraryDir is LibraryDirective) { |
- var jsName = findAnnotation(libraryDir.element, _isJsNameAnnotation); |
- jsDefaultValue = |
- getConstantField(jsName, 'name', types.stringType) as String; |
- } |
+ // Build the public namespace for this library. This allows us to do |
+ // constant time lookups (contrast with `Element.getChild(name)`). |
+ if (currentLibrary.publicNamespace == null) { |
+ (currentLibrary as LibraryElementImpl).publicNamespace = |
+ new PublicNamespaceBuilder().build(currentLibrary); |
} |
- // TODO(jmesserly): visit scriptTag, directives? |
Jennifer Messerly
2015/08/03 21:11:50
went ahead and fixed scriptTag too
|
+ library.library.directives.forEach(_visit); |
+ // Rather than directly visit declarations, we instead use [_loader] to |
+ // visit them. It has the ability to sort elements on demand, so |
+ // dependencies between top level items are handled with a minimal |
+ // reordering of the user's input code. The loader will call back into |
+ // this visitor via [_emitModuleItem] when it's ready to visit the item |
+ // for real. |
_loader.collectElements(currentLibrary, library.partsThenLibrary); |
for (var unit in library.partsThenLibrary) { |
@@ -139,7 +144,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
for (var decl in unit.declarations) { |
if (decl is TopLevelVariableDeclaration) { |
- _visit(decl); |
+ visitTopLevelVariableDeclaration(decl); |
} else { |
_loader.loadDeclaration(decl, decl.element); |
} |
@@ -147,10 +152,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
// Static fields can be emitted into the top-level code, so they need |
// to potentially be ordered independently of the class. |
for (var member in decl.members) { |
- if (member is FieldDeclaration && member.isStatic) { |
- for (var f in member.fields.variables) { |
- _loader.loadDeclaration(f, f.element); |
- } |
+ if (member is FieldDeclaration) { |
+ visitFieldDeclaration(member); |
} |
} |
} |
@@ -208,19 +211,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
var module = js.call("function(#) { 'use strict'; #; #; }", |
[params, dartxImport, _moduleItems]); |
- var program = <JS.Statement>[ |
- js.statement("dart_library.library(#, #, #, #, #)", [ |
- js.string(jsPath, "'"), |
- jsDefaultValue ?? new JS.LiteralNull(), |
- js.commentExpression( |
- "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
- js.commentExpression("Lazy imports", |
- new JS.ArrayInitializer(lazyImports, multiline: true)), |
- module |
- ]) |
- ]; |
+ var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ |
+ js.string(jsPath, "'"), |
+ _jsModuleValue ?? new JS.LiteralNull(), |
+ js.commentExpression( |
+ "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
+ js.commentExpression("Lazy imports", |
+ new JS.ArrayInitializer(lazyImports, multiline: true)), |
+ module |
+ ]); |
+ |
+ var jsBin = compiler.options.runnerOptions.v8Binary; |
- return new JS.Program(program); |
+ String scriptTag = null; |
+ if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; |
+ return new JS.Program(<JS.Statement>[moduleDef], scriptTag: scriptTag); |
} |
void _emitModuleItem(AstNode node) { |
@@ -232,6 +237,54 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
if (code != null) _moduleItems.add(code); |
} |
+ @override |
+ void visitLibraryDirective(LibraryDirective node) { |
+ assert(_jsModuleValue == null); |
+ |
+ var jsName = findAnnotation(node.element, _isJsNameAnnotation); |
+ _jsModuleValue = |
+ getConstantField(jsName, 'name', types.stringType) as String; |
+ } |
+ |
+ @override |
+ void visitImportDirective(ImportDirective node) { |
+ // Nothing to do yet, but we'll want to convert this to an ES6 import once |
+ // we have support for modules. |
+ } |
+ |
+ @override void visitPartDirective(PartDirective node) {} |
+ @override void visitPartOfDirective(PartOfDirective node) {} |
+ |
+ @override |
+ void visitExportDirective(ExportDirective node) { |
+ var exportName = _libraryName(node.uriElement); |
+ |
+ var currentLibNames = currentLibrary.publicNamespace.definedNames; |
+ |
+ var args = [_exportsVar, exportName]; |
+ if (node.combinators.isNotEmpty) { |
+ var shownNames = <JS.Expression>[]; |
+ var hiddenNames = <JS.Expression>[]; |
+ |
+ var show = node.combinators.firstWhere((c) => c is ShowCombinator, |
+ orElse: () => null) as ShowCombinator; |
+ var hide = node.combinators.firstWhere((c) => c is HideCombinator, |
+ orElse: () => null) as HideCombinator; |
+ if (show != null) { |
+ shownNames.addAll(show.shownNames |
+ .map((i) => i.name) |
+ .where((s) => !currentLibNames.containsKey(s)) |
+ .map((s) => js.string(s, "'"))); |
+ } |
+ if (hide != null) { |
+ hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); |
+ } |
+ args.add(new JS.ArrayInitializer(shownNames)); |
+ args.add(new JS.ArrayInitializer(hiddenNames)); |
+ } |
+ _moduleItems.add(js.statement('dart.export(#);', [args])); |
+ } |
+ |
JS.Identifier _initSymbol(JS.Identifier id) { |
var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
_moduleItems.add(s); |
@@ -1776,6 +1829,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor { |
} |
} |
+ /// Emits static fields. |
+ /// |
+ /// Instance fields are emitted in [_initializeFields]. |
+ /// |
+ /// These are generally treated the same as top-level fields, see |
+ /// [visitTopLevelVariableDeclaration]. |
+ @override |
+ visitFieldDeclaration(FieldDeclaration node) { |
+ if (!node.isStatic) return; |
+ |
+ for (var f in node.fields.variables) { |
+ _loader.loadDeclaration(f, f.element); |
+ } |
+ } |
+ |
_addExport(String name) { |
if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; |
} |