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 return js.statement( |
| 326 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ |
| 327 _EXPORTS, |
| 328 name, |
| 329 body, |
| 330 name |
| 331 ]); |
| 332 } |
| 333 |
| 334 if (genericDef != null) { |
| 335 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
| 336 } |
| 337 |
| 338 if (classElem.type.isObject) return body; |
| 339 |
| 340 // If we're not lazy, we still need to ensure our dependencies are |
| 341 // generated first. |
| 342 var classDefs = <JS.Statement>[]; |
| 343 _emitClassIfNeeded(classDefs, classElem.supertype.element); |
| 344 for (var m in classElem.mixins) { |
| 345 _emitClassIfNeeded(classDefs, m.element); |
| 346 } |
| 347 classDefs.add(body); |
| 348 return _statement(classDefs); |
| 349 } |
| 350 |
| 351 void _emitClassIfNeeded(List<JS.Statement> defs, ClassElement base) { |
| 352 // We can only emit classes from this library. |
| 353 if (base.library != currentLibrary) return; |
| 354 |
| 355 var baseNode = _pendingClasses[base]; |
| 356 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); |
| 357 } |
| 358 |
| 359 /// Returns true if the supertype or mixins aren't loaded. |
| 360 /// If that is the case, we'll emit a lazy class definition. |
| 361 bool _lazyClass(ClassElement cls) { |
| 362 if (cls.type.isObject) return false; |
| 363 |
| 364 assert(cls.library == currentLibrary); |
| 365 var result = _lazyClassMemo[cls]; |
| 366 if (result != null) return result; |
| 367 |
| 368 result = _classMightNotBeLoaded(cls.supertype.element); |
| 369 for (var mixin in cls.mixins) { |
| 370 if (result) break; |
| 371 result = _classMightNotBeLoaded(mixin.element); |
| 372 } |
| 373 return _lazyClassMemo[cls] = result; |
| 374 } |
| 375 |
| 376 /// Curated order to minimize lazy classes needed by dart:core and its |
| 377 /// transitive SDK imports. |
| 378 static const CORELIB_ORDER = const [ |
| 379 'dart.core', |
| 380 'dart.collection', |
| 381 'dart._internal' |
| 382 ]; |
| 383 |
| 384 /// Returns true if the class might not be loaded. |
| 385 /// |
| 386 /// If the class is from our library, this can happen because it's lazy. |
| 387 /// |
| 388 /// If the class is from a different library, it could happen if we're in |
| 389 /// a library cycle. In other words, if that different library depends back |
| 390 /// on this library via some transitive import path. |
| 391 /// |
| 392 /// If we could control the global import ordering, we could eliminate some |
| 393 /// of these cases, by ordering the imports of the cyclic libraries in an |
| 394 /// optimal way. For example, we could order the libraries in a cycle to |
| 395 /// minimize laziness. However, we currently assume we cannot control the |
| 396 /// order that the cycle of libraries will be loaded in. |
| 397 bool _classMightNotBeLoaded(ClassElement cls) { |
| 398 if (cls.library == currentLibrary) return _lazyClass(cls); |
| 399 |
| 400 // The SDK is a special case: we optimize the order to prevent laziness. |
| 401 if (cls.library.isInSdk) { |
| 402 // SDK is loaded before non-SDK libraies |
| 403 if (!currentLibrary.isInSdk) return false; |
| 404 |
| 405 // Compute the order of both SDK libraries. If unknown, assume it's after. |
| 406 var classOrder = CORELIB_ORDER.indexOf(cls.library.name); |
| 407 if (classOrder == -1) classOrder = CORELIB_ORDER.length; |
| 408 |
| 409 var currentOrder = CORELIB_ORDER.indexOf(currentLibrary.name); |
| 410 if (currentOrder == -1) currentOrder = CORELIB_ORDER.length; |
| 411 |
| 412 // If the dart:* library we are currently compiling is loaded after the |
| 413 // class's library, then we know the class is available. |
| 414 if (classOrder != currentOrder) return currentOrder < classOrder; |
| 415 |
| 416 // If we don't know the order of the class's library or the current |
| 417 // library, do the normal cycle check. (Not all SDK libs are cycles.) |
| 418 } |
| 419 |
| 420 return _inLibraryCycle(cls.library); |
| 421 } |
| 422 |
| 423 /// Returns true if [library] depends on the [currentLibrary] via some |
| 424 /// transitive import. |
| 425 bool _inLibraryCycle(LibraryElement library) { |
| 426 // SDK libs don't depend on things outside the SDK. |
| 427 // (We can reach this via the recursive call below.) |
| 428 if (library.isInSdk && !currentLibrary.isInSdk) return false; |
| 429 |
| 430 var result = _libraryCycleMemo[library]; |
| 431 if (result != null) return result; |
| 432 |
| 433 result = library == currentLibrary; |
| 434 _libraryCycleMemo[library] = result; |
| 435 for (var e in library.imports) { |
| 436 if (result) break; |
| 437 result = _inLibraryCycle(e.importedLibrary); |
| 438 } |
| 439 for (var e in library.exports) { |
| 440 if (result) break; |
| 441 result = _inLibraryCycle(e.exportedLibrary); |
| 442 } |
| 443 return _libraryCycleMemo[library] = result; |
| 444 } |
| 445 |
| 446 JS.Statement _emitGenericClassDef(ClassElement cls, JS.Statement body) { |
| 447 var name = cls.name; |
| 448 var genericName = '$name\$'; |
| 449 var typeParams = cls.typeParameters.map((p) => new JS.Parameter(p.name)); |
| 450 // TODO(jmesserly): is it worth exporting both names? Alternatively we could |
| 451 // put the generic type constructor on the <dynamic> instance. |
| 452 if (isPublic(name)) _exports.add(genericName); |
| 453 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ |
| 454 genericName, |
| 455 typeParams, |
| 456 body, |
| 457 name |
| 458 ]); |
| 459 } |
| 460 |
| 461 JS.Expression _classHeritage(ClassDeclaration node) { |
| 462 if (node.element.type.isObject) return null; |
| 463 |
| 464 JS.Expression heritage = null; |
| 465 if (node.extendsClause != null) { |
| 466 heritage = _visit(node.extendsClause.superclass); |
| 467 } else { |
| 468 heritage = _emitTypeName(rules.provider.objectType); |
| 469 } |
| 470 if (node.withClause != null) { |
| 471 var mixins = _visitList(node.withClause.mixinTypes); |
| 472 mixins.insert(0, heritage); |
| 473 heritage = js.call('dart.mixin(#)', [mixins]); |
| 474 } |
| 475 return heritage; |
| 476 } |
| 477 |
| 478 /// Emit class members that can be generated as methods. |
| 479 /// Anything not handled here will be addressed in [_finishClassMembers]. |
| 480 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
| 481 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
| 482 var element = node.element; |
| 483 var isObject = element.type.isObject; |
| 484 var name = node.name.name; |
| 485 |
289 var jsMethods = <JS.Method>[]; | 486 var jsMethods = <JS.Method>[]; |
290 // Iff no constructor is specified for a class C, it implicitly has a | 487 // Iff no constructor is specified for a class C, it implicitly has a |
291 // default constructor `C() : super() {}`, unless C is class Object. | 488 // default constructor `C() : super() {}`, unless C is class Object. |
292 if (ctors.isEmpty && !isObject) { | 489 if (ctors.isEmpty && !isObject) { |
293 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 490 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
294 } | 491 } |
295 | 492 |
296 for (var member in node.members) { | 493 for (var member in node.members) { |
297 if (member is ConstructorDeclaration) { | 494 if (member is ConstructorDeclaration) { |
298 jsMethods.add(_emitConstructor(member, name, fields, isObject)); | 495 jsMethods.add(_emitConstructor(member, name, fields, isObject)); |
299 } else if (member is MethodDeclaration) { | 496 } else if (member is MethodDeclaration) { |
300 jsMethods.add(_visit(member)); | 497 jsMethods.add(_visit(member)); |
301 } | 498 } |
302 } | 499 } |
303 | 500 |
304 // Support for adapting dart:core Iterator/Iterable to ES6 versions. | 501 // Support for adapting dart:core Iterator/Iterable to ES6 versions. |
305 // This lets them use for-of loops transparently. | 502 // This lets them use for-of loops transparently. |
306 // https://github.com/lukehoban/es6features#iterators--forof | 503 // https://github.com/lukehoban/es6features#iterators--forof |
307 if (node.element.library.isDartCore && node.element.name == 'Iterable') { | 504 if (element.library.isDartCore && element.name == 'Iterable') { |
308 JS.Fun body = js.call('''function() { | 505 JS.Fun body = js.call('''function() { |
309 var iterator = this.iterator; | 506 var iterator = this.iterator; |
310 return { | 507 return { |
311 next() { | 508 next() { |
312 var done = iterator.moveNext(); | 509 var done = iterator.moveNext(); |
313 return { done: done, current: done ? void 0 : iterator.current }; | 510 return { done: done, current: done ? void 0 : iterator.current }; |
314 } | 511 } |
315 }; | 512 }; |
316 }'''); | 513 }'''); |
317 jsMethods.add(new JS.Method(js.call('Symbol.iterator'), body)); | 514 jsMethods.add(new JS.Method(js.call('Symbol.iterator'), body)); |
318 } | 515 } |
| 516 return jsMethods.where((m) => m != null).toList(growable: false); |
| 517 } |
319 | 518 |
320 JS.Expression heritage = null; | 519 /// Emit class members that need to come after the class declaration, such |
321 if (node.extendsClause != null) { | 520 /// as static fields. See [_emitClassMethods] for things that are emitted |
322 heritage = _visit(node.extendsClause.superclass); | 521 /// insite the ES6 `class { ... }` node. |
323 } else if (!isObject) { | 522 JS.Statement _finishClassMembers(String name, JS.ClassExpression cls, |
324 heritage = _emitTypeName(rules.provider.objectType); | 523 List<ConstructorDeclaration> ctors, List<FieldDeclaration> staticFields) { |
325 } | 524 var body = <JS.Statement>[]; |
326 if (node.withClause != null) { | 525 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 | 526 |
337 // Named constructors | 527 // Named constructors |
338 for (ConstructorDeclaration member in ctors) { | 528 for (ConstructorDeclaration member in ctors) { |
339 if (member.name != null) { | 529 if (member.name != null) { |
340 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ | 530 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
341 name, | 531 name, |
342 js.string(member.name.name, "'") | 532 js.string(member.name.name, "'") |
343 ])); | 533 ])); |
344 } | 534 } |
345 } | 535 } |
346 | 536 |
347 // Static fields | 537 // Static fields |
348 var lazyStatics = <VariableDeclaration>[]; | 538 var lazyStatics = <VariableDeclaration>[]; |
349 for (FieldDeclaration member in staticFields) { | 539 for (FieldDeclaration member in staticFields) { |
350 for (VariableDeclaration field in member.fields.variables) { | 540 for (VariableDeclaration field in member.fields.variables) { |
351 var fieldName = field.name.name; | 541 var fieldName = field.name.name; |
352 if (field.isConst || _isFieldInitConstant(field)) { | 542 if (field.isConst || _isFieldInitConstant(field)) { |
353 var init = _visit(field.initializer); | 543 var init = _visit(field.initializer); |
354 if (init == null) init = new JS.LiteralNull(); | 544 if (init == null) init = new JS.LiteralNull(); |
355 body.add(js.statement('#.# = #;', [name, fieldName, init])); | 545 body.add(js.statement('#.# = #;', [name, fieldName, init])); |
356 } else { | 546 } else { |
357 lazyStatics.add(field); | 547 lazyStatics.add(field); |
358 } | 548 } |
359 } | 549 } |
360 } | 550 } |
361 var lazy = _emitLazyFields(name, lazyStatics); | 551 var lazy = _emitLazyFields(name, lazyStatics); |
362 if (lazy != null) body.add(lazy); | 552 if (lazy != null) body.add(lazy); |
363 | 553 return _statement(body); |
364 currentClass = null; | |
365 return _addTypeParameters(node.typeParameters, name, _statement(body)); | |
366 } | 554 } |
367 | 555 |
368 /// Generates the implicit default constructor for class C of the form | 556 /// Generates the implicit default constructor for class C of the form |
369 /// `C() : super() {}`. | 557 /// `C() : super() {}`. |
370 JS.Method _emitImplicitConstructor( | 558 JS.Method _emitImplicitConstructor( |
371 ClassDeclaration node, String name, List<FieldDeclaration> fields) { | 559 ClassDeclaration node, String name, List<FieldDeclaration> fields) { |
372 // If we don't have a method body, skip this. | 560 // If we don't have a method body, skip this. |
373 if (fields.isEmpty) return null; | 561 if (fields.isEmpty) return null; |
374 | 562 |
375 dynamic body = _initializeFields(fields); | 563 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; | 923 var className = (variable.enclosingElement as ClassElement).name; |
736 return js.call('#.#', [className, name]); | 924 return js.call('#.#', [className, name]); |
737 } else if (e is ParameterElement && e.isInitializingFormal) { | 925 } else if (e is ParameterElement && e.isInitializingFormal) { |
738 name = _fieldParameterName(name); | 926 name = _fieldParameterName(name); |
739 } | 927 } |
740 return new JS.VariableUse(name); | 928 return new JS.VariableUse(name); |
741 } | 929 } |
742 | 930 |
743 JS.Expression _emitTypeName(DartType type) { | 931 JS.Expression _emitTypeName(DartType type) { |
744 var name = type.name; | 932 var name = type.name; |
745 var lib = type.element.library; | 933 var element = type.element; |
746 if (name == '') { | 934 if (name == '') { |
747 // TODO(jmesserly): remove when we're using coercion reifier. | 935 // TODO(jmesserly): remove when we're using coercion reifier. |
748 return _unimplementedCall('Unimplemented type $type'); | 936 return _unimplementedCall('Unimplemented type $type'); |
749 } | 937 } |
750 | 938 |
751 var typeArgs = null; | 939 var typeArgs = null; |
752 if (type is ParameterizedType) { | 940 if (type is ParameterizedType) { |
753 // TODO(jmesserly): this is a workaround for an analyzer bug, see: | 941 // TODO(jmesserly): this is a workaround for an analyzer bug, see: |
754 // https://github.com/dart-lang/dart-dev-compiler/commit/a212d59ad046085a6
26dd8d16881cdb8e8b9c3fa | 942 // https://github.com/dart-lang/dev_compiler/commit/a212d59ad046085a626dd8
d16881cdb8e8b9c3fa |
755 if (type is! FunctionType || type.element is FunctionTypeAlias) { | 943 if (type is! FunctionType || element is FunctionTypeAlias) { |
756 var args = type.typeArguments; | 944 var args = type.typeArguments; |
757 if (args.any((a) => a != rules.provider.dynamicType)) { | 945 if (args.any((a) => a != rules.provider.dynamicType)) { |
758 name = '$name\$'; | 946 name = '$name\$'; |
759 typeArgs = args.map(_emitTypeName); | 947 typeArgs = args.map(_emitTypeName); |
760 } | 948 } |
761 } | 949 } |
762 } | 950 } |
763 | 951 |
764 JS.Expression result; | 952 JS.Expression result; |
765 if (lib != currentLibrary && lib != null) { | 953 if (_needQualifiedName(element)) { |
766 result = js.call('#.#', [_libraryName(lib), name]); | 954 result = js.call('#.#', [_libraryName(element.library), name]); |
767 } else { | 955 } else { |
768 result = new JS.VariableUse(name); | 956 result = new JS.VariableUse(name); |
769 } | 957 } |
770 | 958 |
771 if (typeArgs != null) { | 959 if (typeArgs != null) { |
772 result = js.call('#(#)', [result, typeArgs]); | 960 result = js.call('#(#)', [result, typeArgs]); |
773 } | 961 } |
774 return result; | 962 return result; |
775 } | 963 } |
776 | 964 |
| 965 bool _needQualifiedName(Element element) { |
| 966 var lib = element.library; |
| 967 |
| 968 return lib != null && |
| 969 (lib != currentLibrary || |
| 970 element is ClassElement && _lazyClass(element)); |
| 971 } |
| 972 |
777 JS.Node _emitDPutIfDynamic( | 973 JS.Node _emitDPutIfDynamic( |
778 Expression target, SimpleIdentifier id, Expression rhs) { | 974 Expression target, SimpleIdentifier id, Expression rhs) { |
779 if (rules.isDynamicTarget(target)) { | 975 if (rules.isDynamicTarget(target)) { |
780 return js.call('dart.dput(#, #, #)', [ | 976 return js.call('dart.dput(#, #, #)', [ |
781 _visit(target), | 977 _visit(target), |
782 js.string(id.name, "'"), | 978 js.string(id.name, "'"), |
783 _visit(rhs) | 979 _visit(rhs) |
784 ]); | 980 ]); |
785 } else { | 981 } else { |
786 return null; | 982 return null; |
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1078 | 1274 |
1079 // TODO(jmesserly): use a dummy setter to indicate writable. | 1275 // TODO(jmesserly): use a dummy setter to indicate writable. |
1080 if (!node.isFinal) { | 1276 if (!node.isFinal) { |
1081 methods.add(new JS.Method( | 1277 methods.add(new JS.Method( |
1082 new JS.PropertyName(name), js.call('function(_) {}'), | 1278 new JS.PropertyName(name), js.call('function(_) {}'), |
1083 isSetter: true)); | 1279 isSetter: true)); |
1084 } | 1280 } |
1085 } | 1281 } |
1086 | 1282 |
1087 return js.statement( | 1283 return js.statement( |
1088 'dart.defineLazyProperties(#, { # })', [objExpr, methods]); | 1284 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
1089 } | 1285 } |
1090 | 1286 |
1091 void _flushLibraryProperties(List<JS.Statement> body) { | 1287 void _flushLibraryProperties(List<JS.Statement> body) { |
1092 if (_properties.isEmpty) return; | 1288 if (_properties.isEmpty) return; |
1093 body.add(js.statement('dart.copyProperties($_EXPORTS, { # });', | 1289 body.add(js.statement('dart.copyProperties($_EXPORTS, { # });', |
1094 [_properties.map(_emitTopLevelProperty)])); | 1290 [_properties.map(_emitTopLevelProperty)])); |
1095 _properties.clear(); | 1291 _properties.clear(); |
1096 } | 1292 } |
1097 | 1293 |
1098 @override | 1294 @override |
(...skipping 996 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2095 | 2291 |
2096 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2292 // TODO(jmesserly): in many cases marking the end will be unncessary. |
2097 printer.mark(_location(node.end)); | 2293 printer.mark(_location(node.end)); |
2098 } | 2294 } |
2099 | 2295 |
2100 String _getIdentifier(AstNode node) { | 2296 String _getIdentifier(AstNode node) { |
2101 if (node is SimpleIdentifier) return node.name; | 2297 if (node is SimpleIdentifier) return node.name; |
2102 return null; | 2298 return null; |
2103 } | 2299 } |
2104 } | 2300 } |
OLD | NEW |