@@ -13,6 +13,7 @@ import 'package:analyzer/src/generated/element.dart';
13
13
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
14
14
import 'package:analyzer/src/generated/scanner.dart'
15
15
show StringToken, Token, TokenType;
16
+ import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder;
16
17
17
18
import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
18
19
import 'package:dev_compiler/src/codegen/reify_coercions.dart'
@@ -95,6 +96,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor {
95
96
/// _interceptors.JSArray<E>, used for List literals.
96
97
ClassElement _jsArray;
97
98
99
+ /// The default value of the module object. See [visitLibraryDirective] .
100
+ String _jsModuleValue;
101
+
98
102
Map <String , DartType > _objectMembers;
99
103
100
104
JSCodegenVisitor (AbstractCompiler compiler, this .currentLibrary,
@@ -115,42 +119,41 @@ class JSCodegenVisitor extends GeneralizingAstVisitor {
115
119
TypeProvider get types => rules.provider;
116
120
117
121
JS .Program emitLibrary (LibraryUnit library) {
118
- String jsDefaultValue = null ;
119
-
120
122
// Modify the AST to make coercions explicit.
121
123
new CoercionReifier (library, compiler).reify ();
122
124
123
- var unit = library.library;
124
- if (unit.directives.isNotEmpty) {
125
- var libraryDir = unit.directives.first;
126
- if (libraryDir is LibraryDirective ) {
127
- var jsName = findAnnotation (libraryDir.element, _isJsNameAnnotation);
128
- jsDefaultValue =
129
- getConstantField (jsName, 'name' , types.stringType) as String ;
130
- }
125
+ // Build the public namespace for this library. This allows us to do
126
+ // constant time lookups (contrast with `Element.getChild(name)`).
127
+ if (currentLibrary.publicNamespace == null ) {
128
+ (currentLibrary as LibraryElementImpl ).publicNamespace =
129
+ new PublicNamespaceBuilder ().build (currentLibrary);
131
130
}
132
131
133
- // TODO(jmesserly): visit scriptTag, directives?
132
+ library.library.directives. forEach (_visit);
134
133
134
+ // Rather than directly visit declarations, we instead use [_loader] to
135
+ // visit them. It has the ability to sort elements on demand, so
136
+ // dependencies between top level items are handled with a minimal
137
+ // reordering of the user's input code. The loader will call back into
138
+ // this visitor via [_emitModuleItem] when it's ready to visit the item
139
+ // for real.
135
140
_loader.collectElements (currentLibrary, library.partsThenLibrary);
136
141
137
142
for (var unit in library.partsThenLibrary) {
138
143
_constField = new ConstFieldVisitor (types, unit);
139
144
140
145
for (var decl in unit.declarations) {
141
146
if (decl is TopLevelVariableDeclaration ) {
142
- _visit (decl);
147
+ visitTopLevelVariableDeclaration (decl);
143
148
} else {
144
149
_loader.loadDeclaration (decl, decl.element);
145
150
}
146
151
if (decl is ClassDeclaration ) {
147
152
// Static fields can be emitted into the top-level code, so they need
148
153
// to potentially be ordered independently of the class.
149
154
for (var member in decl.members) {
150
- if (member is FieldDeclaration && member.isStatic) {
151
- for (var f in member.fields.variables) {
152
- _loader.loadDeclaration (f, f.element);
153
- }
155
+ if (member is FieldDeclaration ) {
156
+ visitFieldDeclaration (member);
154
157
}
155
158
}
156
159
}
@@ -208,19 +211,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor {
208
211
var module = js.call ("function(#) { 'use strict'; #; #; }" ,
209
212
[params, dartxImport, _moduleItems]);
210
213
211
- var program = < JS . Statement > [
212
- js.statement ( "dart_library.library(#, #, #, #, #)" , [
213
- js. string (jsPath, "'" ),
214
- jsDefaultValue ?? new JS . LiteralNull (),
215
- js. commentExpression (
216
- "Imports" , new JS . ArrayInitializer (imports, multiline : true )) ,
217
- js. commentExpression ( "Lazy imports" ,
218
- new JS . ArrayInitializer (lazyImports, multiline : true )),
219
- module
220
- ])
221
- ] ;
214
+ var moduleDef = js. statement ( "dart_library.library(#, #, #, #, #)" , [
215
+ js.string (jsPath, "'" ),
216
+ _jsModuleValue ?? new JS . LiteralNull ( ),
217
+ js. commentExpression (
218
+ "Imports" , new JS . ArrayInitializer (imports, multiline : true )),
219
+ js. commentExpression ( "Lazy imports" ,
220
+ new JS . ArrayInitializer (lazyImports, multiline : true )) ,
221
+ module
222
+ ]);
223
+
224
+ var jsBin = compiler.options.runnerOptions.v8Binary ;
222
225
223
- return new JS .Program (program);
226
+ String scriptTag = null ;
227
+ if (library.library.scriptTag != null ) scriptTag = '/usr/bin/env $jsBin ' ;
228
+ return new JS .Program (< JS .Statement > [moduleDef], scriptTag: scriptTag);
224
229
}
225
230
226
231
void _emitModuleItem (AstNode node) {
@@ -232,6 +237,54 @@ class JSCodegenVisitor extends GeneralizingAstVisitor {
232
237
if (code != null ) _moduleItems.add (code);
233
238
}
234
239
240
+ @override
241
+ void visitLibraryDirective (LibraryDirective node) {
242
+ assert (_jsModuleValue == null );
243
+
244
+ var jsName = findAnnotation (node.element, _isJsNameAnnotation);
245
+ _jsModuleValue =
246
+ getConstantField (jsName, 'name' , types.stringType) as String ;
247
+ }
248
+
249
+ @override
250
+ void visitImportDirective (ImportDirective node) {
251
+ // Nothing to do yet, but we'll want to convert this to an ES6 import once
252
+ // we have support for modules.
253
+ }
254
+
255
+ @override void visitPartDirective (PartDirective node) {}
256
+ @override void visitPartOfDirective (PartOfDirective node) {}
257
+
258
+ @override
259
+ void visitExportDirective (ExportDirective node) {
260
+ var exportName = _libraryName (node.uriElement);
261
+
262
+ var currentLibNames = currentLibrary.publicNamespace.definedNames;
263
+
264
+ var args = [_exportsVar, exportName];
265
+ if (node.combinators.isNotEmpty) {
266
+ var shownNames = < JS .Expression > [];
267
+ var hiddenNames = < JS .Expression > [];
268
+
269
+ var show = node.combinators.firstWhere ((c) => c is ShowCombinator ,
270
+ orElse: () => null ) as ShowCombinator ;
271
+ var hide = node.combinators.firstWhere ((c) => c is HideCombinator ,
272
+ orElse: () => null ) as HideCombinator ;
273
+ if (show != null ) {
274
+ shownNames.addAll (show.shownNames
275
+ .map ((i) => i.name)
276
+ .where ((s) => ! currentLibNames.containsKey (s))
277
+ .map ((s) => js.string (s, "'" )));
278
+ }
279
+ if (hide != null ) {
280
+ hiddenNames.addAll (hide.hiddenNames.map ((i) => js.string (i.name, "'" )));
281
+ }
282
+ args.add (new JS .ArrayInitializer (shownNames));
283
+ args.add (new JS .ArrayInitializer (hiddenNames));
284
+ }
285
+ _moduleItems.add (js.statement ('dart.export(#);' , [args]));
286
+ }
287
+
235
288
JS .Identifier _initSymbol (JS .Identifier id) {
236
289
var s = js.statement ('let # = $_SYMBOL (#);' , [id, js.string (id.name, "'" )]);
237
290
_moduleItems.add (s);
@@ -1779,6 +1832,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor {
1779
1832
}
1780
1833
}
1781
1834
1835
+ /// Emits static fields.
1836
+ ///
1837
+ /// Instance fields are emitted in [_initializeFields] .
1838
+ ///
1839
+ /// These are generally treated the same as top-level fields, see
1840
+ /// [visitTopLevelVariableDeclaration] .
1841
+ @override
1842
+ visitFieldDeclaration (FieldDeclaration node) {
1843
+ if (! node.isStatic) return ;
1844
+
1845
+ for (var f in node.fields.variables) {
1846
+ _loader.loadDeclaration (f, f.element);
1847
+ }
1848
+ }
1849
+
1782
1850
_addExport (String name) {
1783
1851
if (! _exports.add (name)) throw 'Duplicate top level name found: $name ' ;
1784
1852
}
0 commit comments