Chromium Code Reviews| 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 |