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

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1263583005: implement exports, fixes #141 (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: rebase Created 5 years, 4 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
8 8
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
11 import 'package:analyzer/src/generated/constant.dart'; 11 import 'package:analyzer/src/generated/constant.dart';
12 import 'package:analyzer/src/generated/element.dart'; 12 import 'package:analyzer/src/generated/element.dart';
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
14 import 'package:analyzer/src/generated/scanner.dart' 14 import 'package:analyzer/src/generated/scanner.dart'
15 show StringToken, Token, TokenType; 15 show StringToken, Token, TokenType;
16 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder;
16 17
17 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
18 import 'package:dev_compiler/src/codegen/reify_coercions.dart' 19 import 'package:dev_compiler/src/codegen/reify_coercions.dart'
19 show CoercionReifier; 20 show CoercionReifier;
20 21
21 // TODO(jmesserly): import from its own package 22 // TODO(jmesserly): import from its own package
22 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 23 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
23 import 'package:dev_compiler/src/js/js_ast.dart' show js; 24 import 'package:dev_compiler/src/js/js_ast.dart' show js;
24 25
25 import 'package:dev_compiler/src/compiler.dart' show AbstractCompiler; 26 import 'package:dev_compiler/src/compiler.dart' show AbstractCompiler;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 final _runtimeLibVar = new JS.Identifier('dart'); 89 final _runtimeLibVar = new JS.Identifier('dart');
89 final _namedArgTemp = new JS.TemporaryId('opts'); 90 final _namedArgTemp = new JS.TemporaryId('opts');
90 91
91 ConstFieldVisitor _constField; 92 ConstFieldVisitor _constField;
92 93
93 ModuleItemLoadOrder _loader; 94 ModuleItemLoadOrder _loader;
94 95
95 /// _interceptors.JSArray<E>, used for List literals. 96 /// _interceptors.JSArray<E>, used for List literals.
96 ClassElement _jsArray; 97 ClassElement _jsArray;
97 98
99 /// The default value of the module object. See [visitLibraryDirective].
100 String _jsModuleValue;
101
98 Map<String, DartType> _objectMembers; 102 Map<String, DartType> _objectMembers;
99 103
100 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, 104 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary,
101 this._extensionTypes, this._fieldsNeedingStorage) 105 this._extensionTypes, this._fieldsNeedingStorage)
102 : compiler = compiler, 106 : compiler = compiler,
103 options = compiler.options.codegenOptions, 107 options = compiler.options.codegenOptions,
104 rules = compiler.rules { 108 rules = compiler.rules {
105 _loader = new ModuleItemLoadOrder(_emitModuleItem); 109 _loader = new ModuleItemLoadOrder(_emitModuleItem);
106 110
107 var context = compiler.context; 111 var context = compiler.context;
108 var src = context.sourceFactory.forUri('dart:_interceptors'); 112 var src = context.sourceFactory.forUri('dart:_interceptors');
109 var interceptors = context.computeLibraryElement(src); 113 var interceptors = context.computeLibraryElement(src);
110 _jsArray = interceptors.getType('JSArray'); 114 _jsArray = interceptors.getType('JSArray');
111 115
112 _objectMembers = getObjectMemberMap(types); 116 _objectMembers = getObjectMemberMap(types);
113 } 117 }
114 118
115 TypeProvider get types => rules.provider; 119 TypeProvider get types => rules.provider;
116 120
117 JS.Program emitLibrary(LibraryUnit library) { 121 JS.Program emitLibrary(LibraryUnit library) {
118 String jsDefaultValue = null;
119
120 // Modify the AST to make coercions explicit. 122 // Modify the AST to make coercions explicit.
121 new CoercionReifier(library, compiler).reify(); 123 new CoercionReifier(library, compiler).reify();
122 124
123 var unit = library.library; 125 // Build the public namespace for this library. This allows us to do
124 if (unit.directives.isNotEmpty) { 126 // constant time lookups (contrast with `Element.getChild(name)`).
125 var libraryDir = unit.directives.first; 127 if (currentLibrary.publicNamespace == null) {
126 if (libraryDir is LibraryDirective) { 128 (currentLibrary as LibraryElementImpl).publicNamespace =
127 var jsName = findAnnotation(libraryDir.element, _isJsNameAnnotation); 129 new PublicNamespaceBuilder().build(currentLibrary);
128 jsDefaultValue =
129 getConstantField(jsName, 'name', types.stringType) as String;
130 }
131 } 130 }
132 131
133 // TODO(jmesserly): visit scriptTag, directives? 132 library.library.directives.forEach(_visit);
Jennifer Messerly 2015/08/03 21:11:50 went ahead and fixed scriptTag too
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 _loader.collectElements(currentLibrary, library.partsThenLibrary); 140 _loader.collectElements(currentLibrary, library.partsThenLibrary);
136 141
137 for (var unit in library.partsThenLibrary) { 142 for (var unit in library.partsThenLibrary) {
138 _constField = new ConstFieldVisitor(types, unit); 143 _constField = new ConstFieldVisitor(types, unit);
139 144
140 for (var decl in unit.declarations) { 145 for (var decl in unit.declarations) {
141 if (decl is TopLevelVariableDeclaration) { 146 if (decl is TopLevelVariableDeclaration) {
142 _visit(decl); 147 visitTopLevelVariableDeclaration(decl);
143 } else { 148 } else {
144 _loader.loadDeclaration(decl, decl.element); 149 _loader.loadDeclaration(decl, decl.element);
145 } 150 }
146 if (decl is ClassDeclaration) { 151 if (decl is ClassDeclaration) {
147 // Static fields can be emitted into the top-level code, so they need 152 // Static fields can be emitted into the top-level code, so they need
148 // to potentially be ordered independently of the class. 153 // to potentially be ordered independently of the class.
149 for (var member in decl.members) { 154 for (var member in decl.members) {
150 if (member is FieldDeclaration && member.isStatic) { 155 if (member is FieldDeclaration) {
151 for (var f in member.fields.variables) { 156 visitFieldDeclaration(member);
152 _loader.loadDeclaration(f, f.element);
153 }
154 } 157 }
155 } 158 }
156 } 159 }
157 } 160 }
158 } 161 }
159 162
160 // Flush any unwritten fields/properties. 163 // Flush any unwritten fields/properties.
161 _flushLazyFields(_moduleItems); 164 _flushLazyFields(_moduleItems);
162 _flushLibraryProperties(_moduleItems); 165 _flushLibraryProperties(_moduleItems);
163 166
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 processImport(library, temp, lazyImports); 204 processImport(library, temp, lazyImports);
202 } 205 }
203 }); 206 });
204 207
205 var dartxImport = 208 var dartxImport =
206 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); 209 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]);
207 210
208 var module = js.call("function(#) { 'use strict'; #; #; }", 211 var module = js.call("function(#) { 'use strict'; #; #; }",
209 [params, dartxImport, _moduleItems]); 212 [params, dartxImport, _moduleItems]);
210 213
211 var program = <JS.Statement>[ 214 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [
212 js.statement("dart_library.library(#, #, #, #, #)", [ 215 js.string(jsPath, "'"),
213 js.string(jsPath, "'"), 216 _jsModuleValue ?? new JS.LiteralNull(),
214 jsDefaultValue ?? new JS.LiteralNull(), 217 js.commentExpression(
215 js.commentExpression( 218 "Imports", new JS.ArrayInitializer(imports, multiline: true)),
216 "Imports", new JS.ArrayInitializer(imports, multiline: true)), 219 js.commentExpression("Lazy imports",
217 js.commentExpression("Lazy imports", 220 new JS.ArrayInitializer(lazyImports, multiline: true)),
218 new JS.ArrayInitializer(lazyImports, multiline: true)), 221 module
219 module 222 ]);
220 ])
221 ];
222 223
223 return new JS.Program(program); 224 var jsBin = compiler.options.runnerOptions.v8Binary;
225
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 void _emitModuleItem(AstNode node) { 231 void _emitModuleItem(AstNode node) {
227 // Attempt to group adjacent fields/properties. 232 // Attempt to group adjacent fields/properties.
228 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems); 233 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems);
229 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); 234 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
230 235
231 var code = _visit(node); 236 var code = _visit(node);
232 if (code != null) _moduleItems.add(code); 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 JS.Identifier _initSymbol(JS.Identifier id) { 288 JS.Identifier _initSymbol(JS.Identifier id) {
236 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); 289 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
237 _moduleItems.add(s); 290 _moduleItems.add(s);
238 return id; 291 return id;
239 } 292 }
240 293
241 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 294 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
242 // until we have better name tracking. 295 // until we have better name tracking.
243 String get _SYMBOL { 296 String get _SYMBOL {
244 var name = currentLibrary.name; 297 var name = currentLibrary.name;
(...skipping 1524 matching lines...) Expand 10 before | Expand all | Expand 10 after
1769 return new JS.Yield(_visit(node.expression)); 1822 return new JS.Yield(_visit(node.expression));
1770 } 1823 }
1771 1824
1772 @override 1825 @override
1773 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 1826 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
1774 for (var v in node.variables.variables) { 1827 for (var v in node.variables.variables) {
1775 _loader.loadDeclaration(v, v.element); 1828 _loader.loadDeclaration(v, v.element);
1776 } 1829 }
1777 } 1830 }
1778 1831
1832 /// Emits static fields.
1833 ///
1834 /// Instance fields are emitted in [_initializeFields].
1835 ///
1836 /// These are generally treated the same as top-level fields, see
1837 /// [visitTopLevelVariableDeclaration].
1838 @override
1839 visitFieldDeclaration(FieldDeclaration node) {
1840 if (!node.isStatic) return;
1841
1842 for (var f in node.fields.variables) {
1843 _loader.loadDeclaration(f, f.element);
1844 }
1845 }
1846
1779 _addExport(String name) { 1847 _addExport(String name) {
1780 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; 1848 if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
1781 } 1849 }
1782 1850
1783 @override 1851 @override
1784 JS.Statement visitVariableDeclarationStatement( 1852 JS.Statement visitVariableDeclarationStatement(
1785 VariableDeclarationStatement node) { 1853 VariableDeclarationStatement node) {
1786 // Special case a single variable with an initializer. 1854 // Special case a single variable with an initializer.
1787 // This helps emit cleaner code for things like: 1855 // This helps emit cleaner code for things like:
1788 // var result = []..add(1)..add(2); 1856 // var result = []..add(1)..add(2);
(...skipping 1190 matching lines...) Expand 10 before | Expand all | Expand 10 after
2979 3047
2980 /// A special kind of element created by the compiler, signifying a temporary 3048 /// A special kind of element created by the compiler, signifying a temporary
2981 /// variable. These objects use instance equality, and should be shared 3049 /// variable. These objects use instance equality, and should be shared
2982 /// everywhere in the tree where they are treated as the same variable. 3050 /// everywhere in the tree where they are treated as the same variable.
2983 class TemporaryVariableElement extends LocalVariableElementImpl { 3051 class TemporaryVariableElement extends LocalVariableElementImpl {
2984 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3052 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
2985 3053
2986 int get hashCode => identityHashCode(this); 3054 int get hashCode => identityHashCode(this);
2987 bool operator ==(Object other) => identical(this, other); 3055 bool operator ==(Object other) => identical(this, other);
2988 } 3056 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698