OLD | NEW |
---|---|
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; | 7 import 'dart:collection' show HashSet, HashMap; |
8 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
9 | 9 |
10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
11 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 11 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
12 import 'package:analyzer/src/generated/constant.dart'; | 12 import 'package:analyzer/src/generated/constant.dart'; |
13 import 'package:analyzer/src/generated/element.dart'; | 13 import 'package:analyzer/src/generated/element.dart'; |
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:source_maps/source_maps.dart' as srcmaps show Printer; | 16 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; |
17 import 'package:source_maps/source_maps.dart' show SourceMapSpan; | 17 import 'package:source_maps/source_maps.dart' show SourceMapSpan; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
60 | 60 |
61 ClassDeclaration currentClass; | 61 ClassDeclaration currentClass; |
62 ConstantEvaluator _constEvaluator; | 62 ConstantEvaluator _constEvaluator; |
63 | 63 |
64 final _exports = <String>[]; | 64 final _exports = <String>[]; |
65 final _lazyFields = <VariableDeclaration>[]; | 65 final _lazyFields = <VariableDeclaration>[]; |
66 final _properties = <FunctionDeclaration>[]; | 66 final _properties = <FunctionDeclaration>[]; |
67 final _privateNames = new HashSet<String>(); | 67 final _privateNames = new HashSet<String>(); |
68 final _pendingPrivateNames = <String>[]; | 68 final _pendingPrivateNames = <String>[]; |
69 | 69 |
70 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | |
71 /// [ClassTypeAlias]. | |
72 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); | |
73 | |
74 /// Memoized results of [_lazyClass]. | |
75 final _lazyClassMemo = new HashMap<ClassElement, bool>(); | |
76 | |
77 /// Memoized results of [_inLibraryCycle]. | |
78 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | |
79 | |
70 JSCodegenVisitor(this.libraryInfo, this.rules, this._checkerReporter); | 80 JSCodegenVisitor(this.libraryInfo, this.rules, this._checkerReporter); |
71 | 81 |
72 Element get currentLibrary => libraryInfo.library; | 82 LibraryElement get currentLibrary => libraryInfo.library; |
73 | 83 |
74 JS.Program emitLibrary(LibraryUnit library) { | 84 JS.Program emitLibrary(LibraryUnit library) { |
75 var jsDefaultValue = '{}'; | 85 var jsDefaultValue = '{}'; |
76 var unit = library.library; | 86 var unit = library.library; |
77 if (unit.directives.isNotEmpty) { | 87 if (unit.directives.isNotEmpty) { |
78 var annotation = _getJsNameAnnotation(unit.directives.first); | 88 var annotation = _getJsNameAnnotation(unit.directives.first); |
79 if (annotation != null) { | 89 if (annotation != null) { |
80 var arguments = annotation.arguments.arguments; | 90 var arguments = annotation.arguments.arguments; |
81 if (!arguments.isEmpty) { | 91 if (!arguments.isEmpty) { |
82 var namedExpression = arguments.first as NamedExpression; | 92 var namedExpression = arguments.first as NamedExpression; |
83 var literal = namedExpression.expression as SimpleStringLiteral; | 93 var literal = namedExpression.expression as SimpleStringLiteral; |
84 jsDefaultValue = literal.stringValue; | 94 jsDefaultValue = literal.stringValue; |
85 } | 95 } |
86 } | 96 } |
87 } | 97 } |
88 var body = <JS.Statement>[]; | 98 var body = <JS.Statement>[]; |
89 | 99 |
90 // Visit parts first, since the "part" declarations come before code | 100 // Collect classes we need to emit, used for: |
91 // in the main library's compilation unit. | 101 // * tracks what we've emitted so we don't emit twice |
92 for (var unit in library.parts) { | 102 // * provides a mapping from ClassElement back to the ClassDeclaration. |
93 body.add(_visit(unit)); | 103 for (var unit in library.partsThenLibrary) { |
104 for (var decl in unit.declarations) { | |
105 if (decl is ClassDeclaration || decl is ClassTypeAlias) { | |
106 _pendingClasses[decl.element] = decl; | |
107 } | |
108 } | |
94 } | 109 } |
95 | 110 |
96 // Visit main library | 111 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); |
97 body.add(_visit(library.library)); | 112 |
113 assert(_pendingClasses.isEmpty); | |
98 | 114 |
99 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 115 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); |
100 | 116 |
101 // TODO(jmesserly): make these immutable in JS? | 117 // TODO(jmesserly): make these immutable in JS? |
102 for (var name in _exports) { | 118 for (var name in _exports) { |
103 body.add(js.statement('$_EXPORTS.# = #;', [name, name])); | 119 body.add(js.statement('$_EXPORTS.# = #;', [name, name])); |
104 } | 120 } |
105 | 121 |
106 var name = jsLibraryName(libraryInfo.library); | 122 var name = jsLibraryName(libraryInfo.library); |
107 | 123 |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
217 visitFunctionTypeAlias(FunctionTypeAlias node) { | 233 visitFunctionTypeAlias(FunctionTypeAlias node) { |
218 // TODO(vsm): Do we need to record type info the generated code for a | 234 // TODO(vsm): Do we need to record type info the generated code for a |
219 // typedef? | 235 // typedef? |
220 } | 236 } |
221 | 237 |
222 @override | 238 @override |
223 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); | 239 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); |
224 | 240 |
225 @override | 241 @override |
226 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 242 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
243 // If we've already emitted this class, skip it. | |
244 var classElem = node.element; | |
245 if (_pendingClasses.remove(classElem) == null) return null; | |
246 | |
227 var name = node.name.name; | 247 var name = node.name.name; |
228 var heritage = | 248 var heritage = |
229 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); | 249 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); |
230 var classDecl = new JS.ClassDeclaration( | 250 var classDecl = new JS.ClassDeclaration( |
231 new JS.ClassExpression(new JS.VariableDeclaration(name), heritage, [])); | 251 new JS.ClassExpression(new JS.VariableDeclaration(name), heritage, [])); |
232 if (isPublic(name)) _exports.add(name); | 252 if (isPublic(name)) _exports.add(name); |
233 return _addTypeParameters(node.typeParameters, name, classDecl); | |
234 } | |
235 | 253 |
236 @override | 254 return _finishClassDef(classElem, classDecl); |
237 visitTypeParameter(TypeParameter node) => new JS.Parameter(node.name.name); | |
238 | |
239 JS.Statement _addTypeParameters( | |
240 TypeParameterList node, String name, JS.Statement clazz) { | |
241 if (node == null) return clazz; | |
242 | |
243 var genericName = '$name\$'; | |
244 var genericDef = js.statement( | |
245 'let # = dart.generic(function(#) { #; return #; });', [ | |
246 genericName, | |
247 _visitList(node.typeParameters), | |
248 clazz, | |
249 name | |
250 ]); | |
251 | |
252 // TODO(jmesserly): we may not want this to be `dynamic` if the generic | |
253 // has a lower bound, e.g. `<T extends SomeType>`. | |
254 // https://github.com/dart-lang/dart-dev-compiler/issues/38 | |
255 var typeArgs = | |
256 new List.filled(node.typeParameters.length, js.call('dart.dynamic')); | |
257 | |
258 var dynInst = js.statement('let # = #(#);', [name, genericName, typeArgs]); | |
259 | |
260 // TODO(jmesserly): is it worth exporting both names? Alternatively we could | |
261 // put the generic type constructor on the <dynamic> instance. | |
262 if (isPublic(name)) _exports.add('${name}\$'); | |
263 return new JS.Block([genericDef, dynInst]); | |
264 } | 255 } |
265 | 256 |
266 @override | 257 @override |
267 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 258 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
259 // If we've already emitted this class, skip it. | |
260 var classElem = node.element; | |
261 if (_pendingClasses.remove(classElem) == null) return null; | |
268 if (_getJsNameAnnotation(node) != null) return null; | 262 if (_getJsNameAnnotation(node) != null) return null; |
269 | 263 |
270 currentClass = node; | 264 currentClass = node; |
271 | 265 |
272 // dart:core Object is a bit special. | 266 var name = classElem.name; |
273 var isObject = node.element.type.isObject; | 267 if (isPublic(name)) _exports.add(name); |
274 | 268 |
275 var body = <JS.Statement>[]; | |
276 | |
277 var name = node.name.name; | |
278 var ctors = <ConstructorDeclaration>[]; | 269 var ctors = <ConstructorDeclaration>[]; |
279 var fields = <FieldDeclaration>[]; | 270 var fields = <FieldDeclaration>[]; |
280 var staticFields = <FieldDeclaration>[]; | 271 var staticFields = <FieldDeclaration>[]; |
281 for (var member in node.members) { | 272 for (var member in node.members) { |
282 if (member is ConstructorDeclaration) { | 273 if (member is ConstructorDeclaration) { |
283 ctors.add(member); | 274 ctors.add(member); |
284 } else if (member is FieldDeclaration) { | 275 } else if (member is FieldDeclaration) { |
285 (member.isStatic ? staticFields : fields).add(member); | 276 (member.isStatic ? staticFields : fields).add(member); |
286 } | 277 } |
287 } | 278 } |
288 | 279 |
280 var classExpr = new JS.ClassExpression(new JS.VariableDeclaration(name), | |
281 _classHeritage(node), _emitClassMethods(node, ctors, fields)); | |
282 | |
283 var body = _finishClassMembers(name, classExpr, ctors, staticFields); | |
284 currentClass = null; | |
285 | |
286 return _finishClassDef(classElem, body); | |
287 } | |
288 | |
289 /// Given a class element and body, complete the class declaration. | |
290 /// This handles generic type parameters, laziness (in library-cycle cases), | |
291 /// and ensuring dependencies are loaded first. | |
292 JS.Statement _finishClassDef(ClassElement classElem, JS.Statement body) { | |
293 var name = classElem.name; | |
294 var genericName = '$name\$'; | |
295 | |
296 JS.Statement genericDef; | |
297 JS.Expression genericInst; | |
298 if (classElem.typeParameters.isNotEmpty) { | |
299 genericDef = _emitGenericClassDef(classElem, body); | |
300 var dynamicArgs = new List.filled( | |
301 classElem.typeParameters.length, js.call('dart.dynamic')); | |
302 | |
303 var target = genericName; | |
304 if (_needQualifiedName(classElem)) { | |
305 target = js.call('#.#', [_EXPORTS, genericName]); | |
306 } | |
307 genericInst = js.call('#(#)', [target, dynamicArgs]); | |
308 } | |
309 | |
310 // The base class and all mixins must be declared before this class. | |
311 if (_lazyClass(classElem)) { | |
312 // TODO(jmesserly): the lazy class def is a simple solution for now. | |
313 // We may want to consider other options in the future. | |
314 | |
315 if (genericDef != null) { | |
316 return js.statement( | |
317 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ | |
318 genericDef, | |
319 _EXPORTS, | |
320 js.string(name, "'"), | |
321 genericName | |
322 ]); | |
323 } | |
324 | |
325 if (body is JS.ClassDeclaration) { | |
326 body = new JS.Return((body as JS.ClassDeclaration).classExpr); | |
327 } else { | |
328 body = js.statement('{ #; return #; }', [body, name]); | |
329 } | |
330 | |
331 return js.statement('dart.defineLazyClass(#, { get #() { #; } });', [ | |
332 _EXPORTS, | |
333 name, | |
334 body | |
335 ]); | |
336 } | |
337 | |
338 if (genericDef != null) { | |
339 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | |
340 } | |
341 | |
342 if (classElem.type.isObject) return body; | |
343 | |
344 // If we're not lazy, we still need to ensure our dependencies are | |
345 // generated first. | |
346 var classDefs = <JS.Statement>[]; | |
347 _emitClassIfNeeded(classDefs, classElem.supertype.element); | |
348 for (var m in classElem.mixins) { | |
349 _emitClassIfNeeded(classDefs, m.element); | |
350 } | |
351 classDefs.add(body); | |
352 return _statement(classDefs); | |
353 } | |
354 | |
355 void _emitClassIfNeeded(List<JS.Statement> defs, ClassElement base) { | |
356 // We can only emit classes from this library. | |
357 if (base.library != currentLibrary) return; | |
358 | |
359 var baseNode = _pendingClasses[base]; | |
360 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); | |
361 } | |
362 | |
363 /// Returns true if the supertype or mixins aren't loaded. | |
364 /// If that is the case, we'll emit a lazy class definition. | |
365 bool _lazyClass(ClassElement cls) { | |
366 if (cls.type.isObject) return false; | |
367 | |
368 assert(cls.library == currentLibrary); | |
369 var result = _lazyClassMemo[cls]; | |
370 if (result != null) return result; | |
371 | |
372 result = _classMightNotBeLoaded(cls.supertype.element); | |
373 for (var mixin in cls.mixins) { | |
374 if (result) break; | |
375 result = _classMightNotBeLoaded(mixin.element); | |
376 } | |
377 return _lazyClassMemo[cls] = result; | |
378 } | |
379 | |
380 /// Curated order to minimize lazy classes needed by dart:core and its | |
381 /// transitive SDK imports. | |
382 static const CORELIB_ORDER = const [ | |
383 'dart.core', | |
384 'dart.collection', | |
385 'dart._internal' | |
386 ]; | |
387 | |
388 /// Returns true if the class might not be loaded. | |
389 /// | |
390 /// If the class is from our library, this can happen because it's lazy. | |
391 /// | |
392 /// If the class is from a different library, it could happen if we're in | |
393 /// a library cycle. In other words, if that different library depends back | |
394 /// on this library via some transitive import path. | |
395 /// | |
396 /// If we could control the global import ordering, we could eliminate some | |
397 /// of these cases, by ordering the imports of the cyclic libraries in an | |
398 /// optimal way. For example, we could order the libraries in a cycle to | |
399 /// minimize laziness. However, we currently assume we cannot control the | |
400 /// order that the cycle of libraries will be loaded in. | |
401 bool _classMightNotBeLoaded(ClassElement cls) { | |
402 if (cls.library == currentLibrary) return _lazyClass(cls); | |
403 | |
404 // The SDK is a special case: we optimize the order to prevent laziness. | |
405 if (cls.library.isInSdk) { | |
406 if (!currentLibrary.isInSdk) return false; | |
407 | |
408 var order = CORELIB_ORDER.indexOf(cls.library.name); | |
409 if (order != -1) { | |
410 // If the dart:* library we are currently compiling is loaded after the | |
411 // class's library, then we know the class is available. | |
412 var currentOrder = CORELIB_ORDER.indexOf(currentLibrary.name); | |
413 return currentOrder == -1 ? false : (currentOrder < order); | |
414 } | |
415 } | |
416 | |
417 return _inLibraryCycle(cls.library); | |
418 } | |
419 | |
420 /// Returns true if [library] depends on the [currentLibrary] via some | |
421 /// transitive import. | |
422 bool _inLibraryCycle(LibraryElement library) { | |
423 // SDK libs don't depend on things outside the SDK. | |
424 if (library.isInSdk && !currentLibrary.isInSdk) return false; | |
Siggi Cherem (dart-lang)
2015/03/18 23:54:55
maybe move the second condition to be an assertion
Jennifer Messerly
2015/03/19 16:03:29
I think it actually can be called via recursive pa
| |
425 | |
426 var result = _libraryCycleMemo[library]; | |
427 if (result != null) return result; | |
428 | |
429 result = library == currentLibrary; | |
430 _libraryCycleMemo[library] = result; | |
431 for (var e in library.imports) { | |
432 if (result) break; | |
433 result = _inLibraryCycle(e.importedLibrary); | |
434 } | |
435 for (var e in library.exports) { | |
436 if (result) break; | |
437 result = _inLibraryCycle(e.exportedLibrary); | |
438 } | |
439 return _libraryCycleMemo[library] = result; | |
440 } | |
441 | |
442 JS.Statement _emitGenericClassDef(ClassElement cls, JS.Statement body) { | |
443 var name = cls.name; | |
444 var genericName = '$name\$'; | |
445 var typeParams = cls.typeParameters.map((p) => new JS.Parameter(p.name)); | |
446 // TODO(jmesserly): is it worth exporting both names? Alternatively we could | |
447 // put the generic type constructor on the <dynamic> instance. | |
448 if (isPublic(name)) _exports.add(genericName); | |
449 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ | |
450 genericName, | |
451 typeParams, | |
452 body, | |
453 name | |
454 ]); | |
455 } | |
456 | |
457 JS.Expression _classHeritage(ClassDeclaration node) { | |
458 if (node.element.type.isObject) return null; | |
459 | |
460 JS.Expression heritage = null; | |
461 if (node.extendsClause != null) { | |
462 heritage = _visit(node.extendsClause.superclass); | |
463 } else { | |
464 heritage = _emitTypeName(rules.provider.objectType); | |
465 } | |
466 if (node.withClause != null) { | |
467 var mixins = _visitList(node.withClause.mixinTypes); | |
468 mixins.insert(0, heritage); | |
469 heritage = js.call('dart.mixin(#)', [mixins]); | |
470 } | |
471 return heritage; | |
472 } | |
473 | |
474 /// Emit class members that can be generated as methods. | |
475 /// Anything not handled here will be addressed in [_finishClassMembers]. | |
476 List<JS.Method> _emitClassMethods(ClassDeclaration node, | |
477 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | |
478 var element = node.element; | |
479 var isObject = element.type.isObject; | |
480 var name = node.name.name; | |
481 | |
289 var jsMethods = <JS.Method>[]; | 482 var jsMethods = <JS.Method>[]; |
290 // Iff no constructor is specified for a class C, it implicitly has a | 483 // Iff no constructor is specified for a class C, it implicitly has a |
291 // default constructor `C() : super() {}`, unless C is class Object. | 484 // default constructor `C() : super() {}`, unless C is class Object. |
292 if (ctors.isEmpty && !isObject) { | 485 if (ctors.isEmpty && !isObject) { |
293 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 486 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
294 } | 487 } |
295 | 488 |
296 for (var member in node.members) { | 489 for (var member in node.members) { |
297 if (member is ConstructorDeclaration) { | 490 if (member is ConstructorDeclaration) { |
298 jsMethods.add(_emitConstructor(member, name, fields, isObject)); | 491 jsMethods.add(_emitConstructor(member, name, fields, isObject)); |
299 } else if (member is MethodDeclaration) { | 492 } else if (member is MethodDeclaration) { |
300 jsMethods.add(_visit(member)); | 493 jsMethods.add(_visit(member)); |
301 } | 494 } |
302 } | 495 } |
303 | 496 |
304 // Support for adapting dart:core Iterator/Iterable to ES6 versions. | 497 // Support for adapting dart:core Iterator/Iterable to ES6 versions. |
305 // This lets them use for-of loops transparently. | 498 // This lets them use for-of loops transparently. |
306 // https://github.com/lukehoban/es6features#iterators--forof | 499 // https://github.com/lukehoban/es6features#iterators--forof |
307 if (node.element.library.isDartCore && node.element.name == 'Iterable') { | 500 if (element.library.isDartCore && element.name == 'Iterable') { |
308 JS.Fun body = js.call('''function() { | 501 JS.Fun body = js.call('''function() { |
309 var iterator = this.iterator; | 502 var iterator = this.iterator; |
310 return { | 503 return { |
311 next() { | 504 next() { |
312 var done = iterator.moveNext(); | 505 var done = iterator.moveNext(); |
313 return { done: done, current: done ? void 0 : iterator.current }; | 506 return { done: done, current: done ? void 0 : iterator.current }; |
314 } | 507 } |
315 }; | 508 }; |
316 }'''); | 509 }'''); |
317 jsMethods.add(new JS.Method(js.call('Symbol.iterator'), body)); | 510 jsMethods.add(new JS.Method(js.call('Symbol.iterator'), body)); |
318 } | 511 } |
512 return jsMethods.where((m) => m != null).toList(growable: false); | |
513 } | |
319 | 514 |
320 JS.Expression heritage = null; | 515 /// Emit class members that need to come after the class declaration, such |
321 if (node.extendsClause != null) { | 516 /// as static fields. See [_emitClassMethods] for things that are emitted |
322 heritage = _visit(node.extendsClause.superclass); | 517 /// insite the ES6 `class { ... }` node. |
323 } else if (!isObject) { | 518 JS.Statement _finishClassMembers(String name, JS.ClassExpression cls, |
324 heritage = _emitTypeName(rules.provider.objectType); | 519 List<ConstructorDeclaration> ctors, List<FieldDeclaration> staticFields) { |
325 } | 520 var body = <JS.Statement>[]; |
326 if (node.withClause != null) { | 521 body.add(new JS.ClassDeclaration(cls)); |
327 var mixins = _visitList(node.withClause.mixinTypes); | |
328 mixins.insert(0, heritage); | |
329 heritage = js.call('dart.mixin(#)', [mixins]); | |
330 } | |
331 body.add(new JS.ClassDeclaration(new JS.ClassExpression( | |
332 new JS.VariableDeclaration(name), heritage, | |
333 jsMethods.where((m) => m != null).toList(growable: false)))); | |
334 | |
335 if (isPublic(name)) _exports.add(name); | |
336 | 522 |
337 // Named constructors | 523 // Named constructors |
338 for (ConstructorDeclaration member in ctors) { | 524 for (ConstructorDeclaration member in ctors) { |
339 if (member.name != null) { | 525 if (member.name != null) { |
340 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ | 526 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
341 name, | 527 name, |
342 js.string(member.name.name, "'") | 528 js.string(member.name.name, "'") |
343 ])); | 529 ])); |
344 } | 530 } |
345 } | 531 } |
346 | 532 |
347 // Static fields | 533 // Static fields |
348 var lazyStatics = <VariableDeclaration>[]; | 534 var lazyStatics = <VariableDeclaration>[]; |
349 for (FieldDeclaration member in staticFields) { | 535 for (FieldDeclaration member in staticFields) { |
350 for (VariableDeclaration field in member.fields.variables) { | 536 for (VariableDeclaration field in member.fields.variables) { |
351 var fieldName = field.name.name; | 537 var fieldName = field.name.name; |
352 if (field.isConst || _isFieldInitConstant(field)) { | 538 if (field.isConst || _isFieldInitConstant(field)) { |
353 var init = _visit(field.initializer); | 539 var init = _visit(field.initializer); |
354 if (init == null) init = new JS.LiteralNull(); | 540 if (init == null) init = new JS.LiteralNull(); |
355 body.add(js.statement('#.# = #;', [name, fieldName, init])); | 541 body.add(js.statement('#.# = #;', [name, fieldName, init])); |
356 } else { | 542 } else { |
357 lazyStatics.add(field); | 543 lazyStatics.add(field); |
358 } | 544 } |
359 } | 545 } |
360 } | 546 } |
361 var lazy = _emitLazyFields(name, lazyStatics); | 547 var lazy = _emitLazyFields(name, lazyStatics); |
362 if (lazy != null) body.add(lazy); | 548 if (lazy != null) body.add(lazy); |
363 | 549 return _statement(body); |
364 currentClass = null; | |
365 return _addTypeParameters(node.typeParameters, name, _statement(body)); | |
366 } | 550 } |
367 | 551 |
368 /// Generates the implicit default constructor for class C of the form | 552 /// Generates the implicit default constructor for class C of the form |
369 /// `C() : super() {}`. | 553 /// `C() : super() {}`. |
370 JS.Method _emitImplicitConstructor( | 554 JS.Method _emitImplicitConstructor( |
371 ClassDeclaration node, String name, List<FieldDeclaration> fields) { | 555 ClassDeclaration node, String name, List<FieldDeclaration> fields) { |
372 // If we don't have a method body, skip this. | 556 // If we don't have a method body, skip this. |
373 if (fields.isEmpty) return null; | 557 if (fields.isEmpty) return null; |
374 | 558 |
375 dynamic body = _initializeFields(fields); | 559 dynamic body = _initializeFields(fields); |
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
735 var className = (variable.enclosingElement as ClassElement).name; | 919 var className = (variable.enclosingElement as ClassElement).name; |
736 return js.call('#.#', [className, name]); | 920 return js.call('#.#', [className, name]); |
737 } else if (e is ParameterElement && e.isInitializingFormal) { | 921 } else if (e is ParameterElement && e.isInitializingFormal) { |
738 name = _fieldParameterName(name); | 922 name = _fieldParameterName(name); |
739 } | 923 } |
740 return new JS.VariableUse(name); | 924 return new JS.VariableUse(name); |
741 } | 925 } |
742 | 926 |
743 JS.Expression _emitTypeName(DartType type) { | 927 JS.Expression _emitTypeName(DartType type) { |
744 var name = type.name; | 928 var name = type.name; |
745 var lib = type.element.library; | 929 var element = type.element; |
746 if (name == '') { | 930 if (name == '') { |
747 // TODO(jmesserly): remove when we're using coercion reifier. | 931 // TODO(jmesserly): remove when we're using coercion reifier. |
748 return _unimplementedCall('Unimplemented type $type'); | 932 return _unimplementedCall('Unimplemented type $type'); |
749 } | 933 } |
750 | 934 |
751 var typeArgs = null; | 935 var typeArgs = null; |
752 if (type is ParameterizedType) { | 936 if (type is ParameterizedType) { |
753 // TODO(jmesserly): this is a workaround for an analyzer bug, see: | 937 // TODO(jmesserly): this is a workaround for an analyzer bug, see: |
754 // https://github.com/dart-lang/dart-dev-compiler/commit/a212d59ad046085a6 26dd8d16881cdb8e8b9c3fa | 938 // https://github.com/dart-lang/dev_compiler/commit/a212d59ad046085a626dd8 d16881cdb8e8b9c3fa |
755 if (type is! FunctionType || type.element is FunctionTypeAlias) { | 939 if (type is! FunctionType || element is FunctionTypeAlias) { |
756 var args = type.typeArguments; | 940 var args = type.typeArguments; |
757 if (args.any((a) => a != rules.provider.dynamicType)) { | 941 if (args.any((a) => a != rules.provider.dynamicType)) { |
758 name = '$name\$'; | 942 name = '$name\$'; |
759 typeArgs = args.map(_emitTypeName); | 943 typeArgs = args.map(_emitTypeName); |
760 } | 944 } |
761 } | 945 } |
762 } | 946 } |
763 | 947 |
764 JS.Expression result; | 948 JS.Expression result; |
765 if (lib != currentLibrary && lib != null) { | 949 if (_needQualifiedName(element)) { |
766 result = js.call('#.#', [_libraryName(lib), name]); | 950 result = js.call('#.#', [_libraryName(element.library), name]); |
767 } else { | 951 } else { |
768 result = new JS.VariableUse(name); | 952 result = new JS.VariableUse(name); |
769 } | 953 } |
770 | 954 |
771 if (typeArgs != null) { | 955 if (typeArgs != null) { |
772 result = js.call('#(#)', [result, typeArgs]); | 956 result = js.call('#(#)', [result, typeArgs]); |
773 } | 957 } |
774 return result; | 958 return result; |
775 } | 959 } |
776 | 960 |
961 bool _needQualifiedName(Element element) { | |
962 var lib = element.library; | |
963 | |
964 return lib != null && | |
965 (lib != currentLibrary || | |
966 element is ClassElement && _lazyClass(element)); | |
967 } | |
968 | |
777 JS.Node _emitDPutIfDynamic( | 969 JS.Node _emitDPutIfDynamic( |
778 Expression target, SimpleIdentifier id, Expression rhs) { | 970 Expression target, SimpleIdentifier id, Expression rhs) { |
779 if (rules.isDynamicTarget(target)) { | 971 if (rules.isDynamicTarget(target)) { |
780 return js.call('dart.dput(#, #, #)', [ | 972 return js.call('dart.dput(#, #, #)', [ |
781 _visit(target), | 973 _visit(target), |
782 js.string(id.name, "'"), | 974 js.string(id.name, "'"), |
783 _visit(rhs) | 975 _visit(rhs) |
784 ]); | 976 ]); |
785 } else { | 977 } else { |
786 return null; | 978 return null; |
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1078 | 1270 |
1079 // TODO(jmesserly): use a dummy setter to indicate writable. | 1271 // TODO(jmesserly): use a dummy setter to indicate writable. |
1080 if (!node.isFinal) { | 1272 if (!node.isFinal) { |
1081 methods.add(new JS.Method( | 1273 methods.add(new JS.Method( |
1082 new JS.PropertyName(name), js.call('function(_) {}'), | 1274 new JS.PropertyName(name), js.call('function(_) {}'), |
1083 isSetter: true)); | 1275 isSetter: true)); |
1084 } | 1276 } |
1085 } | 1277 } |
1086 | 1278 |
1087 return js.statement( | 1279 return js.statement( |
1088 'dart.defineLazyProperties(#, { # })', [objExpr, methods]); | 1280 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
1089 } | 1281 } |
1090 | 1282 |
1091 void _flushLibraryProperties(List<JS.Statement> body) { | 1283 void _flushLibraryProperties(List<JS.Statement> body) { |
1092 if (_properties.isEmpty) return; | 1284 if (_properties.isEmpty) return; |
1093 body.add(js.statement('dart.copyProperties($_EXPORTS, { # });', | 1285 body.add(js.statement('dart.copyProperties($_EXPORTS, { # });', |
1094 [_properties.map(_emitTopLevelProperty)])); | 1286 [_properties.map(_emitTopLevelProperty)])); |
1095 _properties.clear(); | 1287 _properties.clear(); |
1096 } | 1288 } |
1097 | 1289 |
1098 @override | 1290 @override |
(...skipping 996 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2095 | 2287 |
2096 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2288 // TODO(jmesserly): in many cases marking the end will be unncessary. |
2097 printer.mark(_location(node.end)); | 2289 printer.mark(_location(node.end)); |
2098 } | 2290 } |
2099 | 2291 |
2100 String _getIdentifier(AstNode node) { | 2292 String _getIdentifier(AstNode node) { |
2101 if (node is SimpleIdentifier) return node.name; | 2293 if (node is SimpleIdentifier) return node.name; |
2102 return null; | 2294 return null; |
2103 } | 2295 } |
2104 } | 2296 } |
OLD | NEW |