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

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: 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
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/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:path/path.dart' as path; 16 import 'package:path/path.dart' as path;
17 17
18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
19 import 'package:dev_compiler/src/codegen/reify_coercions.dart' 19 import 'package:dev_compiler/src/codegen/reify_coercions.dart'
20 show CoercionReifier; 20 show CoercionReifier;
21 21
22 // TODO(jmesserly): import from its own package 22 // TODO(jmesserly): import from its own package
23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 23 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
24 import 'package:dev_compiler/src/js/js_ast.dart' show js; 24 import 'package:dev_compiler/src/js/js_ast.dart' show js;
25 25
26 import 'package:dev_compiler/src/checker/rules.dart'; 26 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'; 27 import 'package:dev_compiler/src/info.dart';
29 import 'package:dev_compiler/src/options.dart'; 28 import 'package:dev_compiler/src/options.dart';
30 import 'package:dev_compiler/src/utils.dart'; 29 import 'package:dev_compiler/src/utils.dart';
31 30
32 import 'code_generator.dart'; 31 import 'code_generator.dart';
33 import 'js_field_storage.dart'; 32 import 'js_field_storage.dart';
34 import 'js_names.dart' as JS; 33 import 'js_names.dart' as JS;
35 import 'js_metalet.dart' as JS; 34 import 'js_metalet.dart' as JS;
35 import 'js_module_item_order.dart';
36 import 'js_printer.dart' show writeJsLibrary; 36 import 'js_printer.dart' show writeJsLibrary;
37 import 'side_effect_analysis.dart'; 37 import 'side_effect_analysis.dart';
38 38
39 // Various dynamic helpers we call. 39 // Various dynamic helpers we call.
40 // If renaming these, make sure to check other places like the 40 // If renaming these, make sure to check other places like the
41 // dart_runtime.js file and comments. 41 // dart_runtime.js file and comments.
42 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 42 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
43 // import and generate calls to, rather than dart_runtime.js 43 // import and generate calls to, rather than dart_runtime.js
44 const DPUT = 'dput'; 44 const DPUT = 'dput';
45 const DLOAD = 'dload'; 45 const DLOAD = 'dload';
(...skipping 13 matching lines...) Expand all
59 /// Information that is precomputed for this library, indicates which fields 59 /// Information that is precomputed for this library, indicates which fields
60 /// need storage slots. 60 /// need storage slots.
61 final HashSet<FieldElement> _fieldsNeedingStorage; 61 final HashSet<FieldElement> _fieldsNeedingStorage;
62 62
63 /// The variable for the target of the current `..` cascade expression. 63 /// The variable for the target of the current `..` cascade expression.
64 SimpleIdentifier _cascadeTarget; 64 SimpleIdentifier _cascadeTarget;
65 65
66 /// The variable for the current catch clause 66 /// The variable for the current catch clause
67 SimpleIdentifier _catchParameter; 67 SimpleIdentifier _catchParameter;
68 68
69 ConstantEvaluator _constEvaluator;
70
71 ClassElement _currentClassElement = null;
72
73 /// Imported libraries, and the temporaries used to refer to them. 69 /// Imported libraries, and the temporaries used to refer to them.
74 final _imports = new Map<LibraryElement, JS.TemporaryId>(); 70 final _imports = new Map<LibraryElement, JS.TemporaryId>();
75 final _exports = new Set<String>(); 71 final _exports = new Set<String>();
76 final _lazyFields = <VariableDeclaration>[]; 72 final _lazyFields = <VariableDeclaration>[];
77 final _properties = <FunctionDeclaration>[]; 73 final _properties = <FunctionDeclaration>[];
78 final _privateNames = new HashMap<String, JS.TemporaryId>(); 74 final _privateNames = new HashMap<String, JS.TemporaryId>();
79 final _extensionMethodNames = new HashSet<String>(); 75 final _extensionMethodNames = new HashSet<String>();
80 final _pendingStatements = <JS.Statement>[]; 76 final _moduleItems = <JS.Statement>[];
81 final _temps = new HashMap<Element, JS.TemporaryId>(); 77 final _temps = new HashMap<Element, JS.TemporaryId>();
78 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>();
79 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>();
82 80
83 /// The name for the library's exports inside itself. 81 /// The name for the library's exports inside itself.
84 /// `exports` was chosen as the most similar to ES module patterns. 82 /// `exports` was chosen as the most similar to ES module patterns.
85 final _exportsVar = new JS.TemporaryId('exports'); 83 final _exportsVar = new JS.TemporaryId('exports');
86 final _namedArgTemp = new JS.TemporaryId('opts'); 84 final _namedArgTemp = new JS.TemporaryId('opts');
87 85
88 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or 86 ConstFieldVisitor _constField;
89 /// [ClassTypeAlias].
90 final _pendingClasses = new HashMap<Element, CompilationUnitMember>();
91 87
92 /// Memoized results of [_lazyClass]. 88 ModuleItemLoadOrder _loader;
93 final _lazyClassMemo = new HashMap<Element, bool>();
94
95 /// Memoized results of [_inLibraryCycle].
96 final _libraryCycleMemo = new HashMap<LibraryElement, bool>();
97 89
98 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, 90 JSCodegenVisitor(this.options, this.rules, this.libraryInfo,
99 this._extensionMethods, this._fieldsNeedingStorage); 91 this._extensionMethods, this._fieldsNeedingStorage) {
92 _loader = new ModuleItemLoadOrder(_emitModuleItem);
93 }
100 94
101 LibraryElement get currentLibrary => libraryInfo.library; 95 LibraryElement get currentLibrary => libraryInfo.library;
102 TypeProvider get types => rules.provider; 96 TypeProvider get types => rules.provider;
103 97
104 JS.Program emitLibrary(LibraryUnit library) { 98 JS.Program emitLibrary(LibraryUnit library) {
105 String jsDefaultValue = null; 99 String jsDefaultValue = null;
106 100
107 // Modify the AST to make coercions explicit. 101 // Modify the AST to make coercions explicit.
108 new CoercionReifier(library, rules, options).reify(); 102 new CoercionReifier(library, rules, options).reify();
109 103
110 var unit = library.library; 104 var unit = library.library;
111 if (unit.directives.isNotEmpty) { 105 if (unit.directives.isNotEmpty) {
112 var libraryDir = unit.directives.first; 106 var libraryDir = unit.directives.first;
113 if (libraryDir is LibraryDirective) { 107 if (libraryDir is LibraryDirective) {
114 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); 108 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation);
115 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); 109 jsDefaultValue = getConstantField(jsName, 'name', types.stringType);
116 } 110 }
117 } 111 }
118 if (jsDefaultValue == null) jsDefaultValue = '{}'; 112 if (jsDefaultValue == null) jsDefaultValue = '{}';
119 113
120 var body = <JS.Statement>[]; 114 // TODO(jmesserly): visit scriptTag, directives?
121 115
122 // Collect classes we need to emit, used for: 116 _loader.collectElements(currentLibrary, library.partsThenLibrary);
123 // * tracks what we've emitted so we don't emit twice 117
124 // * provides a mapping from ClassElement back to the ClassDeclaration.
125 for (var unit in library.partsThenLibrary) { 118 for (var unit in library.partsThenLibrary) {
119 _constField = new ConstFieldVisitor(types, unit);
120
126 for (var decl in unit.declarations) { 121 for (var decl in unit.declarations) {
127 if (decl is ClassDeclaration || 122 if (decl is TopLevelVariableDeclaration) {
128 decl is ClassTypeAlias || 123 _visit(decl);
129 decl is FunctionTypeAlias) { 124 } else {
130 _pendingClasses[decl.element] = decl; 125 _loader.loadDeclaration(decl, decl.element);
126 }
127 if (decl is ClassDeclaration) {
128 // Static fields can be emitted into the top-level code, so they need
129 // to potentially be ordered independently of the class.
130 for (var member in decl.members) {
131 if (member is FieldDeclaration && member.isStatic) {
132 for (var f in member.fields.variables) {
133 _loader.loadDeclaration(f, f.element);
134 }
135 }
136 }
131 } 137 }
132 } 138 }
133 } 139 }
134 140
135 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); 141 // Flush any unwritten fields/properties.
142 _flushLazyFields(_moduleItems);
143 _flushLibraryProperties(_moduleItems);
136 144
137 assert(_pendingClasses.isEmpty); 145 // Mark all qualified names as qualified or not, depending on if they need
146 // to be loaded lazily or not.
147 unqualifyIfNeeded(Element e, JS.MaybeQualifiedId id) {
148 id.setQualified(!_loader.isLoaded(e));
149 }
150 _qualifiedIds.forEach(unqualifyIfNeeded);
151 _qualifiedGenericIds.forEach(unqualifyIfNeeded);
138 152
139 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); 153 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:'));
140 154
141 // TODO(jmesserly): make these immutable in JS? 155 // TODO(jmesserly): make these immutable in JS?
142 for (var name in _exports) { 156 for (var name in _exports) {
143 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); 157 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name]));
144 } 158 }
145 159
146 var name = new JS.Identifier(jsLibraryName(currentLibrary)); 160 var name = new JS.Identifier(jsLibraryName(currentLibrary));
147 161
148 // TODO(jmesserly): it would be great to run the renamer on the body, 162 // 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. 163 // then figure out if we really need each of these parameters.
150 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 164 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34
151 var program = [ 165 var program = [
152 js.statement('var # = dart.defineLibrary(#, #);', [ 166 js.statement('var # = dart.defineLibrary(#, #);', [
153 name, 167 name,
154 name, 168 name,
155 js.call(jsDefaultValue) 169 js.call(jsDefaultValue)
156 ]) 170 ])
157 ]; 171 ];
158 172
159 var params = [_exportsVar]; 173 var params = [_exportsVar];
160 var args = [name]; 174 var args = [name];
161 _imports.forEach((library, temp) { 175 _imports.forEach((library, temp) {
162 var name = new JS.Identifier(temp.name); 176 var name = new JS.Identifier(temp.name);
163 params.add(temp); 177 params.add(temp);
164 args.add(name); 178 args.add(name);
165 var helper = _libraryMightNotBeLoaded(library) ? 'lazyImport' : 'import'; 179 var helper = _loader.libraryIsLoaded(library) ? 'import' : 'lazyImport';
166 program.add(js.statement('var # = dart.#(#);', [name, helper, name])); 180 program.add(js.statement('var # = dart.#(#);', [name, helper, name]));
167 }); 181 });
168 182
169 program.add(js.statement( 183 program.add(js.statement("(function(#) { 'use strict'; #; })(#);", [
170 "(function(#) { 'use strict'; #; })(#);", [params, body, args])); 184 params,
185 _moduleItems,
186 args
187 ]));
171 188
172 return new JS.Program(program); 189 return new JS.Program(program);
173 } 190 }
174 191
192 void _emitModuleItem(AstNode node) {
193 // Attempt to group adjacent fields/properties.
194 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems);
195 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
196
197 var code = _visit(node);
198 if (code != null) _moduleItems.add(code);
199 }
200
175 JS.Identifier _initSymbol(JS.Identifier id) { 201 JS.Identifier _initSymbol(JS.Identifier id) {
176 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); 202 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
177 _pendingStatements.add(s); 203 _moduleItems.add(s);
178 return id; 204 return id;
179 } 205 }
180 206
181 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 207 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
182 // until we have better name tracking. 208 // until we have better name tracking.
183 String get _SYMBOL { 209 String get _SYMBOL {
184 var name = currentLibrary.name; 210 var name = currentLibrary.name;
185 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; 211 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
186 return 'Symbol'; 212 return 'Symbol';
187 } 213 }
188 214
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('_'); 215 bool isPublic(String name) => !name.startsWith('_');
218 216
219 /// Conversions that we don't handle end up here. 217 /// Conversions that we don't handle end up here.
220 @override 218 @override
221 visitConversion(Conversion node) { 219 visitConversion(Conversion node) {
222 throw 'Unlowered conversion ${node.runtimeType}: $node'; 220 throw 'Unlowered conversion ${node.runtimeType}: $node';
223 } 221 }
224 222
225 @override 223 @override
226 visitAsExpression(AsExpression node) { 224 visitAsExpression(AsExpression node) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 String _jsTypeofName(DartType t) { 271 String _jsTypeofName(DartType t) {
274 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number'; 272 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number';
275 if (rules.isStringType(t)) return 'string'; 273 if (rules.isStringType(t)) return 'string';
276 if (rules.isBoolType(t)) return 'boolean'; 274 if (rules.isBoolType(t)) return 'boolean';
277 return null; 275 return null;
278 } 276 }
279 277
280 @override 278 @override
281 visitFunctionTypeAlias(FunctionTypeAlias node) { 279 visitFunctionTypeAlias(FunctionTypeAlias node) {
282 // If we've already emitted this class, skip it. 280 // If we've already emitted this class, skip it.
283 var type = node.element.type; 281 var element = node.element;
284 if (_pendingClasses.remove(node.element) == null) return null; 282 var type = element.type;
283 var name = element.name;
285 284
286 var name = type.name; 285 _loader.startTopLevel(element);
287 var result = js.statement('let # = dart.typedef(#, () => #);', [ 286 var fnType = js.statement('let # = dart.typedef(#, #);', [
288 new JS.Identifier(name), 287 name,
289 js.string(name, "'"), 288 js.string(name, "'"),
290 _emitTypeName(node.element.type, lowerTypedef: true) 289 _emitTypeName(type, lowerTypedef: true)
291 ]); 290 ]);
291 _loader.finishTopLevel(element);
292 292
293 return _finishClassDef(type, result); 293 return _finishClassDef(type, fnType);
294 } 294 }
295 295
296 @override 296 @override
297 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); 297 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type);
298 298
299 @override 299 @override
300 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { 300 JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
301 // If we've already emitted this class, skip it. 301 // If we've already emitted this class, skip it.
302 var type = node.element.type; 302 var element = node.element;
303 if (_pendingClasses.remove(node.element) == null) return null;
304 303
305 var name = node.name.name;
306 var classDecl = new JS.ClassDeclaration(new JS.ClassExpression( 304 var classDecl = new JS.ClassDeclaration(new JS.ClassExpression(
307 new JS.Identifier(name), _classHeritage(node), [])); 305 new JS.Identifier(element.name), _classHeritage(element), []));
308 306
309 return _finishClassDef(type, classDecl); 307 return _finishClassDef(element.type, classDecl);
310 } 308 }
311 309
312 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { 310 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) {
313 var jsTypeName = getConstantField(jsName, 'name', types.stringType); 311 var jsTypeName = getConstantField(jsName, 'name', types.stringType);
314 312
315 if (jsTypeName != null && jsTypeName != dartClassName) { 313 if (jsTypeName != null && jsTypeName != dartClassName) {
316 // We export the JS type as if it was a Dart type. For example this allows 314 // We export the JS type as if it was a Dart type. For example this allows
317 // `dom.InputElement` to actually be HTMLInputElement. 315 // `dom.InputElement` to actually be HTMLInputElement.
318 // TODO(jmesserly): if we had the JsName on the Element, we could just 316 // TODO(jmesserly): if we had the JsName on the Element, we could just
319 // generate it correctly when we refer to it. 317 // generate it correctly when we refer to it.
320 if (isPublic(dartClassName)) _addExport(dartClassName); 318 if (isPublic(dartClassName)) _addExport(dartClassName);
321 return js.statement('let # = #;', [dartClassName, jsTypeName]); 319 return js.statement('let # = #;', [dartClassName, jsTypeName]);
322 } 320 }
323 return null; 321 return null;
324 } 322 }
325 323
326 @override 324 @override
327 JS.Statement visitClassDeclaration(ClassDeclaration node) { 325 JS.Statement visitClassDeclaration(ClassDeclaration node) {
328 // If we've already emitted this class, skip it. 326 // If we've already emitted this class, skip it.
329 var classElem = node.element; 327 var classElem = node.element;
330 var type = classElem.type; 328 var type = classElem.type;
331 if (_pendingClasses.remove(classElem) == null) return null;
332
333 var jsName = getAnnotationValue(node, _isJsNameAnnotation); 329 var jsName = getAnnotationValue(node, _isJsNameAnnotation);
334 330
335 if (jsName != null) return _emitJsType(node.name.name, jsName); 331 if (jsName != null) return _emitJsType(node.name.name, jsName);
336 332
337 // Set current class
338 assert(_currentClassElement == null);
339 _currentClassElement = classElem;
340
341 var ctors = <ConstructorDeclaration>[]; 333 var ctors = <ConstructorDeclaration>[];
342 var fields = <FieldDeclaration>[]; 334 var fields = <FieldDeclaration>[];
343 var staticFields = <FieldDeclaration>[];
344 for (var member in node.members) { 335 for (var member in node.members) {
345 if (member is ConstructorDeclaration) { 336 if (member is ConstructorDeclaration) {
346 ctors.add(member); 337 ctors.add(member);
347 } else if (member is FieldDeclaration) { 338 } else if (member is FieldDeclaration && !member.isStatic) {
348 (member.isStatic ? staticFields : fields).add(member); 339 fields.add(member);
349 } 340 }
350 } 341 }
351 342
352 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 343 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
353 _classHeritage(node), _emitClassMethods(node, ctors, fields)); 344 _classHeritage(classElem), _emitClassMethods(node, ctors, fields));
354 345
355 String jsPeerName; 346 String jsPeerName;
356 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); 347 var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
357 if (jsPeer != null) { 348 if (jsPeer != null) {
358 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); 349 jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
359 } 350 }
360 351
361 var body = _finishClassMembers( 352 var body =
362 classElem, classExpr, ctors, fields, staticFields, jsPeerName); 353 _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName);
363
364 // Unset current class
365 assert(_currentClassElement == classElem);
366 _currentClassElement = null;
367 354
368 var result = _finishClassDef(type, body); 355 var result = _finishClassDef(type, body);
369 356
370 if (jsPeerName != null) { 357 if (jsPeerName != null) {
371 // This class isn't allowed to be lazy, because we need to set up 358 // This class isn't allowed to be lazy, because we need to set up
372 // the native JS type eagerly at this point. 359 // the native JS type eagerly at this point.
373 // If we wanted to support laziness, we could defer the hookup until 360 // If we wanted to support laziness, we could defer the hookup until
374 // the end of the Dart library cycle load. 361 // the end of the Dart library cycle load.
375 assert(!_lazyClass(type)); 362 assert(_loader.isLoaded(classElem));
376 363
377 // TODO(jmesserly): this copies the dynamic members. 364 // TODO(jmesserly): this copies the dynamic members.
378 // Probably fine for objects coming from JS, but not if we actually 365 // Probably fine for objects coming from JS, but not if we actually
379 // want to support construction of instances with generic types other 366 // want to support construction of instances with generic types other
380 // than dynamic. See issue #154 for Array and List<E> related bug. 367 // than dynamic. See issue #154 for Array and List<E> related bug.
381 var copyMembers = js.statement( 368 var copyMembers = js.statement(
382 'dart.registerExtension(dart.global.#, #);', [ 369 'dart.registerExtension(dart.global.#, #);', [
383 _propertyName(jsPeerName), 370 _propertyName(jsPeerName),
384 classElem.name 371 classElem.name
385 ]); 372 ]);
386 return _statement([result, copyMembers]); 373 return _statement([result, copyMembers]);
387 } 374 }
388 return result; 375 return result;
389 } 376 }
390 377
391 @override 378 @override
392 JS.Statement visitEnumDeclaration(EnumDeclaration node) => 379 JS.Statement visitEnumDeclaration(EnumDeclaration node) =>
393 _unimplementedCall("Unimplemented enum: $node").toStatement(); 380 _unimplementedCall("Unimplemented enum: $node").toStatement();
394 381
395 /// Given a class element and body, complete the class declaration. 382 /// Given a class element and body, complete the class declaration.
396 /// This handles generic type parameters, laziness (in library-cycle cases), 383 /// This handles generic type parameters, laziness (in library-cycle cases),
397 /// and ensuring dependencies are loaded first. 384 /// and ensuring dependencies are loaded first.
398 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { 385 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) {
399 var name = type.name; 386 var name = type.name;
400 var genericName = '$name\$'; 387 var genericName = '$name\$';
401 388
402 JS.Statement genericDef; 389 JS.Statement genericDef = null;
403 JS.Expression genericInst;
404 if (type.typeParameters.isNotEmpty) { 390 if (type.typeParameters.isNotEmpty) {
405 genericDef = _emitGenericClassDef(type, body); 391 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 } 392 }
412 393
413 // The base class and all mixins must be declared before this class. 394 // The base class and all mixins must be declared before this class.
414 if (_lazyClass(type)) { 395 if (!_loader.isLoaded(type.element)) {
415 // TODO(jmesserly): the lazy class def is a simple solution for now. 396 // TODO(jmesserly): the lazy class def is a simple solution for now.
416 // We may want to consider other options in the future. 397 // We may want to consider other options in the future.
417 398
418 if (genericDef != null) { 399 if (genericDef != null) {
419 return js.statement( 400 return js.statement(
420 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ 401 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [
421 genericDef, 402 genericDef,
422 _exportsVar, 403 _exportsVar,
423 _propertyName(name), 404 _propertyName(name),
424 genericName 405 genericName
425 ]); 406 ]);
426 } 407 }
427 408
428 return js.statement( 409 return js.statement(
429 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ 410 'dart.defineLazyClass(#, { get #() { #; return #; } });', [
430 _exportsVar, 411 _exportsVar,
431 _propertyName(name), 412 _propertyName(name),
432 body, 413 body,
433 name 414 name
434 ]); 415 ]);
435 } 416 }
436 417
437 if (isPublic(name)) _addExport(name); 418 if (isPublic(name)) _addExport(name);
438 419
439 if (genericDef != null) { 420 if (genericDef != null) {
440 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); 421 var dynType = fillDynamicTypeArgs(type, types);
422 var genericInst = _emitTypeName(dynType, lowerGeneric: true);
423 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]);
441 } 424 }
442 425 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 } 426 }
553 427
554 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { 428 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) {
555 var name = type.name; 429 var name = type.name;
556 var genericName = '$name\$'; 430 var genericName = '$name\$';
557 var typeParams = type.typeParameters.map((p) => p.name); 431 var typeParams = type.typeParameters.map((p) => p.name);
558 if (isPublic(name)) _exports.add(genericName); 432 if (isPublic(name)) _exports.add(genericName);
559 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ 433 return js.statement('let # = dart.generic(function(#) { #; return #; });', [
560 genericName, 434 genericName,
561 typeParams, 435 typeParams,
562 body, 436 body,
563 name 437 name
564 ]); 438 ]);
565 } 439 }
566 440
567 JS.Expression _classHeritage(node) { 441 JS.Expression _classHeritage(ClassElement element) {
568 if (node.element.type.isObject) return null; 442 var type = element.type;
443 if (type.isObject) return null;
569 444
570 DartType supertype; 445 // Assume we can load eagerly, until proven otherwise.
571 if (node is ClassDeclaration) { 446 _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 447
578 JS.Expression heritage = _emitTypeName(supertype); 448 JS.Expression heritage = _emitTypeName(type.superclass);
579 449 if (type.mixins.isNotEmpty) {
580 if (node.withClause != null) { 450 var mixins = type.mixins.map(_emitTypeName).toList();
581 var mixins = _visitList(node.withClause.mixinTypes);
582 mixins.insert(0, heritage); 451 mixins.insert(0, heritage);
583 heritage = js.call('dart.mixin(#)', [mixins]); 452 heritage = js.call('dart.mixin(#)', [mixins]);
584 } 453 }
585 454
455 _loader.finishTopLevel(element);
586 return heritage; 456 return heritage;
587 } 457 }
588 458
589 List<JS.Method> _emitClassMethods(ClassDeclaration node, 459 List<JS.Method> _emitClassMethods(ClassDeclaration node,
590 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { 460 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
591 var element = node.element; 461 var element = node.element;
592 var type = element.type; 462 var type = element.type;
593 var isObject = type.isObject; 463 var isObject = type.isObject;
594 var name = node.name.name; 464 var name = node.name.name;
595 465
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call( 526 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call(
657 'function() { return new dart.JsIterator(this.#); }', 527 'function() { return new dart.JsIterator(this.#); }',
658 [_emitMemberName('iterator', type: t)])); 528 [_emitMemberName('iterator', type: t)]));
659 } 529 }
660 530
661 /// Emit class members that need to come after the class declaration, such 531 /// Emit class members that need to come after the class declaration, such
662 /// as static fields. See [_emitClassMethods] for things that are emitted 532 /// as static fields. See [_emitClassMethods] for things that are emitted
663 /// inside the ES6 `class { ... }` node. 533 /// inside the ES6 `class { ... }` node.
664 JS.Statement _finishClassMembers(ClassElement classElem, 534 JS.Statement _finishClassMembers(ClassElement classElem,
665 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 535 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
666 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields, 536 List<FieldDeclaration> fields, String jsPeerName) {
667 String jsPeerName) {
668 var name = classElem.name; 537 var name = classElem.name;
669 var body = <JS.Statement>[]; 538 var body = <JS.Statement>[];
670 body.add(new JS.ClassDeclaration(cls)); 539 body.add(new JS.ClassDeclaration(cls));
671 540
672 // TODO(jmesserly): we should really just extend native Array. 541 // TODO(jmesserly): we should really just extend native Array.
673 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { 542 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
674 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ 543 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [
675 classElem.name, 544 classElem.name,
676 _propertyName(jsPeerName) 545 _propertyName(jsPeerName)
677 ])); 546 ]));
(...skipping 21 matching lines...) Expand all
699 // Instance fields, if they override getter/setter pairs 568 // Instance fields, if they override getter/setter pairs
700 for (FieldDeclaration member in fields) { 569 for (FieldDeclaration member in fields) {
701 for (VariableDeclaration fieldDecl in member.fields.variables) { 570 for (VariableDeclaration fieldDecl in member.fields.variables) {
702 var field = fieldDecl.element; 571 var field = fieldDecl.element;
703 if (_fieldsNeedingStorage.contains(field)) { 572 if (_fieldsNeedingStorage.contains(field)) {
704 body.add(_overrideField(field)); 573 body.add(_overrideField(field));
705 } 574 }
706 } 575 }
707 } 576 }
708 577
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); 578 return _statement(body);
727 } 579 }
728 580
729 JS.Statement _overrideField(FieldElement e) { 581 JS.Statement _overrideField(FieldElement e) {
730 var cls = e.enclosingElement; 582 var cls = e.enclosingElement;
731 return js.statement('dart.virtualField(#, #)', [ 583 return js.statement('dart.virtualField(#, #)', [
732 cls.name, 584 cls.name,
733 _emitMemberName(e.name, type: cls.type) 585 _emitMemberName(e.name, type: cls.type)
734 ]); 586 ]);
735 } 587 }
736 588
737 /// Generates the implicit default constructor for class C of the form 589 /// Generates the implicit default constructor for class C of the form
738 /// `C() : super() {}`. 590 /// `C() : super() {}`.
739 JS.Method _emitImplicitConstructor( 591 JS.Method _emitImplicitConstructor(
740 ClassDeclaration node, String name, List<FieldDeclaration> fields) { 592 ClassDeclaration node, String name, List<FieldDeclaration> fields) {
741 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); 593 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty);
742 594
743 // If we don't have a method body, skip this. 595 // If we don't have a method body, skip this.
744 var superCall = _superConstructorCall(node); 596 var superCall = _superConstructorCall(node);
745 if (fields.isEmpty && superCall == null) return null; 597 if (fields.isEmpty && superCall == null) return null;
746 598
747 dynamic body = _initializeFields(fields); 599 dynamic body = _initializeFields(node, fields);
748 if (superCall != null) body = [[body, superCall]]; 600 if (superCall != null) body = [[body, superCall]];
749 return new JS.Method( 601 return new JS.Method(
750 _propertyName(name), js.call('function() { #; }', body)); 602 _propertyName(name), js.call('function() { #; }', body));
751 } 603 }
752 604
753 JS.Method _emitConstructor(ConstructorDeclaration node, String className, 605 JS.Method _emitConstructor(ConstructorDeclaration node, String className,
754 List<FieldDeclaration> fields, bool isObject) { 606 List<FieldDeclaration> fields, bool isObject) {
755 if (_externalOrNative(node)) return null; 607 if (_externalOrNative(node)) return null;
756 608
757 var name = _constructorName(className, node.name); 609 var name = _constructorName(className, node.name);
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
826 body.add(_visit(redirectCall)); 678 body.add(_visit(redirectCall));
827 return new JS.Block(body); 679 return new JS.Block(body);
828 } 680 }
829 681
830 // Initializers only run for non-factory constructors. 682 // Initializers only run for non-factory constructors.
831 if (node.factoryKeyword == null) { 683 if (node.factoryKeyword == null) {
832 // Generate field initializers. 684 // Generate field initializers.
833 // These are expanded into each non-redirecting constructor. 685 // These are expanded into each non-redirecting constructor.
834 // In the future we may want to create an initializer function if we have 686 // 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. 687 // multiple constructors, but it needs to be balanced against readability.
836 body.add(_initializeFields(fields, node.parameters, node.initializers)); 688 body.add(_initializeFields(node, fields));
837 689
838 var superCall = node.initializers.firstWhere( 690 var superCall = node.initializers.firstWhere(
839 (i) => i is SuperConstructorInvocation, orElse: () => null); 691 (i) => i is SuperConstructorInvocation, orElse: () => null);
840 692
841 // If no superinitializer is provided, an implicit superinitializer of the 693 // If no superinitializer is provided, an implicit superinitializer of the
842 // form `super()` is added at the end of the initializer list, unless the 694 // form `super()` is added at the end of the initializer list, unless the
843 // enclosing class is class Object. 695 // enclosing class is class Object.
844 var jsSuper = _superConstructorCall(node.parent, superCall); 696 var jsSuper = _superConstructorCall(node.parent, superCall);
845 if (jsSuper != null) body.add(jsSuper); 697 if (jsSuper != null) body.add(jsSuper);
846 } 698 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
890 if (!e.unnamedConstructor.isSynthetic) return true; 742 if (!e.unnamedConstructor.isSynthetic) return true;
891 return e.fields.any((f) => !f.isStatic && !f.isSynthetic); 743 return e.fields.any((f) => !f.isStatic && !f.isSynthetic);
892 } 744 }
893 745
894 /// Initialize fields. They follow the sequence: 746 /// Initialize fields. They follow the sequence:
895 /// 747 ///
896 /// 1. field declaration initializer if non-const, 748 /// 1. field declaration initializer if non-const,
897 /// 2. field initializing parameters, 749 /// 2. field initializing parameters,
898 /// 3. constructor field initializers, 750 /// 3. constructor field initializers,
899 /// 4. initialize fields not covered in 1-3 751 /// 4. initialize fields not covered in 1-3
900 JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls, 752 JS.Statement _initializeFields(
901 [FormalParameterList parameters, 753 AstNode node, List<FieldDeclaration> fieldDecls) {
902 NodeList<ConstructorInitializer> initializers]) { 754 var unit = node.getAncestor((a) => a is CompilationUnit);
755 var constField = new ConstFieldVisitor(types, unit);
903 756
904 // Run field initializers if they can have side-effects. 757 // Run field initializers if they can have side-effects.
905 var fields = new Map<FieldElement, JS.Expression>(); 758 var fields = new Map<FieldElement, JS.Expression>();
906 var unsetFields = new Map<FieldElement, VariableDeclaration>(); 759 var unsetFields = new Map<FieldElement, VariableDeclaration>();
907 for (var declaration in fieldDecls) { 760 for (var declaration in fieldDecls) {
908 for (var fieldNode in declaration.fields.variables) { 761 for (var fieldNode in declaration.fields.variables) {
909 var element = fieldNode.element; 762 var element = fieldNode.element;
910 if (_isFieldInitConstant(fieldNode)) { 763 if (constField.isFieldInitConstant(fieldNode)) {
911 unsetFields[element] = fieldNode; 764 unsetFields[element] = fieldNode;
912 } else { 765 } else {
913 fields[element] = _visitInitializer(fieldNode); 766 fields[element] = _visitInitializer(fieldNode);
914 } 767 }
915 } 768 }
916 } 769 }
917 770
918 // Initialize fields from `this.fieldName` parameters. 771 // Initialize fields from `this.fieldName` parameters.
919 if (parameters != null) { 772 if (node is ConstructorDeclaration) {
773 var parameters = node.parameters;
774 var initializers = node.initializers;
775
920 for (var p in parameters.parameters) { 776 for (var p in parameters.parameters) {
921 var element = p.element; 777 var element = p.element;
922 if (element is FieldFormalParameterElement) { 778 if (element is FieldFormalParameterElement) {
923 fields[element.field] = _visit(p); 779 fields[element.field] = _visit(p);
924 } 780 }
925 } 781 }
926 }
927 782
928 // Run constructor field initializers such as `: foo = bar.baz` 783 // Run constructor field initializers such as `: foo = bar.baz`
929 if (initializers != null) {
930 for (var init in initializers) { 784 for (var init in initializers) {
931 if (init is ConstructorFieldInitializer) { 785 if (init is ConstructorFieldInitializer) {
932 fields[init.fieldName.staticElement] = _visit(init.expression); 786 fields[init.fieldName.staticElement] = _visit(init.expression);
933 } 787 }
934 } 788 }
935 } 789 }
936 790
937 for (var f in fields.keys) unsetFields.remove(f); 791 for (var f in fields.keys) unsetFields.remove(f);
938 792
939 // Initialize all remaining fields 793 // Initialize all remaining fields
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
1122 var accessor = node.staticElement; 976 var accessor = node.staticElement;
1123 if (accessor == null) { 977 if (accessor == null) {
1124 return js.commentExpression( 978 return js.commentExpression(
1125 'Unimplemented unknown name', new JS.Identifier(node.name)); 979 'Unimplemented unknown name', new JS.Identifier(node.name));
1126 } 980 }
1127 981
1128 // Get the original declaring element. If we had a property accessor, this 982 // Get the original declaring element. If we had a property accessor, this
1129 // indirects back to a (possibly synthetic) field. 983 // indirects back to a (possibly synthetic) field.
1130 var element = accessor; 984 var element = accessor;
1131 if (element is PropertyAccessorElement) element = accessor.variable; 985 if (element is PropertyAccessorElement) element = accessor.variable;
986
987 _loader.declareBeforeUse(element);
988
1132 var name = element.name; 989 var name = element.name;
1133 990
1134 // library member 991 // library member
1135 if (element.enclosingElement is CompilationUnitElement && 992 if (element.enclosingElement is CompilationUnitElement) {
1136 (element.library != currentLibrary || 993 return _maybeQualifiedName(
1137 element is TopLevelVariableElement && !element.isConst)) { 994 element, _emitMemberName(name, isStatic: true));
1138 var memberName = _emitMemberName(name, isStatic: true);
1139 return js.call('#.#', [_libraryName(element.library), memberName]);
1140 } 995 }
1141 996
1142 // Unqualified class member. This could mean implicit-this, or implicit 997 // Unqualified class member. This could mean implicit-this, or implicit
1143 // call to a static from the same class. 998 // call to a static from the same class.
1144 if (element is ClassMemberElement && element is! ConstructorElement) { 999 if (element is ClassMemberElement && element is! ConstructorElement) {
1145 bool isStatic = element.isStatic; 1000 bool isStatic = element.isStatic;
1146 var type = element.enclosingElement.type; 1001 var type = element.enclosingElement.type;
1147 var member = _emitMemberName(name, isStatic: isStatic, type: type); 1002 var member = _emitMemberName(name, isStatic: isStatic, type: type);
1148 1003
1149 // For static methods, we add the raw type name, without generics or 1004 // 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 1005 // library prefix. We don't need those because static calls can't use
1151 // the generic type. 1006 // the generic type.
1152 if (isStatic) { 1007 if (isStatic) {
1153 return js.call('#.#', [type.name, member]); 1008 var dynType = _emitTypeName(fillDynamicTypeArgs(type, types));
1009 return new JS.PropertyAccess(dynType, member);
1154 } 1010 }
1155 1011
1156 // For instance members, we add implicit-this. 1012 // For instance members, we add implicit-this.
1157 // For method tear-offs, we ensure it's a bound method. 1013 // For method tear-offs, we ensure it's a bound method.
1158 var code = 'this.#'; 1014 var code = 'this.#';
1159 if (element is MethodElement && !inInvocationContext(node)) { 1015 if (element is MethodElement && !inInvocationContext(node)) {
1160 code += '.bind(this)'; 1016 code += '.bind(this)';
1161 } 1017 }
1162 return js.call(code, member); 1018 return js.call(code, member);
1163 } 1019 }
(...skipping 29 matching lines...) Expand all
1193 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { 1049 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) {
1194 var properties = <JS.Property>[]; 1050 var properties = <JS.Property>[];
1195 types.forEach((name, type) { 1051 types.forEach((name, type) {
1196 var key = new JS.LiteralString(name); 1052 var key = new JS.LiteralString(name);
1197 var value = _emitTypeName(type); 1053 var value = _emitTypeName(type);
1198 properties.add(new JS.Property(key, value)); 1054 properties.add(new JS.Property(key, value));
1199 }); 1055 });
1200 return new JS.ObjectInitializer(properties); 1056 return new JS.ObjectInitializer(properties);
1201 } 1057 }
1202 1058
1203 JS.Expression _emitTypeName(DartType type, {bool lowerTypedef: false}) { 1059 /// Emits a Dart [type] into code.
1060 ///
1061 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a
1062 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form
1063 /// will be used instead of `List`. These flags are used when generating
1064 /// the definitions for typedefs and generic types, respectively.
1065 JS.Expression _emitTypeName(DartType type,
1066 {bool lowerTypedef: false, bool lowerGeneric: false}) {
1067
1204 // The void and dynamic types are not defined in core. 1068 // The void and dynamic types are not defined in core.
1205 if (type.isVoid) { 1069 if (type.isVoid) {
1206 return js.call('dart.void'); 1070 return js.call('dart.void');
1207 } else if (type.isDynamic) { 1071 } else if (type.isDynamic) {
1208 return js.call('dart.dynamic'); 1072 return js.call('dart.dynamic');
1209 } 1073 }
1210 1074
1075 _loader.declareBeforeUse(type.element);
1076
1077 // TODO(jmesserly): like constants, should we hoist function types out of
1078 // methods? Similar issue with generic types. For all of these, we may want
1079 // to canonicalize them too, at least when inside the same library.
1211 var name = type.name; 1080 var name = type.name;
1212 var element = type.element; 1081 var element = type.element;
1213 if (name == '' || lowerTypedef && type is FunctionType) { 1082 if (name == '' || lowerTypedef) {
1214 if (type is FunctionType) { 1083 var fnType = type as FunctionType;
1215 var returnType = type.returnType; 1084 var returnType = fnType.returnType;
1216 var parameterTypes = type.normalParameterTypes; 1085 var parameterTypes = fnType.normalParameterTypes;
1217 var optionalTypes = type.optionalParameterTypes; 1086 var optionalTypes = fnType.optionalParameterTypes;
1218 var namedTypes = type.namedParameterTypes; 1087 var namedTypes = fnType.namedParameterTypes;
1219 if (namedTypes.isEmpty) { 1088 if (namedTypes.isEmpty) {
1220 if (optionalTypes.isEmpty) { 1089 if (optionalTypes.isEmpty) {
1221 return js.call('dart.functionType(#, #)', [ 1090 return js.call('dart.functionType(#, #)', [
1222 _emitTypeName(returnType), 1091 _emitTypeName(returnType),
1223 _emitTypeNames(parameterTypes) 1092 _emitTypeNames(parameterTypes)
1224 ]); 1093 ]);
1225 } else {
1226 return js.call('dart.functionType(#, #, #)', [
1227 _emitTypeName(returnType),
1228 _emitTypeNames(parameterTypes),
1229 _emitTypeNames(optionalTypes)
1230 ]);
1231 }
1232 } else { 1094 } else {
1233 assert(optionalTypes.isEmpty);
1234 return js.call('dart.functionType(#, #, #)', [ 1095 return js.call('dart.functionType(#, #, #)', [
1235 _emitTypeName(returnType), 1096 _emitTypeName(returnType),
1236 _emitTypeNames(parameterTypes), 1097 _emitTypeNames(parameterTypes),
1237 _emitTypeProperties(namedTypes) 1098 _emitTypeNames(optionalTypes)
1238 ]); 1099 ]);
1239 } 1100 }
1101 } else {
1102 assert(optionalTypes.isEmpty);
1103 return js.call('dart.functionType(#, #, #)', [
1104 _emitTypeName(returnType),
1105 _emitTypeNames(parameterTypes),
1106 _emitTypeProperties(namedTypes)
1107 ]);
1240 } 1108 }
1241 // TODO(jmesserly): remove when we're using coercion reifier.
1242 return _unimplementedCall('Unimplemented type $type');
1243 } 1109 }
1244 1110
1245 var typeArgs = null; 1111 if (type is TypeParameterType) {
1112 return new JS.Identifier(name);
1113 }
1114
1246 if (type is ParameterizedType) { 1115 if (type is ParameterizedType) {
1247 var args = type.typeArguments; 1116 var args = type.typeArguments;
1248 var isCurrentClass = 1117 var isCurrentClass =
1249 type is InterfaceType ? type.element == _currentClassElement : false; 1118 args.isNotEmpty && _loader.isCurrentElement(type.element);
1119 Iterable jsArgs = null;
1250 if (args.any((a) => a != types.dynamicType)) { 1120 if (args.any((a) => a != types.dynamicType)) {
1251 name = '$name\$'; 1121 jsArgs = args.map(_emitTypeName);
1252 typeArgs = args.map(_emitTypeName); 1122 } else if (lowerGeneric || isCurrentClass) {
1253 } else if (args.isNotEmpty && isCurrentClass) {
1254 // When creating a `new S<dynamic>` we try and use the raw form 1123 // 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, 1124 // `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. 1125 // because `S` refers to the current S<T> we are generating.
1257 name = '$name\$'; 1126 jsArgs = [];
1258 typeArgs = []; 1127 }
1128 if (jsArgs != null) {
1129 var genericName = _maybeQualifiedName(
1130 element, _propertyName('$name\$'), _qualifiedGenericIds);
1131 return js.call('#(#)', [genericName, jsArgs]);
1259 } 1132 }
1260 } 1133 }
1261 1134
1262 JS.Expression result; 1135 return _maybeQualifiedName(element, _propertyName(name));
1263 if (_needQualifiedName(element)) { 1136 }
1264 result = js.call('#.#', [_libraryName(element.library), name]); 1137
1265 } else { 1138 JS.Expression _maybeQualifiedName(Element e, JS.Expression name,
1266 result = new JS.Identifier(name); 1139 [Map<Element, JS.MaybeQualifiedId> idTable]) {
1140 var libName = _libraryName(e.library);
1141 if (idTable == null) idTable = _qualifiedIds;
1142
1143 // Mutable top-level fields should always be qualified.
1144 bool mutableTopLevel = e is TopLevelVariableElement && !e.isConst;
1145 if (e.library != currentLibrary || mutableTopLevel) {
1146 return new JS.PropertyAccess(libName, name);
1267 } 1147 }
1268 1148
1269 if (typeArgs != null) { 1149 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 } 1150 }
1283 1151
1284 @override 1152 @override
1285 JS.Expression visitAssignmentExpression(AssignmentExpression node) { 1153 JS.Expression visitAssignmentExpression(AssignmentExpression node) {
1286 var left = node.leftHandSide; 1154 var left = node.leftHandSide;
1287 var right = node.rightHandSide; 1155 var right = node.rightHandSide;
1288 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); 1156 if (node.operator.type == TokenType.EQ) return _emitSet(left, right);
1289 return _emitOpAssign( 1157 return _emitOpAssign(
1290 left, right, node.operator.lexeme[0], node.staticElement, 1158 left, right, node.operator.lexeme[0], node.staticElement,
1291 context: node); 1159 context: node);
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
1481 1349
1482 @override 1350 @override
1483 JS.Statement visitReturnStatement(ReturnStatement node) { 1351 JS.Statement visitReturnStatement(ReturnStatement node) {
1484 var e = node.expression; 1352 var e = node.expression;
1485 if (e == null) return new JS.Return(); 1353 if (e == null) return new JS.Return();
1486 return _visit(e).toReturn(); 1354 return _visit(e).toReturn();
1487 } 1355 }
1488 1356
1489 @override 1357 @override
1490 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 1358 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
1491 var body = <JS.Statement>[]; 1359 for (var v in node.variables.variables) {
1492 1360 _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 } 1361 }
1508
1509 return _statement(body);
1510 } 1362 }
1511 1363
1512 _addExport(String name) { 1364 _addExport(String name) {
1513 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; 1365 if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
1514 } 1366 }
1515 1367
1516 @override 1368 @override
1517 JS.Statement visitVariableDeclarationStatement( 1369 JS.Statement visitVariableDeclarationStatement(
1518 VariableDeclarationStatement node) { 1370 VariableDeclarationStatement node) {
1519 // Special case a single variable with an initializer. 1371 // Special case a single variable with an initializer.
1520 // This helps emit cleaner code for things like: 1372 // This helps emit cleaner code for things like:
1521 // var result = []..add(1)..add(2); 1373 // var result = []..add(1)..add(2);
1522 if (node.variables.variables.length == 1) { 1374 if (node.variables.variables.length == 1) {
1523 var v = node.variables.variables.single; 1375 var v = node.variables.variables.single;
1524 if (v.initializer != null) { 1376 if (v.initializer != null) {
1525 var name = new JS.Identifier(v.name.name); 1377 var name = new JS.Identifier(v.name.name);
1526 return _visit(v.initializer).toVariableDeclaration(name); 1378 return _visit(v.initializer).toVariableDeclaration(name);
1527 } 1379 }
1528 } 1380 }
1529 return _visit(node.variables).toStatement(); 1381 return _visit(node.variables).toStatement();
1530 } 1382 }
1531 1383
1532 @override 1384 @override
1533 visitVariableDeclarationList(VariableDeclarationList node) { 1385 visitVariableDeclarationList(VariableDeclarationList node) {
1534 return new JS.VariableDeclarationList('let', _visitList(node.variables)); 1386 return new JS.VariableDeclarationList('let', _visitList(node.variables));
1535 } 1387 }
1536 1388
1537 @override 1389 @override
1538 JS.VariableInitialization visitVariableDeclaration(VariableDeclaration node) { 1390 visitVariableDeclaration(VariableDeclaration node) {
1391 if (node.element is PropertyInducingElement) return _emitStaticField(node);
1392
1539 var name = new JS.Identifier(node.name.name); 1393 var name = new JS.Identifier(node.name.name);
1540 return new JS.VariableInitialization(name, _visitInitializer(node)); 1394 return new JS.VariableInitialization(name, _visitInitializer(node));
1541 } 1395 }
1542 1396
1397 /// Emits a static or top-level field.
1398 JS.Statement _emitStaticField(VariableDeclaration field) {
1399 PropertyInducingElement element = field.element;
1400 assert(element.isStatic);
1401
1402 bool eagerInit;
1403 JS.Expression jsInit;
1404 if (field.isConst || _constField.isFieldInitConstant(field)) {
1405 // If the field is constant, try and generate it at the top level.
1406 _loader.startTopLevel(element);
1407 jsInit = _visitInitializer(field);
1408 _loader.finishTopLevel(element);
1409 eagerInit = _loader.isLoaded(element);
1410 } else {
1411 jsInit = _visitInitializer(field);
1412 eagerInit = false;
1413 }
1414
1415 var fieldName = field.name.name;
1416 if (field.isConst && eagerInit && element is TopLevelVariableElement) {
1417 // constant fields don't change, so we can generate them as `let`
1418 // but add them to the module's exports. However, make sure we generate
1419 // anything they depend on first.
1420
1421 if (isPublic(fieldName)) _addExport(fieldName);
1422 return js.statement('let # = #;', [new JS.Identifier(fieldName), jsInit]);
1423 }
1424
1425 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
1426 return js.statement('# = #;', [_visit(field.name), jsInit]);
1427 }
1428
1429 var body = [];
1430 if (_lazyFields.isNotEmpty) {
1431 var existingTarget = _lazyFields[0].element.enclosingElement;
1432 if (existingTarget != element.enclosingElement) {
1433 _flushLazyFields(body);
1434 }
1435 }
1436
1437 _lazyFields.add(field);
1438
1439 return _statement(body);
1440 }
1441
1543 JS.Expression _visitInitializer(VariableDeclaration node) { 1442 JS.Expression _visitInitializer(VariableDeclaration node) {
1544 var value = _visit(node.initializer); 1443 var value = _visit(node.initializer);
1545 // explicitly initialize to null, to avoid getting `undefined`. 1444 // explicitly initialize to null, to avoid getting `undefined`.
1546 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 1445 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
1547 return value != null ? value : new JS.LiteralNull(); 1446 return value != null ? value : new JS.LiteralNull();
1548 } 1447 }
1549 1448
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) { 1449 void _flushLazyFields(List<JS.Statement> body) {
1558 var code = _emitLazyFields(_exportsVar, _lazyFields); 1450 if (_lazyFields.isEmpty) return;
1559 if (code != null) { 1451 body.add(_emitLazyFields(_lazyFields));
1560 // Ensure symbols for private fields are defined.
1561 _flushPendingStatements(body);
1562 body.add(code);
1563 }
1564 _lazyFields.clear(); 1452 _lazyFields.clear();
1565 } 1453 }
1566 1454
1567 JS.Statement _emitLazyFields( 1455 JS.Statement _emitLazyFields(List<VariableDeclaration> fields) {
1568 JS.Expression objExpr, List<VariableDeclaration> fields) {
1569 if (fields.isEmpty) return null;
1570
1571 var methods = []; 1456 var methods = [];
1572 for (var node in fields) { 1457 for (var node in fields) {
1573 var name = node.name.name; 1458 var name = node.name.name;
1574 var element = node.element; 1459 var element = node.element;
1575 var access = _emitMemberName(name, type: element.type, isStatic: true); 1460 var access = _emitMemberName(name, type: element.type, isStatic: true);
1576 methods.add(new JS.Method( 1461 methods.add(new JS.Method(
1577 access, js.call('function() { return #; }', _visit(node.initializer)), 1462 access, js.call('function() { return #; }', _visit(node.initializer)),
1578 isGetter: true)); 1463 isGetter: true));
1579 1464
1580 // TODO(jmesserly): use a dummy setter to indicate writable. 1465 // TODO(jmesserly): use a dummy setter to indicate writable.
1581 if (!node.isFinal) { 1466 if (!node.isFinal) {
1582 methods.add( 1467 methods.add(
1583 new JS.Method(access, js.call('function(_) {}'), isSetter: true)); 1468 new JS.Method(access, js.call('function(_) {}'), isSetter: true));
1584 } 1469 }
1585 } 1470 }
1586 1471
1472 JS.Expression objExpr = _exportsVar;
1473 var target = _lazyFields[0].element.enclosingElement;
1474 if (target is ClassElement) {
1475 objExpr = new JS.Identifier(target.type.name);
1476 }
1477
1587 return js.statement( 1478 return js.statement(
1588 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); 1479 'dart.defineLazyProperties(#, { # });', [objExpr, methods]);
1589 } 1480 }
1590 1481
1591 void _flushLibraryProperties(List<JS.Statement> body) { 1482 void _flushLibraryProperties(List<JS.Statement> body) {
1592 if (_properties.isEmpty) return; 1483 if (_properties.isEmpty) return;
1593 body.add(js.statement('dart.copyProperties(#, { # });', [ 1484 body.add(js.statement('dart.copyProperties(#, { # });', [
1594 _exportsVar, 1485 _exportsVar,
1595 _properties.map(_emitTopLevelProperty) 1486 _properties.map(_emitTopLevelProperty)
1596 ])); 1487 ]));
1597 _properties.clear(); 1488 _properties.clear();
1598 } 1489 }
1599 1490
1600 @override 1491 @override
1601 visitConstructorName(ConstructorName node) { 1492 visitConstructorName(ConstructorName node) {
1602 var typeName = _visit(node.type); 1493 var typeName = _visit(node.type);
1603 if (node.name != null) { 1494 if (node.name != null) {
1604 return js.call( 1495 return js.call(
1605 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); 1496 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]);
1606 } 1497 }
1607 return typeName; 1498 return typeName;
1608 } 1499 }
1609 1500
1610 @override 1501 @override
1611 visitInstanceCreationExpression(InstanceCreationExpression node) { 1502 visitInstanceCreationExpression(InstanceCreationExpression node) {
1612 var newExpr = js.call( 1503 emitNew() => js.call(
1613 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); 1504 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]);
1614 if (node.isConst) return _const(node, newExpr); 1505 if (node.isConst) return _emitConst(node, emitNew);
1615 return newExpr; 1506 return emitNew();
1616 } 1507 }
1617 1508
1618 /// True if this type is built-in to JS, and we use the values unwrapped. 1509 /// 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 1510 /// For these types we generate a calling convention via static
1620 /// "extension methods". This allows types to be extended without adding 1511 /// "extension methods". This allows types to be extended without adding
1621 /// extensions directly on the prototype. 1512 /// extensions directly on the prototype.
1622 bool _isJSBuiltinType(DartType t) => 1513 bool _isJSBuiltinType(DartType t) =>
1623 typeIsPrimitiveInJS(t) || rules.isStringType(t); 1514 typeIsPrimitiveInJS(t) || rules.isStringType(t);
1624 1515
1625 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || 1516 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". 1664 // LocalVariableElementImpl, so we could repurpose to mean "temp".
1774 // * add a new property to LocalVariableElementImpl. 1665 // * add a new property to LocalVariableElementImpl.
1775 // * create a new subtype of LocalVariableElementImpl to mark a temp. 1666 // * create a new subtype of LocalVariableElementImpl to mark a temp.
1776 var id = 1667 var id =
1777 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); 1668 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1));
1778 id.staticElement = new TemporaryVariableElement.forNode(id); 1669 id.staticElement = new TemporaryVariableElement.forNode(id);
1779 id.staticType = type; 1670 id.staticType = type;
1780 return id; 1671 return id;
1781 } 1672 }
1782 1673
1783 JS.Expression _const(Expression node, JS.Expression expr, [String nameHint]) { 1674 JS.Expression _emitConst(Expression node, JS.Expression expr()) {
1784 var value = js.call('dart.const(#)', expr); 1675 // TODO(jmesserly): emit the constants at top level if possible.
1785 1676 // This wasn't quite working, so disabled for now.
1786 // If we're inside a method or function, capture the value into a 1677 return js.call('dart.const(#)', expr());
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 } 1678 }
1804 1679
1805 /// Returns a new expression, which can be be used safely *once* on the 1680 /// 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. 1681 /// left hand side, and *once* on the right side of an assignment.
1807 /// For example: `expr1[expr2] += y` can be compiled as 1682 /// For example: `expr1[expr2] += y` can be compiled as
1808 /// `expr1[expr2] = expr1[expr2] + y`. 1683 /// `expr1[expr2] = expr1[expr2] + y`.
1809 /// 1684 ///
1810 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated 1685 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated
1811 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. 1686 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`.
1812 /// 1687 ///
(...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after
2270 visitIntegerLiteral(IntegerLiteral node) => js.number(node.value); 2145 visitIntegerLiteral(IntegerLiteral node) => js.number(node.value);
2271 2146
2272 @override 2147 @override
2273 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value); 2148 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value);
2274 2149
2275 @override 2150 @override
2276 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); 2151 visitNullLiteral(NullLiteral node) => new JS.LiteralNull();
2277 2152
2278 @override 2153 @override
2279 visitSymbolLiteral(SymbolLiteral node) { 2154 visitSymbolLiteral(SymbolLiteral node) {
2280 // TODO(vsm): When we canonicalize, we need to treat private symbols 2155 emitSymbol() {
2281 // correctly. 2156 // TODO(vsm): When we canonicalize, we need to treat private symbols
2282 var name = js.string(node.components.join('.'), "'"); 2157 // correctly.
2283 var nameHint = 'symbol_' + node.components.join('_'); 2158 var name = js.string(node.components.join('.'), "'");
2284 return _const( 2159 return new JS.New(_emitTypeName(types.symbolType), [name]);
2285 node, new JS.New(_emitTypeName(types.symbolType), [name]), nameHint); 2160 }
2161 return _emitConst(node, emitSymbol);
2286 } 2162 }
2287 2163
2288 @override 2164 @override
2289 visitListLiteral(ListLiteral node) { 2165 visitListLiteral(ListLiteral node) {
2290 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); 2166 emitList() {
2291 2167 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements));
2292 ParameterizedType type = node.staticType; 2168 ParameterizedType type = node.staticType;
2293 if (type.typeArguments.any((a) => a != types.dynamicType)) { 2169 if (type.typeArguments.any((a) => a != types.dynamicType)) {
2294 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); 2170 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]);
2171 }
2172 return list;
2295 } 2173 }
2296 if (node.constKeyword != null) return _const(node, list); 2174 if (node.constKeyword != null) return _emitConst(node, emitList);
2297 return list; 2175 return emitList();
2298 } 2176 }
2299 2177
2300 @override 2178 @override
2301 visitMapLiteral(MapLiteral node) { 2179 visitMapLiteral(MapLiteral node) {
2302 // TODO(jmesserly): we can likely make these faster. 2180 // TODO(jmesserly): we can likely make these faster.
2303 var entries = node.entries; 2181 emitMap() {
2304 var mapArguments = null; 2182 var entries = node.entries;
2305 if (entries.isEmpty) { 2183 var mapArguments = null;
2306 mapArguments = []; 2184 if (entries.isEmpty) {
2307 } else if (entries.every((e) => e.key is StringLiteral)) { 2185 mapArguments = [];
2308 // Use JS object literal notation if possible, otherwise use an array. 2186 } else if (entries.every((e) => e.key is StringLiteral)) {
2309 // We could do this any time all keys are non-nullable String type. 2187 // Use JS object literal notation if possible, otherwise use an array.
2310 // For now, support StringLiteral as the common non-nullable String case. 2188 // We could do this any time all keys are non-nullable String type.
2311 var props = []; 2189 // For now, support StringLiteral as the common non-nullable String case .
2312 for (var e in entries) { 2190 var props = [];
2313 props.add(new JS.Property(_visit(e.key), _visit(e.value))); 2191 for (var e in entries) {
2192 props.add(new JS.Property(_visit(e.key), _visit(e.value)));
2193 }
2194 mapArguments = new JS.ObjectInitializer(props);
2195 } else {
2196 var values = [];
2197 for (var e in entries) {
2198 values.add(_visit(e.key));
2199 values.add(_visit(e.value));
2200 }
2201 mapArguments = new JS.ArrayInitializer(values);
2314 } 2202 }
2315 mapArguments = new JS.ObjectInitializer(props); 2203 // TODO(jmesserly): add generic types args.
2316 } else { 2204 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 } 2205 }
2324 // TODO(jmesserly): add generic types args. 2206 if (node.constKeyword != null) return _emitConst(node, emitMap);
2325 var map = js.call('dart.map(#)', [mapArguments]); 2207 return emitMap();
2326 if (node.constKeyword != null) return _const(node, map);
2327 return map;
2328 } 2208 }
2329 2209
2330 @override 2210 @override
2331 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => 2211 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) =>
2332 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); 2212 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"');
2333 2213
2334 @override 2214 @override
2335 JS.Expression visitAdjacentStrings(AdjacentStrings node) => 2215 JS.Expression visitAdjacentStrings(AdjacentStrings node) =>
2336 _visitListToBinary(node.strings, '+'); 2216 _visitListToBinary(node.strings, '+');
2337 2217
(...skipping 30 matching lines...) Expand all
2368 JS.Expression _unimplementedCall(String comment) { 2248 JS.Expression _unimplementedCall(String comment) {
2369 return js.call('dart.throw_(#)', [js.escapedString(comment)]); 2249 return js.call('dart.throw_(#)', [js.escapedString(comment)]);
2370 } 2250 }
2371 2251
2372 @override 2252 @override
2373 visitNode(AstNode node) { 2253 visitNode(AstNode node) {
2374 // TODO(jmesserly): verify this is unreachable. 2254 // TODO(jmesserly): verify this is unreachable.
2375 throw 'Unimplemented ${node.runtimeType}: $node'; 2255 throw 'Unimplemented ${node.runtimeType}: $node';
2376 } 2256 }
2377 2257
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) { 2258 _visit(AstNode node) {
2402 if (node == null) return null; 2259 if (node == null) return null;
2403 var result = node.accept(this); 2260 var result = node.accept(this);
2404 if (result is JS.Node) result.sourceInformation = node; 2261 if (result is JS.Node) result.sourceInformation = node;
2405 return result; 2262 return result;
2406 } 2263 }
2407 2264
2408 List _visitList(Iterable<AstNode> nodes) { 2265 List _visitList(Iterable<AstNode> nodes) {
2409 if (nodes == null) return null; 2266 if (nodes == null) return null;
2410 var result = []; 2267 var result = [];
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
2620 2477
2621 /// A special kind of element created by the compiler, signifying a temporary 2478 /// A special kind of element created by the compiler, signifying a temporary
2622 /// variable. These objects use instance equality, and should be shared 2479 /// variable. These objects use instance equality, and should be shared
2623 /// everywhere in the tree where they are treated as the same variable. 2480 /// everywhere in the tree where they are treated as the same variable.
2624 class TemporaryVariableElement extends LocalVariableElementImpl { 2481 class TemporaryVariableElement extends LocalVariableElementImpl {
2625 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 2482 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
2626 2483
2627 int get hashCode => identityHashCode(this); 2484 int get hashCode => identityHashCode(this);
2628 bool operator ==(Object other) => identical(this, other); 2485 bool operator ==(Object other) => identical(this, other);
2629 } 2486 }
OLDNEW
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698