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

Side by Side Diff: lib/src/codegen/js_codegen.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: fix static fields 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 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; 7 import 'dart:collection' show HashSet, HashMap;
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/engine.dart' show RecordingErrorListener;
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 14 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
14 import 'package:analyzer/src/generated/scanner.dart' 15 import 'package:analyzer/src/generated/scanner.dart'
15 show StringToken, Token, TokenType; 16 show StringToken, Token, TokenType;
16 import 'package:path/path.dart' as path; 17 import 'package:path/path.dart' as path;
17 18
18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; 19 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
19 import 'package:dev_compiler/src/codegen/reify_coercions.dart' 20 import 'package:dev_compiler/src/codegen/reify_coercions.dart'
20 show CoercionReifier; 21 show CoercionReifier;
21 22
22 // TODO(jmesserly): import from its own package 23 // TODO(jmesserly): import from its own package
23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 24 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
24 import 'package:dev_compiler/src/js/js_ast.dart' show js; 25 import 'package:dev_compiler/src/js/js_ast.dart' show js;
25 26
26 import 'package:dev_compiler/src/checker/rules.dart'; 27 import 'package:dev_compiler/src/checker/rules.dart';
27 import 'package:dev_compiler/src/dependency_graph.dart';
28 import 'package:dev_compiler/src/info.dart'; 28 import 'package:dev_compiler/src/info.dart';
29 import 'package:dev_compiler/src/options.dart'; 29 import 'package:dev_compiler/src/options.dart';
30 import 'package:dev_compiler/src/utils.dart'; 30 import 'package:dev_compiler/src/utils.dart';
31 31
32 import 'code_generator.dart'; 32 import 'code_generator.dart';
33 import 'js_field_storage.dart'; 33 import 'js_field_storage.dart';
34 import 'js_names.dart' as JS; 34 import 'js_names.dart' as JS;
35 import 'js_metalet.dart' as JS; 35 import 'js_metalet.dart' as JS;
36 import 'js_module_item_order.dart';
36 import 'js_printer.dart' show writeJsLibrary; 37 import 'js_printer.dart' show writeJsLibrary;
37 import 'side_effect_analysis.dart'; 38 import 'side_effect_analysis.dart';
38 39
39 // Various dynamic helpers we call. 40 // Various dynamic helpers we call.
40 // If renaming these, make sure to check other places like the 41 // If renaming these, make sure to check other places like the
41 // dart_runtime.js file and comments. 42 // dart_runtime.js file and comments.
42 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 43 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
43 // import and generate calls to, rather than dart_runtime.js 44 // import and generate calls to, rather than dart_runtime.js
44 const DPUT = 'dput'; 45 const DPUT = 'dput';
45 const DLOAD = 'dload'; 46 const DLOAD = 'dload';
46 const DINDEX = 'dindex'; 47 const DINDEX = 'dindex';
47 const DSETINDEX = 'dsetindex'; 48 const DSETINDEX = 'dsetindex';
48 const DCALL = 'dcall'; 49 const DCALL = 'dcall';
49 const DSEND = 'dsend'; 50 const DSEND = 'dsend';
50 51
52 class ConstFieldVisitor {
Jennifer Messerly 2015/05/12 16:23:45 I forgot to move this to side_effect_analysis.dart
vsm 2015/05/12 18:00:11 +1 to moving out. Not really js_cg specific.
Jennifer Messerly 2015/05/12 18:47:04 Done.
53 final ConstantVisitor _constantVisitor;
54
55 ConstFieldVisitor(TypeProvider types, CompilationUnit unit)
56 : _constantVisitor = new ConstantVisitor.con1(types,
57 new ErrorReporter(new RecordingErrorListener(), unit.element.source));
58
59 // TODO(jmesserly): this is used to determine if the field initialization is
60 // side effect free. We should make the check more general, as things like
61 // list/map literals/regexp are also side effect free and fairly common
62 // to use as field initializers.
63 bool isFieldInitConstant(VariableDeclaration field) =>
64 field.initializer == null || computeConstant(field) != null;
65
66 DartObjectImpl computeConstant(VariableDeclaration field) {
67 // If the constant is already computed by ConstantEvaluator, just return it.
68 VariableElementImpl element = field.element;
69 var result = element.evaluationResult;
70 if (result != null) return result.value;
71
72 // ConstantEvaluator will not compute constants for non-const fields
73 // at least for cases like `int x = 0;`, so run ConstantVisitor for those.
74 // TODO(jmesserly): ideally we'd only do this if we're sure it was skipped
75 // by ConstantEvaluator.
vsm 2015/05/12 18:00:11 Is that equivalent to only doing this if !field.is
Jennifer Messerly 2015/05/12 18:47:04 Ah, yes. I think so. I'll add an assert in place o
76 var initializer = field.initializer;
77 if (initializer == null) return null;
78 return initializer.accept(_constantVisitor);
79 }
80 }
81
51 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { 82 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
52 final CompilerOptions options; 83 final CompilerOptions options;
53 final TypeRules rules; 84 final TypeRules rules;
54 final LibraryInfo libraryInfo; 85 final LibraryInfo libraryInfo;
55 86
56 /// The global extension method table. 87 /// The global extension method table.
57 final HashMap<String, List<InterfaceType>> _extensionMethods; 88 final HashMap<String, List<InterfaceType>> _extensionMethods;
58 89
59 /// Information that is precomputed for this library, indicates which fields 90 /// Information that is precomputed for this library, indicates which fields
60 /// need storage slots. 91 /// need storage slots.
61 final HashSet<FieldElement> _fieldsNeedingStorage; 92 final HashSet<FieldElement> _fieldsNeedingStorage;
62 93
63 /// The variable for the target of the current `..` cascade expression. 94 /// The variable for the target of the current `..` cascade expression.
64 SimpleIdentifier _cascadeTarget; 95 SimpleIdentifier _cascadeTarget;
65 96
66 /// The variable for the current catch clause 97 /// The variable for the current catch clause
67 SimpleIdentifier _catchParameter; 98 SimpleIdentifier _catchParameter;
68 99
69 ConstantEvaluator _constEvaluator;
70
71 ClassElement _currentClassElement = null;
72
73 /// Imported libraries, and the temporaries used to refer to them. 100 /// Imported libraries, and the temporaries used to refer to them.
74 final _imports = new Map<LibraryElement, JS.TemporaryId>(); 101 final _imports = new Map<LibraryElement, JS.TemporaryId>();
75 final _exports = new Set<String>(); 102 final _exports = new Set<String>();
76 final _lazyFields = <VariableDeclaration>[]; 103 final _lazyFields = <VariableDeclaration>[];
77 final _properties = <FunctionDeclaration>[]; 104 final _properties = <FunctionDeclaration>[];
78 final _privateNames = new HashMap<String, JS.TemporaryId>(); 105 final _privateNames = new HashMap<String, JS.TemporaryId>();
79 final _extensionMethodNames = new HashSet<String>(); 106 final _extensionMethodNames = new HashSet<String>();
80 final _pendingStatements = <JS.Statement>[]; 107 final _moduleItems = <JS.Statement>[];
81 final _temps = new HashMap<Element, JS.TemporaryId>(); 108 final _temps = new HashMap<Element, JS.TemporaryId>.identity();
109 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>();
110 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>();
82 111
83 /// The name for the library's exports inside itself. 112 /// The name for the library's exports inside itself.
84 /// `exports` was chosen as the most similar to ES module patterns. 113 /// `exports` was chosen as the most similar to ES module patterns.
85 final _exportsVar = new JS.TemporaryId('exports'); 114 final _exportsVar = new JS.TemporaryId('exports');
86 final _namedArgTemp = new JS.TemporaryId('opts'); 115 final _namedArgTemp = new JS.TemporaryId('opts');
87 116
88 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or 117 ConstFieldVisitor _constField;
89 /// [ClassTypeAlias].
90 final _pendingClasses = new HashMap<Element, CompilationUnitMember>();
91 118
92 /// Memoized results of [_lazyClass]. 119 ModuleItemLoadOrder _loader;
93 final _lazyClassMemo = new HashMap<Element, bool>();
94
95 /// Memoized results of [_inLibraryCycle].
96 final _libraryCycleMemo = new HashMap<LibraryElement, bool>();
97 120
98 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, 121 JSCodegenVisitor(this.options, this.rules, this.libraryInfo,
99 this._extensionMethods, this._fieldsNeedingStorage); 122 this._extensionMethods, this._fieldsNeedingStorage) {
123 _loader = new ModuleItemLoadOrder(_emitModuleItem);
Jennifer Messerly 2015/05/12 16:23:45 alternate design: this could be a mixin which decl
124 }
100 125
101 LibraryElement get currentLibrary => libraryInfo.library; 126 LibraryElement get currentLibrary => libraryInfo.library;
102 TypeProvider get types => rules.provider; 127 TypeProvider get types => rules.provider;
103 128
104 JS.Program emitLibrary(LibraryUnit library) { 129 JS.Program emitLibrary(LibraryUnit library) {
105 String jsDefaultValue = null; 130 String jsDefaultValue = null;
106 131
107 // Modify the AST to make coercions explicit. 132 // Modify the AST to make coercions explicit.
108 new CoercionReifier(library, rules, options).reify(); 133 new CoercionReifier(library, rules, options).reify();
109 134
110 var unit = library.library; 135 var unit = library.library;
111 if (unit.directives.isNotEmpty) { 136 if (unit.directives.isNotEmpty) {
112 var libraryDir = unit.directives.first; 137 var libraryDir = unit.directives.first;
113 if (libraryDir is LibraryDirective) { 138 if (libraryDir is LibraryDirective) {
114 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); 139 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation);
115 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); 140 jsDefaultValue = getConstantField(jsName, 'name', types.stringType);
116 } 141 }
117 } 142 }
118 if (jsDefaultValue == null) jsDefaultValue = '{}'; 143 if (jsDefaultValue == null) jsDefaultValue = '{}';
119 144
120 var body = <JS.Statement>[]; 145 // TODO(jmesserly): visit scriptTag, directives?
Jennifer Messerly 2015/05/12 16:23:45 this TODO just moved from somewhere else
121 146
122 // Collect classes we need to emit, used for: 147 _loader.collectElements(currentLibrary, library.partsThenLibrary);
123 // * tracks what we've emitted so we don't emit twice 148
124 // * provides a mapping from ClassElement back to the ClassDeclaration.
125 for (var unit in library.partsThenLibrary) { 149 for (var unit in library.partsThenLibrary) {
150 _constField = new ConstFieldVisitor(types, unit);
151
126 for (var decl in unit.declarations) { 152 for (var decl in unit.declarations) {
127 if (decl is ClassDeclaration || 153 if (decl is TopLevelVariableDeclaration) {
128 decl is ClassTypeAlias || 154 _visit(decl);
129 decl is FunctionTypeAlias) { 155 } else {
130 _pendingClasses[decl.element] = decl; 156 _loader.loadDeclaration(decl, decl.element);
157 }
158 if (decl is ClassDeclaration) {
159 // Static fields can be emitted into the top-level code, so they need
160 // to potentially be ordered independently of the class.
161 for (var member in decl.members) {
162 if (member is FieldDeclaration && member.isStatic) {
163 for (var f in member.fields.variables) {
164 _loader.loadDeclaration(f, f.element);
165 }
166 }
167 }
131 } 168 }
132 } 169 }
133 } 170 }
134 171
135 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); 172 // Flush any unwritten fields/properties.
173 _flushLazyFields(_moduleItems);
174 _flushLibraryProperties(_moduleItems);
136 175
137 assert(_pendingClasses.isEmpty); 176 // Mark all qualified names as qualified or not, depending on if they need
177 // to be loaded lazily or not.
178 unqualifyIfNeeded(Element e, JS.MaybeQualifiedId id) {
179 id.setQualified(!_loader.isLoaded(e));
180 }
181 _qualifiedIds.forEach(unqualifyIfNeeded);
182 _qualifiedGenericIds.forEach(unqualifyIfNeeded);
138 183
139 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); 184 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:'));
140 185
141 // TODO(jmesserly): make these immutable in JS? 186 // TODO(jmesserly): make these immutable in JS?
142 for (var name in _exports) { 187 for (var name in _exports) {
143 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); 188 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name]));
144 } 189 }
145 190
146 var name = new JS.Identifier(jsLibraryName(currentLibrary)); 191 var name = new JS.Identifier(jsLibraryName(currentLibrary));
147 192
148 // TODO(jmesserly): it would be great to run the renamer on the body, 193 // TODO(jmesserly): it would be great to run the renamer on the body,
149 // then figure out if we really need each of these parameters. 194 // then figure out if we really need each of these parameters.
150 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 195 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34
151 var program = [ 196 var program = [
152 js.statement('var # = dart.defineLibrary(#, #);', [ 197 js.statement('var # = dart.defineLibrary(#, #);', [
153 name, 198 name,
154 name, 199 name,
155 js.call(jsDefaultValue) 200 js.call(jsDefaultValue)
156 ]) 201 ])
157 ]; 202 ];
158 203
159 var params = [_exportsVar]; 204 var params = [_exportsVar];
160 var args = [name]; 205 var args = [name];
161 _imports.forEach((library, temp) { 206 _imports.forEach((library, temp) {
162 var name = new JS.Identifier(temp.name); 207 var name = new JS.Identifier(temp.name);
163 params.add(temp); 208 params.add(temp);
164 args.add(name); 209 args.add(name);
165 var helper = _libraryMightNotBeLoaded(library) ? 'lazyImport' : 'import'; 210 var helper = _loader.libraryIsLoaded(library) ? 'import' : 'lazyImport';
166 program.add(js.statement('var # = dart.#(#);', [name, helper, name])); 211 program.add(js.statement('var # = dart.#(#);', [name, helper, name]));
167 }); 212 });
168 213
169 program.add(js.statement( 214 program.add(js.statement("(function(#) { 'use strict'; #; })(#);", [
170 "(function(#) { 'use strict'; #; })(#);", [params, body, args])); 215 params,
216 _moduleItems,
217 args
218 ]));
171 219
172 return new JS.Program(program); 220 return new JS.Program(program);
173 } 221 }
174 222
223 void _emitModuleItem(AstNode node) {
224 // Attempt to group adjacent fields/properties.
225 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems);
226 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
227
228 var code = _visit(node);
229 if (code != null) _moduleItems.add(code);
230 }
231
175 JS.Identifier _initSymbol(JS.Identifier id) { 232 JS.Identifier _initSymbol(JS.Identifier id) {
176 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); 233 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
177 _pendingStatements.add(s); 234 _moduleItems.add(s);
178 return id; 235 return id;
179 } 236 }
180 237
181 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 238 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
182 // until we have better name tracking. 239 // until we have better name tracking.
183 String get _SYMBOL { 240 String get _SYMBOL {
184 var name = currentLibrary.name; 241 var name = currentLibrary.name;
185 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; 242 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
186 return 'Symbol'; 243 return 'Symbol';
187 } 244 }
188 245
189 @override
190 JS.Statement visitCompilationUnit(CompilationUnit node) {
191 var source = node.element.source;
192
193 _constEvaluator = new ConstantEvaluator(source, types);
194
195 // TODO(jmesserly): scriptTag, directives.
196 var body = <JS.Statement>[];
197 for (var child in node.declarations) {
198 // Attempt to group adjacent fields/properties.
199 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
200 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
201
202 var code = _visit(child);
203 if (code != null) {
204 _flushPendingStatements(body);
205 body.add(code);
206 }
207 }
208
209 // Flush any unwritten fields/properties.
210 _flushLazyFields(body);
211 _flushLibraryProperties(body);
212
213 assert(_pendingStatements.isEmpty);
214 return _statement(body);
215 }
216
217 bool isPublic(String name) => !name.startsWith('_'); 246 bool isPublic(String name) => !name.startsWith('_');
218 247
219 /// Conversions that we don't handle end up here. 248 /// Conversions that we don't handle end up here.
220 @override 249 @override
221 visitConversion(Conversion node) { 250 visitConversion(Conversion node) {
222 throw 'Unlowered conversion ${node.runtimeType}: $node'; 251 throw 'Unlowered conversion ${node.runtimeType}: $node';
223 } 252 }
224 253
225 @override 254 @override
226 visitAsExpression(AsExpression node) { 255 visitAsExpression(AsExpression node) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 String _jsTypeofName(DartType t) { 302 String _jsTypeofName(DartType t) {
274 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number'; 303 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number';
275 if (rules.isStringType(t)) return 'string'; 304 if (rules.isStringType(t)) return 'string';
276 if (rules.isBoolType(t)) return 'boolean'; 305 if (rules.isBoolType(t)) return 'boolean';
277 return null; 306 return null;
278 } 307 }
279 308
280 @override 309 @override
281 visitFunctionTypeAlias(FunctionTypeAlias node) { 310 visitFunctionTypeAlias(FunctionTypeAlias node) {
282 // If we've already emitted this class, skip it. 311 // If we've already emitted this class, skip it.
283 var type = node.element.type; 312 var element = node.element;
284 if (_pendingClasses.remove(node.element) == null) return null; 313 var type = element.type;
314 var name = element.name;
285 315
286 var name = type.name; 316 _loader.startTopLevel(element);
287 var result = js.statement('let # = dart.typedef(#, () => #);', [ 317 var fnType = js.statement('let # = dart.typedef(#, #);', [
288 new JS.Identifier(name), 318 name,
289 js.string(name, "'"), 319 js.string(name, "'"),
290 _emitTypeName(node.element.type, lowerTypedef: true) 320 _emitTypeName(type, lowerTypedef: true)
291 ]); 321 ]);
322 _loader.finishTopLevel(element);
292 323
293 return _finishClassDef(type, result); 324 return _finishClassDef(type, fnType);
294 } 325 }
295 326
296 @override 327 @override
297 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); 328 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type);
298 329
299 @override 330 @override
300 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { 331 JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
301 // If we've already emitted this class, skip it. 332 // If we've already emitted this class, skip it.
302 var type = node.element.type; 333 var element = node.element;
303 if (_pendingClasses.remove(node.element) == null) return null;
304 334
305 var name = node.name.name;
306 var classDecl = new JS.ClassDeclaration(new JS.ClassExpression( 335 var classDecl = new JS.ClassDeclaration(new JS.ClassExpression(
307 new JS.Identifier(name), _classHeritage(node), [])); 336 new JS.Identifier(element.name), _classHeritage(element), []));
308 337
309 return _finishClassDef(type, classDecl); 338 return _finishClassDef(element.type, classDecl);
310 } 339 }
311 340
312 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { 341 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) {
313 var jsTypeName = getConstantField(jsName, 'name', types.stringType); 342 var jsTypeName = getConstantField(jsName, 'name', types.stringType);
314 343
315 if (jsTypeName != null && jsTypeName != dartClassName) { 344 if (jsTypeName != null && jsTypeName != dartClassName) {
316 // We export the JS type as if it was a Dart type. For example this allows 345 // We export the JS type as if it was a Dart type. For example this allows
317 // `dom.InputElement` to actually be HTMLInputElement. 346 // `dom.InputElement` to actually be HTMLInputElement.
318 // TODO(jmesserly): if we had the JsName on the Element, we could just 347 // TODO(jmesserly): if we had the JsName on the Element, we could just
319 // generate it correctly when we refer to it. 348 // generate it correctly when we refer to it.
320 if (isPublic(dartClassName)) _addExport(dartClassName); 349 if (isPublic(dartClassName)) _addExport(dartClassName);
321 return js.statement('let # = #;', [dartClassName, jsTypeName]); 350 return js.statement('let # = #;', [dartClassName, jsTypeName]);
322 } 351 }
323 return null; 352 return null;
324 } 353 }
325 354
326 @override 355 @override
327 JS.Statement visitClassDeclaration(ClassDeclaration node) { 356 JS.Statement visitClassDeclaration(ClassDeclaration node) {
328 // If we've already emitted this class, skip it. 357 // If we've already emitted this class, skip it.
329 var classElem = node.element; 358 var classElem = node.element;
330 var type = classElem.type; 359 var type = classElem.type;
331 if (_pendingClasses.remove(classElem) == null) return null;
332
333 var jsName = getAnnotationValue(node, _isJsNameAnnotation); 360 var jsName = getAnnotationValue(node, _isJsNameAnnotation);
334 361
335 if (jsName != null) return _emitJsType(node.name.name, jsName); 362 if (jsName != null) return _emitJsType(node.name.name, jsName);
336 363
337 // Set current class
338 assert(_currentClassElement == null);
339 _currentClassElement = classElem;
340
341 var ctors = <ConstructorDeclaration>[]; 364 var ctors = <ConstructorDeclaration>[];
342 var fields = <FieldDeclaration>[]; 365 var fields = <FieldDeclaration>[];
343 var staticFields = <FieldDeclaration>[];
344 for (var member in node.members) { 366 for (var member in node.members) {
345 if (member is ConstructorDeclaration) { 367 if (member is ConstructorDeclaration) {
346 ctors.add(member); 368 ctors.add(member);
347 } else if (member is FieldDeclaration) { 369 } else if (member is FieldDeclaration && !member.isStatic) {
348 (member.isStatic ? staticFields : fields).add(member); 370 fields.add(member);
349 } 371 }
350 } 372 }
351 373
352 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 374 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
353 _classHeritage(node), _emitClassMethods(node, ctors, fields)); 375 _classHeritage(classElem), _emitClassMethods(node, ctors, fields));
354 376
355 String jsPeerName; 377 String jsPeerName;
356 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); 378 var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
357 if (jsPeer != null) { 379 if (jsPeer != null) {
358 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); 380 jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
359 } 381 }
360 382
361 var body = _finishClassMembers( 383 var body =
362 classElem, classExpr, ctors, fields, staticFields, jsPeerName); 384 _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName);
363
364 // Unset current class
365 assert(_currentClassElement == classElem);
366 _currentClassElement = null;
367 385
368 var result = _finishClassDef(type, body); 386 var result = _finishClassDef(type, body);
369 387
370 if (jsPeerName != null) { 388 if (jsPeerName != null) {
371 // This class isn't allowed to be lazy, because we need to set up 389 // This class isn't allowed to be lazy, because we need to set up
372 // the native JS type eagerly at this point. 390 // the native JS type eagerly at this point.
373 // If we wanted to support laziness, we could defer the hookup until 391 // If we wanted to support laziness, we could defer the hookup until
374 // the end of the Dart library cycle load. 392 // the end of the Dart library cycle load.
375 assert(!_lazyClass(type)); 393 assert(_loader.isLoaded(classElem));
376 394
377 // TODO(jmesserly): this copies the dynamic members. 395 // TODO(jmesserly): this copies the dynamic members.
378 // Probably fine for objects coming from JS, but not if we actually 396 // Probably fine for objects coming from JS, but not if we actually
379 // want to support construction of instances with generic types other 397 // want to support construction of instances with generic types other
380 // than dynamic. See issue #154 for Array and List<E> related bug. 398 // than dynamic. See issue #154 for Array and List<E> related bug.
381 var copyMembers = js.statement( 399 var copyMembers = js.statement(
382 'dart.registerExtension(dart.global.#, #);', [ 400 'dart.registerExtension(dart.global.#, #);', [
383 _propertyName(jsPeerName), 401 _propertyName(jsPeerName),
384 classElem.name 402 classElem.name
385 ]); 403 ]);
386 return _statement([result, copyMembers]); 404 return _statement([result, copyMembers]);
387 } 405 }
388 return result; 406 return result;
389 } 407 }
390 408
391 @override 409 @override
392 JS.Statement visitEnumDeclaration(EnumDeclaration node) => 410 JS.Statement visitEnumDeclaration(EnumDeclaration node) =>
393 _unimplementedCall("Unimplemented enum: $node").toStatement(); 411 _unimplementedCall("Unimplemented enum: $node").toStatement();
394 412
395 /// Given a class element and body, complete the class declaration. 413 /// Given a class element and body, complete the class declaration.
396 /// This handles generic type parameters, laziness (in library-cycle cases), 414 /// This handles generic type parameters, laziness (in library-cycle cases),
397 /// and ensuring dependencies are loaded first. 415 /// and ensuring dependencies are loaded first.
398 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { 416 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) {
399 var name = type.name; 417 var name = type.name;
400 var genericName = '$name\$'; 418 var genericName = '$name\$';
401 419
402 JS.Statement genericDef; 420 JS.Statement genericDef = null;
403 JS.Expression genericInst;
404 if (type.typeParameters.isNotEmpty) { 421 if (type.typeParameters.isNotEmpty) {
405 genericDef = _emitGenericClassDef(type, body); 422 genericDef = _emitGenericClassDef(type, body);
406 var target = genericName;
407 if (_needQualifiedName(type.element)) {
408 target = js.call('#.#', [_exportsVar, genericName]);
409 }
410 genericInst = js.call('#()', [target]);
411 } 423 }
412 424
413 // The base class and all mixins must be declared before this class. 425 // The base class and all mixins must be declared before this class.
414 if (_lazyClass(type)) { 426 if (!_loader.isLoaded(type.element)) {
415 // TODO(jmesserly): the lazy class def is a simple solution for now. 427 // TODO(jmesserly): the lazy class def is a simple solution for now.
416 // We may want to consider other options in the future. 428 // We may want to consider other options in the future.
417 429
418 if (genericDef != null) { 430 if (genericDef != null) {
419 return js.statement( 431 return js.statement(
420 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ 432 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [
421 genericDef, 433 genericDef,
422 _exportsVar, 434 _exportsVar,
423 _propertyName(name), 435 _propertyName(name),
424 genericName 436 genericName
425 ]); 437 ]);
426 } 438 }
427 439
428 return js.statement( 440 return js.statement(
429 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ 441 'dart.defineLazyClass(#, { get #() { #; return #; } });', [
430 _exportsVar, 442 _exportsVar,
431 _propertyName(name), 443 _propertyName(name),
432 body, 444 body,
433 name 445 name
434 ]); 446 ]);
435 } 447 }
436 448
437 if (isPublic(name)) _addExport(name); 449 if (isPublic(name)) _addExport(name);
438 450
439 if (genericDef != null) { 451 if (genericDef != null) {
440 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); 452 var dynType = fillDynamicTypeArgs(type, types);
453 var genericInst = _emitTypeName(dynType, lowerGeneric: true);
454 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]);
441 } 455 }
442 456 return body;
443 if (type.isObject) return body;
444
445 // If we're not lazy, we still need to ensure our dependencies are
446 // generated first.
447 var classDefs = <JS.Statement>[];
448 if (type is InterfaceType) {
449 _emitClassIfNeeded(classDefs, type.superclass);
450 for (var m in type.element.mixins) {
451 _emitClassIfNeeded(classDefs, m);
452 }
453 } else if (type is FunctionType) {
454 _emitClassIfNeeded(classDefs, types.functionType);
455 }
456 classDefs.add(body);
457 return _statement(classDefs);
458 }
459
460 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) {
461 // We can only emit classes from this library.
462 if (base.element.library != currentLibrary) return;
463
464 var baseNode = _pendingClasses[base.element];
465 if (baseNode != null) defs.add(visitClassDeclaration(baseNode));
466 }
467
468 /// Returns true if the supertype or mixins aren't loaded.
469 /// If that is the case, we'll emit a lazy class definition.
470 bool _lazyClass(DartType type) {
471 if (type.isObject) return false;
472
473 // Use the element as the key, as those are unique whereas generic types
474 // can have their arguments substituted.
475 assert(type.element.library == currentLibrary);
476 var result = _lazyClassMemo[type.element];
477 if (result != null) return result;
478
479 if (type is InterfaceType) {
480 result = _typeMightNotBeLoaded(type.superclass) ||
481 type.mixins.any(_typeMightNotBeLoaded);
482 } else if (type is FunctionType) {
483 result = _typeMightNotBeLoaded(types.functionType);
484 }
485 return _lazyClassMemo[type.element] = result;
486 }
487
488 /// Returns true if the class might not be loaded.
489 ///
490 /// If the class is from our library, this can happen because it's lazy.
491 ///
492 /// If the class is from a different library, it could happen if we're in
493 /// a library cycle. In other words, if that different library depends back
494 /// on this library via some transitive import path.
495 ///
496 /// If we could control the global import ordering, we could eliminate some
497 /// of these cases, by ordering the imports of the cyclic libraries in an
498 /// optimal way. For example, we could order the libraries in a cycle to
499 /// minimize laziness. However, we currently assume we cannot control the
500 /// order that the cycle of libraries will be loaded in.
501 bool _typeMightNotBeLoaded(DartType type) {
502 var library = type.element.library;
503 if (library == currentLibrary) return _lazyClass(type);
504 return _libraryMightNotBeLoaded(library);
505 }
506
507 bool _libraryMightNotBeLoaded(LibraryElement library) {
508 // The SDK is a special case: we optimize the order to prevent laziness.
509 if (library.isInSdk) {
510 // SDK is loaded before non-SDK libraies
511 if (!currentLibrary.isInSdk) return false;
512
513 // Compute the order of both SDK libraries. If unknown, assume it's after.
514 var classOrder = corelibOrder.indexOf(library.name);
515 if (classOrder == -1) classOrder = corelibOrder.length;
516
517 var currentOrder = corelibOrder.indexOf(currentLibrary.name);
518 if (currentOrder == -1) currentOrder = corelibOrder.length;
519
520 // If the dart:* library we are currently compiling is loaded after the
521 // class's library, then we know the class is available.
522 if (classOrder != currentOrder) return currentOrder < classOrder;
523
524 // If we don't know the order of the class's library or the current
525 // library, do the normal cycle check. (Not all SDK libs are cycles.)
526 }
527
528 return _inLibraryCycle(library);
529 }
530
531 /// Returns true if [library] depends on the [currentLibrary] via some
532 /// transitive import.
533 bool _inLibraryCycle(LibraryElement library) {
534 // SDK libs don't depend on things outside the SDK.
535 // (We can reach this via the recursive call below.)
536 if (library.isInSdk && !currentLibrary.isInSdk) return false;
537
538 var result = _libraryCycleMemo[library];
539 if (result != null) return result;
540
541 result = library == currentLibrary;
542 _libraryCycleMemo[library] = result;
543 for (var e in library.imports) {
544 if (result) break;
545 result = _inLibraryCycle(e.importedLibrary);
546 }
547 for (var e in library.exports) {
548 if (result) break;
549 result = _inLibraryCycle(e.exportedLibrary);
550 }
551 return _libraryCycleMemo[library] = result;
552 } 457 }
553 458
554 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { 459 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) {
555 var name = type.name; 460 var name = type.name;
556 var genericName = '$name\$'; 461 var genericName = '$name\$';
557 var typeParams = type.typeParameters.map((p) => p.name); 462 var typeParams = type.typeParameters.map((p) => p.name);
558 if (isPublic(name)) _exports.add(genericName); 463 if (isPublic(name)) _exports.add(genericName);
559 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ 464 return js.statement('let # = dart.generic(function(#) { #; return #; });', [
560 genericName, 465 genericName,
561 typeParams, 466 typeParams,
562 body, 467 body,
563 name 468 name
564 ]); 469 ]);
565 } 470 }
566 471
567 JS.Expression _classHeritage(node) { 472 JS.Expression _classHeritage(ClassElement element) {
568 if (node.element.type.isObject) return null; 473 var type = element.type;
474 if (type.isObject) return null;
569 475
570 DartType supertype; 476 // Assume we can load eagerly, until proven otherwise.
571 if (node is ClassDeclaration) { 477 _loader.startTopLevel(element);
572 var ext = node.extendsClause;
573 supertype = ext != null ? ext.superclass.type : types.objectType;
574 } else {
575 supertype = (node as ClassTypeAlias).superclass.type;
576 }
577 478
578 JS.Expression heritage = _emitTypeName(supertype); 479 JS.Expression heritage = _emitTypeName(type.superclass);
579 480 if (type.mixins.isNotEmpty) {
580 if (node.withClause != null) { 481 var mixins = type.mixins.map(_emitTypeName).toList();
581 var mixins = _visitList(node.withClause.mixinTypes);
582 mixins.insert(0, heritage); 482 mixins.insert(0, heritage);
583 heritage = js.call('dart.mixin(#)', [mixins]); 483 heritage = js.call('dart.mixin(#)', [mixins]);
584 } 484 }
585 485
486 _loader.finishTopLevel(element);
586 return heritage; 487 return heritage;
587 } 488 }
588 489
589 List<JS.Method> _emitClassMethods(ClassDeclaration node, 490 List<JS.Method> _emitClassMethods(ClassDeclaration node,
590 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { 491 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
591 var element = node.element; 492 var element = node.element;
592 var type = element.type; 493 var type = element.type;
593 var isObject = type.isObject; 494 var isObject = type.isObject;
594 var name = node.name.name; 495 var name = node.name.name;
595 496
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call( 557 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call(
657 'function() { return new dart.JsIterator(this.#); }', 558 'function() { return new dart.JsIterator(this.#); }',
658 [_emitMemberName('iterator', type: t)])); 559 [_emitMemberName('iterator', type: t)]));
659 } 560 }
660 561
661 /// Emit class members that need to come after the class declaration, such 562 /// Emit class members that need to come after the class declaration, such
662 /// as static fields. See [_emitClassMethods] for things that are emitted 563 /// as static fields. See [_emitClassMethods] for things that are emitted
663 /// inside the ES6 `class { ... }` node. 564 /// inside the ES6 `class { ... }` node.
664 JS.Statement _finishClassMembers(ClassElement classElem, 565 JS.Statement _finishClassMembers(ClassElement classElem,
665 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 566 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
666 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields, 567 List<FieldDeclaration> fields, String jsPeerName) {
667 String jsPeerName) {
668 var name = classElem.name; 568 var name = classElem.name;
669 var body = <JS.Statement>[]; 569 var body = <JS.Statement>[];
670 body.add(new JS.ClassDeclaration(cls)); 570 body.add(new JS.ClassDeclaration(cls));
671 571
672 // TODO(jmesserly): we should really just extend native Array. 572 // TODO(jmesserly): we should really just extend native Array.
673 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { 573 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
674 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ 574 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [
675 classElem.name, 575 classElem.name,
676 _propertyName(jsPeerName) 576 _propertyName(jsPeerName)
677 ])); 577 ]));
(...skipping 21 matching lines...) Expand all
699 // Instance fields, if they override getter/setter pairs 599 // Instance fields, if they override getter/setter pairs
700 for (FieldDeclaration member in fields) { 600 for (FieldDeclaration member in fields) {
701 for (VariableDeclaration fieldDecl in member.fields.variables) { 601 for (VariableDeclaration fieldDecl in member.fields.variables) {
702 var field = fieldDecl.element; 602 var field = fieldDecl.element;
703 if (_fieldsNeedingStorage.contains(field)) { 603 if (_fieldsNeedingStorage.contains(field)) {
704 body.add(_overrideField(field)); 604 body.add(_overrideField(field));
705 } 605 }
706 } 606 }
707 } 607 }
708 608
709 // Static fields
710 var lazyStatics = <VariableDeclaration>[];
711 for (FieldDeclaration member in staticFields) {
712 for (VariableDeclaration field in member.fields.variables) {
713 var fieldName = field.name.name;
714 if ((field.isConst || _isFieldInitConstant(field)) &&
715 !JS.invalidStaticFieldName(fieldName)) {
716 var init = _visit(field.initializer);
717 if (init == null) init = new JS.LiteralNull();
718 body.add(js.statement('#.# = #;', [name, fieldName, init]));
719 } else {
720 lazyStatics.add(field);
721 }
722 }
723 }
724 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics);
725 if (lazy != null) body.add(lazy);
726 return _statement(body); 609 return _statement(body);
727 } 610 }
728 611
729 JS.Statement _overrideField(FieldElement e) { 612 JS.Statement _overrideField(FieldElement e) {
730 var cls = e.enclosingElement; 613 var cls = e.enclosingElement;
731 return js.statement('dart.virtualField(#, #)', [ 614 return js.statement('dart.virtualField(#, #)', [
732 cls.name, 615 cls.name,
733 _emitMemberName(e.name, type: cls.type) 616 _emitMemberName(e.name, type: cls.type)
734 ]); 617 ]);
735 } 618 }
736 619
737 /// Generates the implicit default constructor for class C of the form 620 /// Generates the implicit default constructor for class C of the form
738 /// `C() : super() {}`. 621 /// `C() : super() {}`.
739 JS.Method _emitImplicitConstructor( 622 JS.Method _emitImplicitConstructor(
740 ClassDeclaration node, String name, List<FieldDeclaration> fields) { 623 ClassDeclaration node, String name, List<FieldDeclaration> fields) {
741 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); 624 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty);
742 625
743 // If we don't have a method body, skip this. 626 // If we don't have a method body, skip this.
744 var superCall = _superConstructorCall(node); 627 var superCall = _superConstructorCall(node);
745 if (fields.isEmpty && superCall == null) return null; 628 if (fields.isEmpty && superCall == null) return null;
746 629
747 dynamic body = _initializeFields(fields); 630 dynamic body = _initializeFields(node, fields);
748 if (superCall != null) body = [[body, superCall]]; 631 if (superCall != null) body = [[body, superCall]];
749 return new JS.Method( 632 return new JS.Method(
750 _propertyName(name), js.call('function() { #; }', body)); 633 _propertyName(name), js.call('function() { #; }', body));
751 } 634 }
752 635
753 JS.Method _emitConstructor(ConstructorDeclaration node, String className, 636 JS.Method _emitConstructor(ConstructorDeclaration node, String className,
754 List<FieldDeclaration> fields, bool isObject) { 637 List<FieldDeclaration> fields, bool isObject) {
755 if (_externalOrNative(node)) return null; 638 if (_externalOrNative(node)) return null;
756 639
757 var name = _constructorName(className, node.name); 640 var name = _constructorName(className, node.name);
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
826 body.add(_visit(redirectCall)); 709 body.add(_visit(redirectCall));
827 return new JS.Block(body); 710 return new JS.Block(body);
828 } 711 }
829 712
830 // Initializers only run for non-factory constructors. 713 // Initializers only run for non-factory constructors.
831 if (node.factoryKeyword == null) { 714 if (node.factoryKeyword == null) {
832 // Generate field initializers. 715 // Generate field initializers.
833 // These are expanded into each non-redirecting constructor. 716 // These are expanded into each non-redirecting constructor.
834 // In the future we may want to create an initializer function if we have 717 // In the future we may want to create an initializer function if we have
835 // multiple constructors, but it needs to be balanced against readability. 718 // multiple constructors, but it needs to be balanced against readability.
836 body.add(_initializeFields(fields, node.parameters, node.initializers)); 719 body.add(_initializeFields(node, fields));
837 720
838 var superCall = node.initializers.firstWhere( 721 var superCall = node.initializers.firstWhere(
839 (i) => i is SuperConstructorInvocation, orElse: () => null); 722 (i) => i is SuperConstructorInvocation, orElse: () => null);
840 723
841 // If no superinitializer is provided, an implicit superinitializer of the 724 // If no superinitializer is provided, an implicit superinitializer of the
842 // form `super()` is added at the end of the initializer list, unless the 725 // form `super()` is added at the end of the initializer list, unless the
843 // enclosing class is class Object. 726 // enclosing class is class Object.
844 var jsSuper = _superConstructorCall(node.parent, superCall); 727 var jsSuper = _superConstructorCall(node.parent, superCall);
845 if (jsSuper != null) body.add(jsSuper); 728 if (jsSuper != null) body.add(jsSuper);
846 } 729 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
890 if (!e.unnamedConstructor.isSynthetic) return true; 773 if (!e.unnamedConstructor.isSynthetic) return true;
891 return e.fields.any((f) => !f.isStatic && !f.isSynthetic); 774 return e.fields.any((f) => !f.isStatic && !f.isSynthetic);
892 } 775 }
893 776
894 /// Initialize fields. They follow the sequence: 777 /// Initialize fields. They follow the sequence:
895 /// 778 ///
896 /// 1. field declaration initializer if non-const, 779 /// 1. field declaration initializer if non-const,
897 /// 2. field initializing parameters, 780 /// 2. field initializing parameters,
898 /// 3. constructor field initializers, 781 /// 3. constructor field initializers,
899 /// 4. initialize fields not covered in 1-3 782 /// 4. initialize fields not covered in 1-3
900 JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls, 783 JS.Statement _initializeFields(
901 [FormalParameterList parameters, 784 AstNode node, List<FieldDeclaration> fieldDecls) {
902 NodeList<ConstructorInitializer> initializers]) { 785 var unit = node.getAncestor((a) => a is CompilationUnit);
786 var constField = new ConstFieldVisitor(types, unit);
903 787
904 // Run field initializers if they can have side-effects. 788 // Run field initializers if they can have side-effects.
905 var fields = new Map<FieldElement, JS.Expression>(); 789 var fields = new Map<FieldElement, JS.Expression>();
906 var unsetFields = new Map<FieldElement, VariableDeclaration>(); 790 var unsetFields = new Map<FieldElement, VariableDeclaration>();
907 for (var declaration in fieldDecls) { 791 for (var declaration in fieldDecls) {
908 for (var fieldNode in declaration.fields.variables) { 792 for (var fieldNode in declaration.fields.variables) {
909 var element = fieldNode.element; 793 var element = fieldNode.element;
910 if (_isFieldInitConstant(fieldNode)) { 794 if (constField.isFieldInitConstant(fieldNode)) {
911 unsetFields[element] = fieldNode; 795 unsetFields[element] = fieldNode;
912 } else { 796 } else {
913 fields[element] = _visitInitializer(fieldNode); 797 fields[element] = _visitInitializer(fieldNode);
914 } 798 }
915 } 799 }
916 } 800 }
917 801
918 // Initialize fields from `this.fieldName` parameters. 802 // Initialize fields from `this.fieldName` parameters.
919 if (parameters != null) { 803 if (node is ConstructorDeclaration) {
804 var parameters = node.parameters;
805 var initializers = node.initializers;
806
920 for (var p in parameters.parameters) { 807 for (var p in parameters.parameters) {
921 var element = p.element; 808 var element = p.element;
922 if (element is FieldFormalParameterElement) { 809 if (element is FieldFormalParameterElement) {
923 fields[element.field] = _visit(p); 810 fields[element.field] = _visit(p);
924 } 811 }
925 } 812 }
926 }
927 813
928 // Run constructor field initializers such as `: foo = bar.baz` 814 // Run constructor field initializers such as `: foo = bar.baz`
929 if (initializers != null) {
930 for (var init in initializers) { 815 for (var init in initializers) {
931 if (init is ConstructorFieldInitializer) { 816 if (init is ConstructorFieldInitializer) {
932 fields[init.fieldName.staticElement] = _visit(init.expression); 817 fields[init.fieldName.staticElement] = _visit(init.expression);
933 } 818 }
934 } 819 }
935 } 820 }
936 821
937 for (var f in fields.keys) unsetFields.remove(f); 822 for (var f in fields.keys) unsetFields.remove(f);
938 823
939 // Initialize all remaining fields 824 // Initialize all remaining fields
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
1122 var accessor = node.staticElement; 1007 var accessor = node.staticElement;
1123 if (accessor == null) { 1008 if (accessor == null) {
1124 return js.commentExpression( 1009 return js.commentExpression(
1125 'Unimplemented unknown name', new JS.Identifier(node.name)); 1010 'Unimplemented unknown name', new JS.Identifier(node.name));
1126 } 1011 }
1127 1012
1128 // Get the original declaring element. If we had a property accessor, this 1013 // Get the original declaring element. If we had a property accessor, this
1129 // indirects back to a (possibly synthetic) field. 1014 // indirects back to a (possibly synthetic) field.
1130 var element = accessor; 1015 var element = accessor;
1131 if (element is PropertyAccessorElement) element = accessor.variable; 1016 if (element is PropertyAccessorElement) element = accessor.variable;
1017
1018 _loader.declareBeforeUse(element);
1019
1132 var name = element.name; 1020 var name = element.name;
1133 1021
1134 // library member 1022 // library member
1135 if (element.enclosingElement is CompilationUnitElement && 1023 if (element.enclosingElement is CompilationUnitElement) {
1136 (element.library != currentLibrary || 1024 return _maybeQualifiedName(
1137 element is TopLevelVariableElement && !element.isConst)) { 1025 element, _emitMemberName(name, isStatic: true));
1138 var memberName = _emitMemberName(name, isStatic: true);
1139 return js.call('#.#', [_libraryName(element.library), memberName]);
1140 } 1026 }
1141 1027
1142 // Unqualified class member. This could mean implicit-this, or implicit 1028 // Unqualified class member. This could mean implicit-this, or implicit
1143 // call to a static from the same class. 1029 // call to a static from the same class.
1144 if (element is ClassMemberElement && element is! ConstructorElement) { 1030 if (element is ClassMemberElement && element is! ConstructorElement) {
1145 bool isStatic = element.isStatic; 1031 bool isStatic = element.isStatic;
1146 var type = element.enclosingElement.type; 1032 var type = element.enclosingElement.type;
1147 var member = _emitMemberName(name, isStatic: isStatic, type: type); 1033 var member = _emitMemberName(name, isStatic: isStatic, type: type);
1148 1034
1149 // For static methods, we add the raw type name, without generics or 1035 // For static methods, we add the raw type name, without generics or
1150 // library prefix. We don't need those because static calls can't use 1036 // library prefix. We don't need those because static calls can't use
1151 // the generic type. 1037 // the generic type.
1152 if (isStatic) { 1038 if (isStatic) {
1153 return js.call('#.#', [type.name, member]); 1039 var dynType = _emitTypeName(fillDynamicTypeArgs(type, types));
1040 return new JS.PropertyAccess(dynType, member);
1154 } 1041 }
1155 1042
1156 // For instance members, we add implicit-this. 1043 // For instance members, we add implicit-this.
1157 // For method tear-offs, we ensure it's a bound method. 1044 // For method tear-offs, we ensure it's a bound method.
1158 var code = 'this.#'; 1045 var code = 'this.#';
1159 if (element is MethodElement && !inInvocationContext(node)) { 1046 if (element is MethodElement && !inInvocationContext(node)) {
1160 code += '.bind(this)'; 1047 code += '.bind(this)';
1161 } 1048 }
1162 return js.call(code, member); 1049 return js.call(code, member);
1163 } 1050 }
(...skipping 29 matching lines...) Expand all
1193 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { 1080 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) {
1194 var properties = <JS.Property>[]; 1081 var properties = <JS.Property>[];
1195 types.forEach((name, type) { 1082 types.forEach((name, type) {
1196 var key = new JS.LiteralString(name); 1083 var key = new JS.LiteralString(name);
1197 var value = _emitTypeName(type); 1084 var value = _emitTypeName(type);
1198 properties.add(new JS.Property(key, value)); 1085 properties.add(new JS.Property(key, value));
1199 }); 1086 });
1200 return new JS.ObjectInitializer(properties); 1087 return new JS.ObjectInitializer(properties);
1201 } 1088 }
1202 1089
1203 JS.Expression _emitTypeName(DartType type, {bool lowerTypedef: false}) { 1090 /// Emits a Dart [type] into code.
1091 ///
1092 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a
1093 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form
1094 /// will be used instead of `List`. These flags are used when generating
1095 /// the definitions for typedefs and generic types, respectively.
1096 JS.Expression _emitTypeName(DartType type,
1097 {bool lowerTypedef: false, bool lowerGeneric: false}) {
1098
1204 // The void and dynamic types are not defined in core. 1099 // The void and dynamic types are not defined in core.
1205 if (type.isVoid) { 1100 if (type.isVoid) {
1206 return js.call('dart.void'); 1101 return js.call('dart.void');
1207 } else if (type.isDynamic) { 1102 } else if (type.isDynamic) {
1208 return js.call('dart.dynamic'); 1103 return js.call('dart.dynamic');
1209 } 1104 }
1210 1105
1106 _loader.declareBeforeUse(type.element);
1107
1108 // TODO(jmesserly): like constants, should we hoist function types out of
1109 // methods? Similar issue with generic types. For all of these, we may want
1110 // to canonicalize them too, at least when inside the same library.
1211 var name = type.name; 1111 var name = type.name;
1212 var element = type.element; 1112 var element = type.element;
1213 if (name == '' || lowerTypedef && type is FunctionType) { 1113 if (name == '' || lowerTypedef) {
1214 if (type is FunctionType) { 1114 var fnType = type as FunctionType;
Jennifer Messerly 2015/05/12 16:23:45 if we get here, type has to be FunctionType.
1215 var returnType = type.returnType; 1115 var returnType = fnType.returnType;
1216 var parameterTypes = type.normalParameterTypes; 1116 var parameterTypes = fnType.normalParameterTypes;
1217 var optionalTypes = type.optionalParameterTypes; 1117 var optionalTypes = fnType.optionalParameterTypes;
1218 var namedTypes = type.namedParameterTypes; 1118 var namedTypes = fnType.namedParameterTypes;
1219 if (namedTypes.isEmpty) { 1119 if (namedTypes.isEmpty) {
1220 if (optionalTypes.isEmpty) { 1120 if (optionalTypes.isEmpty) {
1221 return js.call('dart.functionType(#, #)', [ 1121 return js.call('dart.functionType(#, #)', [
1222 _emitTypeName(returnType), 1122 _emitTypeName(returnType),
1223 _emitTypeNames(parameterTypes) 1123 _emitTypeNames(parameterTypes)
1224 ]); 1124 ]);
1225 } else {
1226 return js.call('dart.functionType(#, #, #)', [
1227 _emitTypeName(returnType),
1228 _emitTypeNames(parameterTypes),
1229 _emitTypeNames(optionalTypes)
1230 ]);
1231 }
1232 } else { 1125 } else {
1233 assert(optionalTypes.isEmpty);
1234 return js.call('dart.functionType(#, #, #)', [ 1126 return js.call('dart.functionType(#, #, #)', [
1235 _emitTypeName(returnType), 1127 _emitTypeName(returnType),
1236 _emitTypeNames(parameterTypes), 1128 _emitTypeNames(parameterTypes),
1237 _emitTypeProperties(namedTypes) 1129 _emitTypeNames(optionalTypes)
1238 ]); 1130 ]);
1239 } 1131 }
1132 } else {
1133 assert(optionalTypes.isEmpty);
1134 return js.call('dart.functionType(#, #, #)', [
1135 _emitTypeName(returnType),
1136 _emitTypeNames(parameterTypes),
1137 _emitTypeProperties(namedTypes)
1138 ]);
1240 } 1139 }
1241 // TODO(jmesserly): remove when we're using coercion reifier.
1242 return _unimplementedCall('Unimplemented type $type');
1243 } 1140 }
1244 1141
1245 var typeArgs = null; 1142 if (type is TypeParameterType) {
1143 return new JS.Identifier(name);
1144 }
1145
1246 if (type is ParameterizedType) { 1146 if (type is ParameterizedType) {
1247 var args = type.typeArguments; 1147 var args = type.typeArguments;
1248 var isCurrentClass = 1148 var isCurrentClass =
1249 type is InterfaceType ? type.element == _currentClassElement : false; 1149 args.isNotEmpty && _loader.isCurrentElement(type.element);
1150 Iterable jsArgs = null;
1250 if (args.any((a) => a != types.dynamicType)) { 1151 if (args.any((a) => a != types.dynamicType)) {
1251 name = '$name\$'; 1152 jsArgs = args.map(_emitTypeName);
1252 typeArgs = args.map(_emitTypeName); 1153 } else if (lowerGeneric || isCurrentClass) {
1253 } else if (args.isNotEmpty && isCurrentClass) {
1254 // When creating a `new S<dynamic>` we try and use the raw form 1154 // When creating a `new S<dynamic>` we try and use the raw form
1255 // `new S()`, but this does not work if we're inside the same class, 1155 // `new S()`, but this does not work if we're inside the same class,
1256 // because `S` refers to the current S<T> we are generating. 1156 // because `S` refers to the current S<T> we are generating.
1257 name = '$name\$'; 1157 jsArgs = [];
1258 typeArgs = []; 1158 }
1159 if (jsArgs != null) {
1160 var genericName = _maybeQualifiedName(
1161 element, _propertyName('$name\$'), _qualifiedGenericIds);
1162 return js.call('#(#)', [genericName, jsArgs]);
1259 } 1163 }
1260 } 1164 }
1261 1165
1262 JS.Expression result; 1166 return _maybeQualifiedName(element, _propertyName(name));
1263 if (_needQualifiedName(element)) { 1167 }
1264 result = js.call('#.#', [_libraryName(element.library), name]); 1168
1265 } else { 1169 JS.Expression _maybeQualifiedName(Element e, JS.Expression name,
Jennifer Messerly 2015/05/12 16:23:45 we can now make name qualification decisions after
1266 result = new JS.Identifier(name); 1170 [Map<Element, JS.MaybeQualifiedId> idTable]) {
1171 var libName = _libraryName(e.library);
1172 if (idTable == null) idTable = _qualifiedIds;
1173
1174 // Mutable top-level fields should always be qualified.
1175 bool mutableTopLevel = e is TopLevelVariableElement && !e.isConst;
1176 if (e.library != currentLibrary || mutableTopLevel) {
1177 return new JS.PropertyAccess(libName, name);
1267 } 1178 }
1268 1179
1269 if (typeArgs != null) { 1180 return idTable.putIfAbsent(e, () => new JS.MaybeQualifiedId(libName, name));
1270 result = js.call('#(#)', [result, typeArgs]);
1271 }
1272 return result;
1273 }
1274
1275 bool _needQualifiedName(Element element) {
1276 var lib = element.library;
1277 if (lib == null) return false;
1278 if (lib != currentLibrary) return true;
1279 if (element is ClassElement) return _lazyClass(element.type);
1280 if (element is FunctionTypeAliasElement) return _lazyClass(element.type);
1281 return false;
1282 } 1181 }
1283 1182
1284 @override 1183 @override
1285 JS.Expression visitAssignmentExpression(AssignmentExpression node) { 1184 JS.Expression visitAssignmentExpression(AssignmentExpression node) {
1286 var left = node.leftHandSide; 1185 var left = node.leftHandSide;
1287 var right = node.rightHandSide; 1186 var right = node.rightHandSide;
1288 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); 1187 if (node.operator.type == TokenType.EQ) return _emitSet(left, right);
1289 return _emitOpAssign( 1188 return _emitOpAssign(
1290 left, right, node.operator.lexeme[0], node.staticElement, 1189 left, right, node.operator.lexeme[0], node.staticElement,
1291 context: node); 1190 context: node);
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
1481 1380
1482 @override 1381 @override
1483 JS.Statement visitReturnStatement(ReturnStatement node) { 1382 JS.Statement visitReturnStatement(ReturnStatement node) {
1484 var e = node.expression; 1383 var e = node.expression;
1485 if (e == null) return new JS.Return(); 1384 if (e == null) return new JS.Return();
1486 return _visit(e).toReturn(); 1385 return _visit(e).toReturn();
1487 } 1386 }
1488 1387
1489 @override 1388 @override
1490 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 1389 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
1491 var body = <JS.Statement>[]; 1390 for (var v in node.variables.variables) {
1492 1391 _loader.loadDeclaration(v, v.element);
1493 for (var field in node.variables.variables) {
1494 if (field.isConst) {
1495 // constant fields don't change, so we can generate them as `let`
1496 // but add them to the module's exports
1497 var name = field.name.name;
1498 body.add(js.statement(
1499 'let # = #;', [new JS.Identifier(name), _visitInitializer(field)]));
1500 if (isPublic(name)) _addExport(name);
1501 } else if (_isFieldInitConstant(field)) {
1502 body.add(js.statement(
1503 '# = #;', [_visit(field.name), _visitInitializer(field)]));
1504 } else {
1505 _lazyFields.add(field);
1506 }
1507 } 1392 }
1508
1509 return _statement(body);
1510 } 1393 }
1511 1394
1512 _addExport(String name) { 1395 _addExport(String name) {
1513 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; 1396 if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
1514 } 1397 }
1515 1398
1516 @override 1399 @override
1517 JS.Statement visitVariableDeclarationStatement( 1400 JS.Statement visitVariableDeclarationStatement(
1518 VariableDeclarationStatement node) { 1401 VariableDeclarationStatement node) {
1519 // Special case a single variable with an initializer. 1402 // Special case a single variable with an initializer.
1520 // This helps emit cleaner code for things like: 1403 // This helps emit cleaner code for things like:
1521 // var result = []..add(1)..add(2); 1404 // var result = []..add(1)..add(2);
1522 if (node.variables.variables.length == 1) { 1405 if (node.variables.variables.length == 1) {
1523 var v = node.variables.variables.single; 1406 var v = node.variables.variables.single;
1524 if (v.initializer != null) { 1407 if (v.initializer != null) {
1525 var name = new JS.Identifier(v.name.name); 1408 var name = new JS.Identifier(v.name.name);
1526 return _visit(v.initializer).toVariableDeclaration(name); 1409 return _visit(v.initializer).toVariableDeclaration(name);
1527 } 1410 }
1528 } 1411 }
1529 return _visit(node.variables).toStatement(); 1412 return _visit(node.variables).toStatement();
1530 } 1413 }
1531 1414
1532 @override 1415 @override
1533 visitVariableDeclarationList(VariableDeclarationList node) { 1416 visitVariableDeclarationList(VariableDeclarationList node) {
1534 return new JS.VariableDeclarationList('let', _visitList(node.variables)); 1417 return new JS.VariableDeclarationList('let', _visitList(node.variables));
1535 } 1418 }
1536 1419
1537 @override 1420 @override
1538 JS.VariableInitialization visitVariableDeclaration(VariableDeclaration node) { 1421 visitVariableDeclaration(VariableDeclaration node) {
1422 if (node.element is PropertyInducingElement) return _emitStaticField(node);
1423
1539 var name = new JS.Identifier(node.name.name); 1424 var name = new JS.Identifier(node.name.name);
1540 return new JS.VariableInitialization(name, _visitInitializer(node)); 1425 return new JS.VariableInitialization(name, _visitInitializer(node));
1541 } 1426 }
1542 1427
1428 /// Emits a static or top-level field.
1429 JS.Statement _emitStaticField(VariableDeclaration field) {
1430 PropertyInducingElement element = field.element;
1431 assert(element.isStatic);
1432
1433 bool eagerInit;
1434 JS.Expression jsInit;
1435 if (field.isConst || _constField.isFieldInitConstant(field)) {
1436 // If the field is constant, try and generate it at the top level.
1437 _loader.startTopLevel(element);
1438 jsInit = _visitInitializer(field);
1439 _loader.finishTopLevel(element);
1440 eagerInit = _loader.isLoaded(element);
1441 } else {
1442 jsInit = _visitInitializer(field);
1443 eagerInit = false;
1444 }
1445
1446 var fieldName = field.name.name;
1447 if (field.isConst && eagerInit && element is TopLevelVariableElement) {
1448 // constant fields don't change, so we can generate them as `let`
1449 // but add them to the module's exports. However, make sure we generate
1450 // anything they depend on first.
1451
1452 if (isPublic(fieldName)) _addExport(fieldName);
1453 return js.statement('let # = #;', [new JS.Identifier(fieldName), jsInit]);
1454 }
1455
1456 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
1457 return js.statement('# = #;', [_visit(field.name), jsInit]);
1458 }
1459
1460 var body = [];
1461 if (_lazyFields.isNotEmpty) {
1462 var existingTarget = _lazyFields[0].element.enclosingElement;
1463 if (existingTarget != element.enclosingElement) {
1464 _flushLazyFields(body);
1465 }
1466 }
1467
1468 _lazyFields.add(field);
1469
1470 return _statement(body);
1471 }
1472
1543 JS.Expression _visitInitializer(VariableDeclaration node) { 1473 JS.Expression _visitInitializer(VariableDeclaration node) {
1544 var value = _visit(node.initializer); 1474 var value = _visit(node.initializer);
1545 // explicitly initialize to null, to avoid getting `undefined`. 1475 // explicitly initialize to null, to avoid getting `undefined`.
1546 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 1476 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
1547 return value != null ? value : new JS.LiteralNull(); 1477 return value != null ? value : new JS.LiteralNull();
1548 } 1478 }
1549 1479
1550 void _flushPendingStatements(List<JS.Statement> body) {
1551 if (_pendingStatements.isNotEmpty) {
1552 body.addAll(_pendingStatements);
1553 _pendingStatements.clear();
1554 }
1555 }
1556
1557 void _flushLazyFields(List<JS.Statement> body) { 1480 void _flushLazyFields(List<JS.Statement> body) {
1558 var code = _emitLazyFields(_exportsVar, _lazyFields); 1481 if (_lazyFields.isEmpty) return;
1559 if (code != null) { 1482 body.add(_emitLazyFields(_lazyFields));
1560 // Ensure symbols for private fields are defined.
1561 _flushPendingStatements(body);
1562 body.add(code);
1563 }
1564 _lazyFields.clear(); 1483 _lazyFields.clear();
1565 } 1484 }
1566 1485
1567 JS.Statement _emitLazyFields( 1486 JS.Statement _emitLazyFields(List<VariableDeclaration> fields) {
1568 JS.Expression objExpr, List<VariableDeclaration> fields) {
1569 if (fields.isEmpty) return null;
1570
1571 var methods = []; 1487 var methods = [];
1572 for (var node in fields) { 1488 for (var node in fields) {
1573 var name = node.name.name; 1489 var name = node.name.name;
1574 var element = node.element; 1490 var element = node.element;
1575 var access = _emitMemberName(name, type: element.type, isStatic: true); 1491 var access = _emitMemberName(name, type: element.type, isStatic: true);
1576 methods.add(new JS.Method( 1492 methods.add(new JS.Method(
1577 access, js.call('function() { return #; }', _visit(node.initializer)), 1493 access, js.call('function() { return #; }', _visit(node.initializer)),
1578 isGetter: true)); 1494 isGetter: true));
1579 1495
1580 // TODO(jmesserly): use a dummy setter to indicate writable. 1496 // TODO(jmesserly): use a dummy setter to indicate writable.
1581 if (!node.isFinal) { 1497 if (!node.isFinal) {
1582 methods.add( 1498 methods.add(
1583 new JS.Method(access, js.call('function(_) {}'), isSetter: true)); 1499 new JS.Method(access, js.call('function(_) {}'), isSetter: true));
1584 } 1500 }
1585 } 1501 }
1586 1502
1503 JS.Expression objExpr = _exportsVar;
1504 var target = _lazyFields[0].element.enclosingElement;
1505 if (target is ClassElement) {
1506 objExpr = new JS.Identifier(target.type.name);
1507 }
1508
1587 return js.statement( 1509 return js.statement(
1588 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); 1510 'dart.defineLazyProperties(#, { # });', [objExpr, methods]);
1589 } 1511 }
1590 1512
1591 void _flushLibraryProperties(List<JS.Statement> body) { 1513 void _flushLibraryProperties(List<JS.Statement> body) {
1592 if (_properties.isEmpty) return; 1514 if (_properties.isEmpty) return;
1593 body.add(js.statement('dart.copyProperties(#, { # });', [ 1515 body.add(js.statement('dart.copyProperties(#, { # });', [
1594 _exportsVar, 1516 _exportsVar,
1595 _properties.map(_emitTopLevelProperty) 1517 _properties.map(_emitTopLevelProperty)
1596 ])); 1518 ]));
1597 _properties.clear(); 1519 _properties.clear();
1598 } 1520 }
1599 1521
1600 @override 1522 @override
1601 visitConstructorName(ConstructorName node) { 1523 visitConstructorName(ConstructorName node) {
1602 var typeName = _visit(node.type); 1524 var typeName = _visit(node.type);
1603 if (node.name != null) { 1525 if (node.name != null) {
1604 return js.call( 1526 return js.call(
1605 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); 1527 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]);
1606 } 1528 }
1607 return typeName; 1529 return typeName;
1608 } 1530 }
1609 1531
1610 @override 1532 @override
1611 visitInstanceCreationExpression(InstanceCreationExpression node) { 1533 visitInstanceCreationExpression(InstanceCreationExpression node) {
1612 var newExpr = js.call( 1534 emitNew() => js.call(
1613 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); 1535 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]);
1614 if (node.isConst) return _const(node, newExpr); 1536 if (node.isConst) return _emitConst(node, emitNew);
1615 return newExpr; 1537 return emitNew();
1616 } 1538 }
1617 1539
1618 /// True if this type is built-in to JS, and we use the values unwrapped. 1540 /// True if this type is built-in to JS, and we use the values unwrapped.
1619 /// For these types we generate a calling convention via static 1541 /// For these types we generate a calling convention via static
1620 /// "extension methods". This allows types to be extended without adding 1542 /// "extension methods". This allows types to be extended without adding
1621 /// extensions directly on the prototype. 1543 /// extensions directly on the prototype.
1622 bool _isJSBuiltinType(DartType t) => 1544 bool _isJSBuiltinType(DartType t) =>
1623 typeIsPrimitiveInJS(t) || rules.isStringType(t); 1545 typeIsPrimitiveInJS(t) || rules.isStringType(t);
1624 1546
1625 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || 1547 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) ||
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
1773 // LocalVariableElementImpl, so we could repurpose to mean "temp". 1695 // LocalVariableElementImpl, so we could repurpose to mean "temp".
1774 // * add a new property to LocalVariableElementImpl. 1696 // * add a new property to LocalVariableElementImpl.
1775 // * create a new subtype of LocalVariableElementImpl to mark a temp. 1697 // * create a new subtype of LocalVariableElementImpl to mark a temp.
1776 var id = 1698 var id =
1777 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); 1699 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1));
1778 id.staticElement = new TemporaryVariableElement.forNode(id); 1700 id.staticElement = new TemporaryVariableElement.forNode(id);
1779 id.staticType = type; 1701 id.staticType = type;
1780 return id; 1702 return id;
1781 } 1703 }
1782 1704
1783 JS.Expression _const(Expression node, JS.Expression expr, [String nameHint]) { 1705 JS.Expression _emitConst(Expression node, JS.Expression expr()) {
1784 var value = js.call('dart.const(#)', expr); 1706 // TODO(jmesserly): emit the constants at top level if possible.
1785 1707 // This wasn't quite working, so disabled for now.
1786 // If we're inside a method or function, capture the value into a 1708 return js.call('dart.const(#)', expr());
Jennifer Messerly 2015/05/12 16:23:45 the idea here is: _emitConst can tell _loader it's
1787 // global temporary, so we don't do the expensive canonicalization step.
1788 var ancestor = node.getAncestor((n) => n is FunctionBody ||
1789 (n is FieldDeclaration && n.staticKeyword == null));
1790 if (ancestor == null) return value;
1791
1792 if (nameHint == null) {
1793 nameHint = 'const_' + getStaticType(node).name;
1794 }
1795
1796 // TODO(jmesserly): enable this once we fix
1797 // https://github.com/dart-lang/dev_compiler/issues/131
1798 /*var temp = new JSTemporary(nameHint);
1799 _pendingStatements.add(js.statement('let # = #;', [temp, value]));
1800 return temp;*/
1801 assert(nameHint != null); // so it's not marked unused
1802 return value;
1803 } 1709 }
1804 1710
1805 /// Returns a new expression, which can be be used safely *once* on the 1711 /// Returns a new expression, which can be be used safely *once* on the
1806 /// left hand side, and *once* on the right side of an assignment. 1712 /// left hand side, and *once* on the right side of an assignment.
1807 /// For example: `expr1[expr2] += y` can be compiled as 1713 /// For example: `expr1[expr2] += y` can be compiled as
1808 /// `expr1[expr2] = expr1[expr2] + y`. 1714 /// `expr1[expr2] = expr1[expr2] + y`.
1809 /// 1715 ///
1810 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated 1716 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated
1811 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. 1717 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`.
1812 /// 1718 ///
(...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after
2270 visitIntegerLiteral(IntegerLiteral node) => js.number(node.value); 2176 visitIntegerLiteral(IntegerLiteral node) => js.number(node.value);
2271 2177
2272 @override 2178 @override
2273 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value); 2179 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value);
2274 2180
2275 @override 2181 @override
2276 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); 2182 visitNullLiteral(NullLiteral node) => new JS.LiteralNull();
2277 2183
2278 @override 2184 @override
2279 visitSymbolLiteral(SymbolLiteral node) { 2185 visitSymbolLiteral(SymbolLiteral node) {
2280 // TODO(vsm): When we canonicalize, we need to treat private symbols 2186 emitSymbol() {
2281 // correctly. 2187 // TODO(vsm): When we canonicalize, we need to treat private symbols
2282 var name = js.string(node.components.join('.'), "'"); 2188 // correctly.
2283 var nameHint = 'symbol_' + node.components.join('_'); 2189 var name = js.string(node.components.join('.'), "'");
2284 return _const( 2190 return new JS.New(_emitTypeName(types.symbolType), [name]);
2285 node, new JS.New(_emitTypeName(types.symbolType), [name]), nameHint); 2191 }
2192 return _emitConst(node, emitSymbol);
2286 } 2193 }
2287 2194
2288 @override 2195 @override
2289 visitListLiteral(ListLiteral node) { 2196 visitListLiteral(ListLiteral node) {
2290 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); 2197 emitList() {
2291 2198 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements));
2292 ParameterizedType type = node.staticType; 2199 ParameterizedType type = node.staticType;
2293 if (type.typeArguments.any((a) => a != types.dynamicType)) { 2200 if (type.typeArguments.any((a) => a != types.dynamicType)) {
2294 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); 2201 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]);
2202 }
2203 return list;
2295 } 2204 }
2296 if (node.constKeyword != null) return _const(node, list); 2205 if (node.constKeyword != null) return _emitConst(node, emitList);
2297 return list; 2206 return emitList();
2298 } 2207 }
2299 2208
2300 @override 2209 @override
2301 visitMapLiteral(MapLiteral node) { 2210 visitMapLiteral(MapLiteral node) {
2302 // TODO(jmesserly): we can likely make these faster. 2211 // TODO(jmesserly): we can likely make these faster.
2303 var entries = node.entries; 2212 emitMap() {
2304 var mapArguments = null; 2213 var entries = node.entries;
2305 if (entries.isEmpty) { 2214 var mapArguments = null;
2306 mapArguments = []; 2215 if (entries.isEmpty) {
2307 } else if (entries.every((e) => e.key is StringLiteral)) { 2216 mapArguments = [];
2308 // Use JS object literal notation if possible, otherwise use an array. 2217 } else if (entries.every((e) => e.key is StringLiteral)) {
2309 // We could do this any time all keys are non-nullable String type. 2218 // Use JS object literal notation if possible, otherwise use an array.
2310 // For now, support StringLiteral as the common non-nullable String case. 2219 // We could do this any time all keys are non-nullable String type.
2311 var props = []; 2220 // For now, support StringLiteral as the common non-nullable String case .
2312 for (var e in entries) { 2221 var props = [];
2313 props.add(new JS.Property(_visit(e.key), _visit(e.value))); 2222 for (var e in entries) {
2223 props.add(new JS.Property(_visit(e.key), _visit(e.value)));
2224 }
2225 mapArguments = new JS.ObjectInitializer(props);
2226 } else {
2227 var values = [];
2228 for (var e in entries) {
2229 values.add(_visit(e.key));
2230 values.add(_visit(e.value));
2231 }
2232 mapArguments = new JS.ArrayInitializer(values);
2314 } 2233 }
2315 mapArguments = new JS.ObjectInitializer(props); 2234 // TODO(jmesserly): add generic types args.
2316 } else { 2235 return js.call('dart.map(#)', [mapArguments]);
2317 var values = [];
2318 for (var e in entries) {
2319 values.add(_visit(e.key));
2320 values.add(_visit(e.value));
2321 }
2322 mapArguments = new JS.ArrayInitializer(values);
2323 } 2236 }
2324 // TODO(jmesserly): add generic types args. 2237 if (node.constKeyword != null) return _emitConst(node, emitMap);
2325 var map = js.call('dart.map(#)', [mapArguments]); 2238 return emitMap();
2326 if (node.constKeyword != null) return _const(node, map);
2327 return map;
2328 } 2239 }
2329 2240
2330 @override 2241 @override
2331 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => 2242 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) =>
2332 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); 2243 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"');
2333 2244
2334 @override 2245 @override
2335 JS.Expression visitAdjacentStrings(AdjacentStrings node) => 2246 JS.Expression visitAdjacentStrings(AdjacentStrings node) =>
2336 _visitListToBinary(node.strings, '+'); 2247 _visitListToBinary(node.strings, '+');
2337 2248
(...skipping 30 matching lines...) Expand all
2368 JS.Expression _unimplementedCall(String comment) { 2279 JS.Expression _unimplementedCall(String comment) {
2369 return js.call('dart.throw_(#)', [js.escapedString(comment)]); 2280 return js.call('dart.throw_(#)', [js.escapedString(comment)]);
2370 } 2281 }
2371 2282
2372 @override 2283 @override
2373 visitNode(AstNode node) { 2284 visitNode(AstNode node) {
2374 // TODO(jmesserly): verify this is unreachable. 2285 // TODO(jmesserly): verify this is unreachable.
2375 throw 'Unimplemented ${node.runtimeType}: $node'; 2286 throw 'Unimplemented ${node.runtimeType}: $node';
2376 } 2287 }
2377 2288
2378 // TODO(jmesserly): this is used to determine if the field initialization is
2379 // side effect free. We should make the check more general, as things like
2380 // list/map literals/regexp are also side effect free and fairly common
2381 // to use as field initializers.
2382 bool _isFieldInitConstant(VariableDeclaration field) =>
2383 field.initializer == null || _computeConstant(field).isValid;
2384
2385 EvaluationResult _computeConstant(VariableDeclaration field) {
2386 // If the constant is already computed by ConstantEvaluator, just return it.
2387 VariableElementImpl element = field.element;
2388 var result = element.evaluationResult;
2389 if (result != null) return result;
2390
2391 // ConstantEvaluator will not compute constants for non-const fields
2392 // at least for cases like `int x = 0;`, so run ConstantVisitor for those.
2393 // TODO(jmesserly): ideally we'd only do this if we're sure it was skipped
2394 // by ConstantEvaluator.
2395 var initializer = field.initializer;
2396 if (initializer == null) return null;
2397
2398 return _constEvaluator.evaluate(initializer);
2399 }
2400
2401 _visit(AstNode node) { 2289 _visit(AstNode node) {
2402 if (node == null) return null; 2290 if (node == null) return null;
2403 var result = node.accept(this); 2291 var result = node.accept(this);
2404 if (result is JS.Node) result.sourceInformation = node; 2292 if (result is JS.Node) result.sourceInformation = node;
2405 return result; 2293 return result;
2406 } 2294 }
2407 2295
2408 List _visitList(Iterable<AstNode> nodes) { 2296 List _visitList(Iterable<AstNode> nodes) {
2409 if (nodes == null) return null; 2297 if (nodes == null) return null;
2410 var result = []; 2298 var result = [];
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
2620 2508
2621 /// A special kind of element created by the compiler, signifying a temporary 2509 /// A special kind of element created by the compiler, signifying a temporary
2622 /// variable. These objects use instance equality, and should be shared 2510 /// variable. These objects use instance equality, and should be shared
2623 /// everywhere in the tree where they are treated as the same variable. 2511 /// everywhere in the tree where they are treated as the same variable.
2624 class TemporaryVariableElement extends LocalVariableElementImpl { 2512 class TemporaryVariableElement extends LocalVariableElementImpl {
2625 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 2513 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
2626 2514
2627 int get hashCode => identityHashCode(this); 2515 int get hashCode => identityHashCode(this);
2628 bool operator ==(Object other) => identical(this, other); 2516 bool operator ==(Object other) => identical(this, other);
2629 } 2517 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698