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, HashMap; | 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/resolver.dart' show TypeProvider; | |
| 14 import 'package:analyzer/src/generated/scanner.dart' | 15 import 'package:analyzer/src/generated/scanner.dart' |
| 15 show StringToken, Token, TokenType; | 16 show StringToken, Token, TokenType; |
| 16 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; | 17 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; |
| 17 import 'package:source_maps/source_maps.dart' show SourceMapSpan; | 18 import 'package:source_maps/source_maps.dart' show SourceMapSpan; |
| 18 import 'package:source_span/source_span.dart' show SourceLocation; | 19 import 'package:source_span/source_span.dart' show SourceLocation; |
| 19 import 'package:path/path.dart' as path; | 20 import 'package:path/path.dart' as path; |
| 20 | 21 |
| 21 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; | 22 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; |
| 22 | 23 |
| 23 // TODO(jmesserly): import from its own package | 24 // TODO(jmesserly): import from its own package |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 44 | 45 |
| 45 // TODO(jacobr): we would like to do something like the following | 46 // TODO(jacobr): we would like to do something like the following |
| 46 // but we don't have summary support yet. | 47 // but we don't have summary support yet. |
| 47 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 48 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 48 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 49 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| 49 | 50 |
| 50 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 51 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 51 final LibraryInfo libraryInfo; | 52 final LibraryInfo libraryInfo; |
| 52 final TypeRules rules; | 53 final TypeRules rules; |
| 53 | 54 |
| 55 /// The global extension method table. | |
| 56 final HashMap<String, List<InterfaceType>> _extensionMethods; | |
| 57 | |
| 54 /// The variable for the target of the current `..` cascade expression. | 58 /// The variable for the target of the current `..` cascade expression. |
| 55 SimpleIdentifier _cascadeTarget; | 59 SimpleIdentifier _cascadeTarget; |
| 56 /// The variable for the current catch clause | 60 /// The variable for the current catch clause |
| 57 SimpleIdentifier _catchParameter; | 61 SimpleIdentifier _catchParameter; |
| 58 | 62 |
| 59 ClassDeclaration currentClass; | 63 ClassDeclaration currentClass; |
| 60 ConstantEvaluator _constEvaluator; | 64 ConstantEvaluator _constEvaluator; |
| 61 | 65 |
| 62 final _exports = new Set<String>(); | 66 final _exports = new Set<String>(); |
| 63 final _lazyFields = <VariableDeclaration>[]; | 67 final _lazyFields = <VariableDeclaration>[]; |
| 64 final _properties = <FunctionDeclaration>[]; | 68 final _properties = <FunctionDeclaration>[]; |
| 65 final _privateNames = new HashSet<String>(); | 69 final _privateNames = new HashSet<String>(); |
| 66 final _pendingPrivateNames = <String>[]; | 70 final _pendingPrivateNames = <String>[]; |
| 67 final _extensionMethodNames = new HashSet<String>(); | 71 final _extensionMethodNames = new HashSet<String>(); |
| 68 final _pendingExtensionMethodNames = <String>[]; | 72 final _pendingExtensionMethodNames = <String>[]; |
| 69 | 73 |
| 70 InterfaceType _fillDynamicTypeArgs(InterfaceType t) { | 74 /// The name for the library's exports inside itself. |
| 71 var d = rules.provider.dynamicType; | 75 /// This much be a constant because we interpolate it into template strings, |
| 72 return t.substitute4(new List.filled(t.typeArguments.length, d)); | 76 /// and otherwise it would break caching for them. |
| 73 } | 77 /// `exports` was chosen as the most similar to ES module patterns. |
| 74 // TODO(jacobr): determine the the set of types with extension methods from | 78 final JSTemporary _exportsVar = new JSTemporary('exports'); |
| 75 // the annotations rather than hard coding the list once the analyzer | 79 final JSTemporary _namedArgTemp = new JSTemporary('opts'); |
| 76 // supports summaries. | |
| 77 List<InterfaceType> _jsExtensionMethodTypes; | |
| 78 List<InterfaceType> get jsExtensionMethodTypes { | |
| 79 if (_jsExtensionMethodTypes != null) return _jsExtensionMethodTypes; | |
| 80 _jsExtensionMethodTypes = <InterfaceType>[ | |
| 81 rules.provider.listType, | |
| 82 rules.provider.iterableType | |
| 83 ].map(_fillDynamicTypeArgs).toList(); | |
| 84 return _jsExtensionMethodTypes; | |
| 85 } | |
| 86 | |
| 87 Map<ClassElement, Set<String>> _extensionMethods; | |
| 88 | |
| 89 Map<ClassElement, Set<String>> get extensionMethods { | |
| 90 if (_extensionMethods != null) return _extensionMethods; | |
| 91 _extensionMethods = new HashMap<ClassElement, HashSet<String>>(); | |
| 92 | |
| 93 for (var type in jsExtensionMethodTypes) { | |
| 94 var names = new HashSet<String>(); | |
| 95 var e = type.element; | |
| 96 names.addAll(e.methods.map((m) => m.name)); | |
| 97 names.addAll(e.accessors.map((m) => m.name)); | |
| 98 _extensionMethods[e] = names; | |
| 99 } | |
| 100 return _extensionMethods; | |
| 101 } | |
| 102 | 80 |
| 103 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 81 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
| 104 /// [ClassTypeAlias]. | 82 /// [ClassTypeAlias]. |
| 105 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); | 83 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); |
| 106 | 84 |
| 107 /// Memoized results of [_lazyClass]. | 85 /// Memoized results of [_lazyClass]. |
| 108 final _lazyClassMemo = new HashMap<Element, bool>(); | 86 final _lazyClassMemo = new HashMap<Element, bool>(); |
| 109 | 87 |
| 110 /// Memoized results of [_inLibraryCycle]. | 88 /// Memoized results of [_inLibraryCycle]. |
| 111 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 89 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
| 112 | 90 |
| 113 JSCodegenVisitor(this.libraryInfo, this.rules); | 91 JSCodegenVisitor(this.libraryInfo, this.rules, this._extensionMethods); |
| 114 | 92 |
| 115 LibraryElement get currentLibrary => libraryInfo.library; | 93 LibraryElement get currentLibrary => libraryInfo.library; |
| 116 | 94 TypeProvider get types => rules.provider; |
| 117 /// The name for the library's exports inside itself. | |
| 118 /// This much be a constant because we interpolate it into template strings, | |
| 119 /// and otherwise it would break caching for them. | |
| 120 /// `exports` was chosen as the most similar to ES module patterns. | |
| 121 final JSTemporary _exportsVar = new JSTemporary('exports'); | |
| 122 final JSTemporary _namedArgTemp = new JSTemporary('opts'); | |
| 123 | 95 |
| 124 JS.Program emitLibrary(LibraryUnit library) { | 96 JS.Program emitLibrary(LibraryUnit library) { |
| 125 var jsDefaultValue = '{}'; | 97 var jsDefaultValue = '{}'; |
| 126 var unit = library.library; | 98 var unit = library.library; |
| 127 if (unit.directives.isNotEmpty) { | 99 if (unit.directives.isNotEmpty) { |
| 128 var annotation = _getJsNameAnnotation(unit.directives.first); | 100 var annotation = _getJsNameAnnotation(unit.directives.first); |
| 129 if (annotation != null) { | 101 if (annotation != null) { |
| 130 var arguments = annotation.arguments.arguments; | 102 var arguments = annotation.arguments.arguments; |
| 131 if (!arguments.isEmpty) { | 103 if (!arguments.isEmpty) { |
| 132 var namedExpression = arguments.first as NamedExpression; | 104 var namedExpression = arguments.first as NamedExpression; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 187 String get _SYMBOL { | 159 String get _SYMBOL { |
| 188 var name = currentLibrary.name; | 160 var name = currentLibrary.name; |
| 189 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; | 161 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; |
| 190 return 'Symbol'; | 162 return 'Symbol'; |
| 191 } | 163 } |
| 192 | 164 |
| 193 @override | 165 @override |
| 194 JS.Statement visitCompilationUnit(CompilationUnit node) { | 166 JS.Statement visitCompilationUnit(CompilationUnit node) { |
| 195 var source = node.element.source; | 167 var source = node.element.source; |
| 196 | 168 |
| 197 _constEvaluator = new ConstantEvaluator(source, rules.provider); | 169 _constEvaluator = new ConstantEvaluator(source, types); |
| 198 | 170 |
| 199 // TODO(jmesserly): scriptTag, directives. | 171 // TODO(jmesserly): scriptTag, directives. |
| 200 var body = <JS.Statement>[]; | 172 var body = <JS.Statement>[]; |
| 201 for (var child in node.declarations) { | 173 for (var child in node.declarations) { |
| 202 // Attempt to group adjacent fields/properties. | 174 // Attempt to group adjacent fields/properties. |
| 203 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 175 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
| 204 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 176 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
| 205 | 177 |
| 206 var code = _visit(child); | 178 var code = _visit(child); |
| 207 | 179 |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 405 // The base class and all mixins must be declared before this class. | 377 // The base class and all mixins must be declared before this class. |
| 406 if (_lazyClass(type)) { | 378 if (_lazyClass(type)) { |
| 407 // TODO(jmesserly): the lazy class def is a simple solution for now. | 379 // TODO(jmesserly): the lazy class def is a simple solution for now. |
| 408 // We may want to consider other options in the future. | 380 // We may want to consider other options in the future. |
| 409 | 381 |
| 410 if (genericDef != null) { | 382 if (genericDef != null) { |
| 411 return js.statement( | 383 return js.statement( |
| 412 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ | 384 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ |
| 413 genericDef, | 385 genericDef, |
| 414 _exportsVar, | 386 _exportsVar, |
| 415 js.string(name, "'"), | 387 _propertyName(name), |
| 416 genericName | 388 genericName |
| 417 ]); | 389 ]); |
| 418 } | 390 } |
| 419 | 391 |
| 420 return js.statement( | 392 return js.statement( |
| 421 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ | 393 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ |
| 422 _exportsVar, | 394 _exportsVar, |
| 423 _propertyName(name), | 395 _propertyName(name), |
| 424 body, | 396 body, |
| 425 name | 397 name |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 436 | 408 |
| 437 // If we're not lazy, we still need to ensure our dependencies are | 409 // If we're not lazy, we still need to ensure our dependencies are |
| 438 // generated first. | 410 // generated first. |
| 439 var classDefs = <JS.Statement>[]; | 411 var classDefs = <JS.Statement>[]; |
| 440 if (type is InterfaceType) { | 412 if (type is InterfaceType) { |
| 441 _emitClassIfNeeded(classDefs, type.superclass); | 413 _emitClassIfNeeded(classDefs, type.superclass); |
| 442 for (var m in type.element.mixins) { | 414 for (var m in type.element.mixins) { |
| 443 _emitClassIfNeeded(classDefs, m); | 415 _emitClassIfNeeded(classDefs, m); |
| 444 } | 416 } |
| 445 } else if (type is FunctionType) { | 417 } else if (type is FunctionType) { |
| 446 _emitClassIfNeeded(classDefs, rules.provider.functionType); | 418 _emitClassIfNeeded(classDefs, types.functionType); |
| 447 } | 419 } |
| 448 classDefs.add(body); | 420 classDefs.add(body); |
| 449 return _statement(classDefs); | 421 return _statement(classDefs); |
| 450 } | 422 } |
| 451 | 423 |
| 452 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { | 424 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { |
| 453 // We can only emit classes from this library. | 425 // We can only emit classes from this library. |
| 454 if (base.element.library != currentLibrary) return; | 426 if (base.element.library != currentLibrary) return; |
| 455 | 427 |
| 456 var baseNode = _pendingClasses[base.element]; | 428 var baseNode = _pendingClasses[base.element]; |
| 457 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); | 429 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); |
| 458 } | 430 } |
| 459 | 431 |
| 460 /// Returns true if the supertype or mixins aren't loaded. | 432 /// Returns true if the supertype or mixins aren't loaded. |
| 461 /// If that is the case, we'll emit a lazy class definition. | 433 /// If that is the case, we'll emit a lazy class definition. |
| 462 bool _lazyClass(DartType type) { | 434 bool _lazyClass(DartType type) { |
| 463 if (type.isObject) return false; | 435 if (type.isObject) return false; |
| 464 | 436 |
| 465 // Use the element as the key, as those are unique whereas generic types | 437 // Use the element as the key, as those are unique whereas generic types |
| 466 // can have their arguments substituted. | 438 // can have their arguments substituted. |
| 467 assert(type.element.library == currentLibrary); | 439 assert(type.element.library == currentLibrary); |
| 468 var result = _lazyClassMemo[type.element]; | 440 var result = _lazyClassMemo[type.element]; |
| 469 if (result != null) return result; | 441 if (result != null) return result; |
| 470 | 442 |
| 471 if (type is InterfaceType) { | 443 if (type is InterfaceType) { |
| 472 result = _typeMightNotBeLoaded(type.superclass) || | 444 result = _typeMightNotBeLoaded(type.superclass) || |
| 473 type.mixins.any(_typeMightNotBeLoaded); | 445 type.mixins.any(_typeMightNotBeLoaded); |
| 474 } else if (type is FunctionType) { | 446 } else if (type is FunctionType) { |
| 475 result = _typeMightNotBeLoaded(rules.provider.functionType); | 447 result = _typeMightNotBeLoaded(types.functionType); |
| 476 } | 448 } |
| 477 return _lazyClassMemo[type.element] = result; | 449 return _lazyClassMemo[type.element] = result; |
| 478 } | 450 } |
| 479 | 451 |
| 480 /// Curated order to minimize lazy classes needed by dart:core and its | 452 /// Curated order to minimize lazy classes needed by dart:core and its |
| 481 /// transitive SDK imports. | 453 /// transitive SDK imports. |
| 482 static const CORELIB_ORDER = const [ | 454 static const CORELIB_ORDER = const [ |
| 483 'dart.core', | 455 'dart.core', |
| 484 'dart.collection', | 456 'dart.collection', |
| 485 'dart._internal' | 457 'dart._internal' |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 561 ]); | 533 ]); |
| 562 } | 534 } |
| 563 | 535 |
| 564 JS.Expression _classHeritage(ClassDeclaration node) { | 536 JS.Expression _classHeritage(ClassDeclaration node) { |
| 565 if (node.element.type.isObject) return null; | 537 if (node.element.type.isObject) return null; |
| 566 | 538 |
| 567 JS.Expression heritage = null; | 539 JS.Expression heritage = null; |
| 568 if (node.extendsClause != null) { | 540 if (node.extendsClause != null) { |
| 569 heritage = _visit(node.extendsClause.superclass); | 541 heritage = _visit(node.extendsClause.superclass); |
| 570 } else { | 542 } else { |
| 571 heritage = _emitTypeName(rules.provider.objectType); | 543 heritage = _emitTypeName(types.objectType); |
| 572 } | 544 } |
| 573 if (node.withClause != null) { | 545 if (node.withClause != null) { |
| 574 var mixins = _visitList(node.withClause.mixinTypes); | 546 var mixins = _visitList(node.withClause.mixinTypes); |
| 575 mixins.insert(0, heritage); | 547 mixins.insert(0, heritage); |
| 576 heritage = js.call('dart.mixin(#)', [mixins]); | 548 heritage = js.call('dart.mixin(#)', [mixins]); |
| 577 } | 549 } |
| 578 return heritage; | 550 return heritage; |
| 579 } | 551 } |
| 580 | 552 |
| 581 /// Emit class members that can be generated as methods. | |
| 582 /// Anything not handled here will be addressed in [_finishClassMembers]. | |
| 583 Iterable<InterfaceType> getMatchingExtensionMethodTypes(InterfaceType type) => | |
| 584 jsExtensionMethodTypes.where((t) => rules.isSubTypeOf(type, t)); | |
| 585 | |
| 586 LibraryElement getExtensionLibrary( | |
| 587 Iterable<InterfaceType> extensionTypes, String name) { | |
| 588 var library = null; | |
| 589 for (var type in extensionTypes) { | |
| 590 var element = type.element; | |
| 591 if (extensionMethods[element].contains(name)) { | |
| 592 assert(library == null || library == element.library); | |
| 593 library = element.library; | |
| 594 } | |
| 595 } | |
| 596 return library; | |
| 597 } | |
| 598 | |
| 599 JS.Expression nameIfExtension(Expression target, String name) { | |
| 600 var targetType = rules.getStaticType(target); | |
| 601 if (targetType is! InterfaceType) return null; | |
| 602 var extensionLibrary = | |
| 603 getExtensionLibrary(getMatchingExtensionMethodTypes(targetType), name); | |
| 604 if (extensionLibrary == null) return null; | |
| 605 return js.call('#.#', [ | |
| 606 _libraryName(extensionLibrary), | |
| 607 _emitExtensionMethodName(name) | |
| 608 ]); | |
| 609 } | |
| 610 | |
| 611 List<JS.Method> _emitClassMethods(ClassDeclaration node, | 553 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
| 612 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | 554 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
| 613 var element = node.element; | 555 var element = node.element; |
| 614 var isObject = element.type.isObject; | 556 var type = element.type; |
| 557 var isObject = type.isObject; | |
| 615 var name = node.name.name; | 558 var name = node.name.name; |
| 616 | 559 |
| 617 var jsMethods = <JS.Method>[]; | 560 var jsMethods = <JS.Method>[]; |
| 618 // Iff no constructor is specified for a class C, it implicitly has a | 561 // Iff no constructor is specified for a class C, it implicitly has a |
| 619 // default constructor `C() : super() {}`, unless C is class Object. | 562 // default constructor `C() : super() {}`, unless C is class Object. |
| 620 if (ctors.isEmpty && !isObject) { | 563 if (ctors.isEmpty && !isObject) { |
| 621 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 564 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
| 622 } | 565 } |
| 623 var extensionTypes = getMatchingExtensionMethodTypes(element.type); | |
| 624 for (var member in node.members) { | 566 for (var member in node.members) { |
| 625 if (member is ConstructorDeclaration) { | 567 if (member is ConstructorDeclaration) { |
| 626 jsMethods.add(_emitConstructor(member, name, fields, isObject)); | 568 jsMethods.add(_emitConstructor(member, name, fields, isObject)); |
| 627 } else if (member is MethodDeclaration) { | 569 } else if (member is MethodDeclaration) { |
| 628 jsMethods.add(_emitMethodDeclaration(member, extensionTypes)); | 570 jsMethods.add(_emitMethodDeclaration(type, member)); |
| 629 } | 571 } |
| 630 } | 572 } |
| 631 | 573 |
| 632 // Support for adapting dart:core Iterator/Iterable to ES6 versions. | 574 // Support for adapting dart:core Iterator/Iterable to ES6 versions. |
| 633 // This lets them use for-of loops transparently. | 575 // This lets them use for-of loops transparently. |
| 634 // https://github.com/lukehoban/es6features#iterators--forof | 576 // https://github.com/lukehoban/es6features#iterators--forof |
| 635 if (element.library.isDartCore && element.name == 'Iterable') { | 577 if (element.library.isDartCore && element.name == 'Iterable') { |
| 636 JS.Fun body = js.call('''function() { | 578 JS.Fun body = js.call('''function() { |
| 637 var iterator = this.iterator; | 579 var iterator = this.iterator; |
| 638 return { | 580 return { |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 725 node.name == null) { | 667 node.name == null) { |
| 726 // Implements Dart constructor behavior. Because of V8 `super` | 668 // Implements Dart constructor behavior. Because of V8 `super` |
| 727 // [constructor restrictions] | 669 // [constructor restrictions] |
| 728 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) | 670 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) |
| 729 // we cannot currently emit actual ES6 constructors with super calls. | 671 // we cannot currently emit actual ES6 constructors with super calls. |
| 730 // Instead we use the same trick as named constructors, and do them as | 672 // Instead we use the same trick as named constructors, and do them as |
| 731 // instance methods that perform initialization. | 673 // instance methods that perform initialization. |
| 732 // TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8 | 674 // TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8 |
| 733 // settles. See <https://github.com/dart-lang/dev_compiler/issues/51>. | 675 // settles. See <https://github.com/dart-lang/dev_compiler/issues/51>. |
| 734 // Performance of this pattern is likely to be bad. | 676 // Performance of this pattern is likely to be bad. |
| 735 name = js.string('constructor', "'"); | 677 name = _propertyName('constructor'); |
| 736 // Mark the parameter as no-rename. | 678 // Mark the parameter as no-rename. |
| 737 var args = new JS.Identifier('arguments', allowRename: false); | 679 var args = new JS.Identifier('arguments', allowRename: false); |
| 738 body = js.statement('''{ | 680 body = js.statement('''{ |
| 739 // Get the class name for this instance. | 681 // Get the class name for this instance. |
| 740 let name = this.constructor.name; | 682 let name = this.constructor.name; |
| 741 // Call the default constructor. | 683 // Call the default constructor. |
| 742 let init = this[name]; | 684 let init = this[name]; |
| 743 let result = void 0; | 685 let result = void 0; |
| 744 if (init) result = init.apply(this, #); | 686 if (init) result = init.apply(this, #); |
| 745 return result === void 0 ? this : result; | 687 return result === void 0 ? this : result; |
| 746 }''', args); | 688 }''', args); |
| 747 } else { | 689 } else { |
| 748 body = _emitConstructorBody(node, fields); | 690 body = _emitConstructorBody(node, fields); |
| 749 } | 691 } |
| 750 | 692 |
| 751 // We generate constructors as initializer methods in the class; | 693 // We generate constructors as initializer methods in the class; |
| 752 // this allows use of `super` for instance methods/properties. | 694 // this allows use of `super` for instance methods/properties. |
| 753 // It also avoids V8 restrictions on `super` in default constructors. | 695 // It also avoids V8 restrictions on `super` in default constructors. |
| 754 return new JS.Method(name, new JS.Fun(_visit(node.parameters), body)) | 696 return new JS.Method(name, new JS.Fun(_visit(node.parameters), body)) |
| 755 ..sourceInformation = node; | 697 ..sourceInformation = node; |
| 756 } | 698 } |
| 757 | 699 |
| 758 JS.Expression _constructorName(String className, SimpleIdentifier name) { | 700 JS.Expression _constructorName(String className, SimpleIdentifier name) { |
| 759 if (name == null) return js.string(className, "'"); | 701 if (name == null) return _propertyName(className); |
| 760 return _emitMemberName(name.name, isStatic: true); | 702 return _emitMemberName(name.name, isStatic: true); |
| 761 } | 703 } |
| 762 | 704 |
| 763 JS.Block _emitConstructorBody( | 705 JS.Block _emitConstructorBody( |
| 764 ConstructorDeclaration node, List<FieldDeclaration> fields) { | 706 ConstructorDeclaration node, List<FieldDeclaration> fields) { |
| 765 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 707 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
| 766 if (node.redirectedConstructor != null) { | 708 if (node.redirectedConstructor != null) { |
| 767 return js.statement('{ return new #(#); }', [ | 709 return js.statement('{ return new #(#); }', [ |
| 768 _visit(node.redirectedConstructor), | 710 _visit(node.redirectedConstructor), |
| 769 _visit(node.parameters) | 711 _visit(node.parameters) |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 842 /// 1. field declaration initializer if non-const, | 784 /// 1. field declaration initializer if non-const, |
| 843 /// 2. field initializing parameters, | 785 /// 2. field initializing parameters, |
| 844 /// 3. constructor field initializers, | 786 /// 3. constructor field initializers, |
| 845 /// 4. initialize fields not covered in 1-3 | 787 /// 4. initialize fields not covered in 1-3 |
| 846 JS.Statement _initializeFields(List<FieldDeclaration> fields, | 788 JS.Statement _initializeFields(List<FieldDeclaration> fields, |
| 847 [FormalParameterList parameters, | 789 [FormalParameterList parameters, |
| 848 NodeList<ConstructorInitializer> initializers]) { | 790 NodeList<ConstructorInitializer> initializers]) { |
| 849 var body = <JS.Statement>[]; | 791 var body = <JS.Statement>[]; |
| 850 | 792 |
| 851 // Run field initializers if they can have side-effects. | 793 // Run field initializers if they can have side-effects. |
| 852 var unsetFields = new Map<String, VariableDeclaration>(); | 794 var unsetFields = new Map<FieldElement, VariableDeclaration>(); |
| 853 for (var declaration in fields) { | 795 for (var declaration in fields) { |
| 854 for (var field in declaration.fields.variables) { | 796 for (var field in declaration.fields.variables) { |
| 855 if (_isFieldInitConstant(field)) { | 797 if (_isFieldInitConstant(field)) { |
| 856 unsetFields[field.name.name] = field; | 798 unsetFields[field.element] = field; |
| 857 } else { | 799 } else { |
| 858 body.add(js.statement( | 800 body.add(js.statement( |
| 859 '# = #;', [_visit(field.name), _visitInitializer(field)])); | 801 '# = #;', [_visit(field.name), _visitInitializer(field)])); |
| 860 } | 802 } |
| 861 } | 803 } |
| 862 } | 804 } |
| 863 | 805 |
| 864 // Initialize fields from `this.fieldName` parameters. | 806 // Initialize fields from `this.fieldName` parameters. |
| 865 if (parameters != null) { | 807 if (parameters != null) { |
| 866 for (var p in parameters.parameters) { | 808 for (var p in parameters.parameters) { |
| 867 if (p is DefaultFormalParameter) p = p.parameter; | 809 if (p is DefaultFormalParameter) p = p.parameter; |
| 868 if (p is FieldFormalParameter) { | 810 if (p is FieldFormalParameter) { |
| 869 var name = p.identifier.name; | 811 var field = (p.element as FieldFormalParameterElement).field; |
| 870 body.add( | 812 // Use the getter to initialize the field. This is a bit strange, but |
| 871 js.statement('this.# = #;', [_emitMemberName(name), _visit(p)])); | 813 // final fields don't have a setter element that we could use instead. |
| 872 unsetFields.remove(name); | 814 |
| 815 var memberName = | |
| 816 _emitMemberName(field.name, type: field.enclosingElement.type); | |
| 817 body.add(js.statement('this.# = #;', [memberName, _visit(p)])); | |
| 818 unsetFields.remove(field); | |
| 873 } | 819 } |
| 874 } | 820 } |
| 875 } | 821 } |
| 876 | 822 |
| 877 // Run constructor field initializers such as `: foo = bar.baz` | 823 // Run constructor field initializers such as `: foo = bar.baz` |
| 878 if (initializers != null) { | 824 if (initializers != null) { |
| 879 for (var init in initializers) { | 825 for (var init in initializers) { |
| 880 if (init is ConstructorFieldInitializer) { | 826 if (init is ConstructorFieldInitializer) { |
| 881 body.add(js.statement( | 827 body.add(js.statement( |
| 882 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); | 828 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); |
| 883 unsetFields.remove(init.fieldName.name); | 829 unsetFields.remove(init.fieldName.staticElement); |
| 884 } | 830 } |
| 885 } | 831 } |
| 886 } | 832 } |
| 887 | 833 |
| 888 // Initialize all remaining fields | 834 // Initialize all remaining fields |
| 889 unsetFields.forEach((name, field) { | 835 unsetFields.forEach((field, fieldNode) { |
| 890 JS.Expression value; | 836 JS.Expression value; |
| 891 if (field.initializer != null) { | 837 if (fieldNode.initializer != null) { |
| 892 value = _visit(field.initializer); | 838 value = _visit(fieldNode.initializer); |
| 893 } else { | 839 } else { |
| 894 var type = rules.elementType(field.element); | 840 var type = rules.elementType(field); |
| 841 value = new JS.LiteralNull(); | |
| 895 if (rules.maybeNonNullableType(type)) { | 842 if (rules.maybeNonNullableType(type)) { |
| 896 value = js.call('dart.as(null, #)', _emitTypeName(type)); | 843 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]); |
| 897 } else { | |
| 898 value = new JS.LiteralNull(); | |
| 899 } | 844 } |
| 900 } | 845 } |
| 901 body.add(js.statement('this.# = #;', [_emitMemberName(name), value])); | 846 var memberName = |
| 847 _emitMemberName(field.name, type: field.enclosingElement.type); | |
| 848 body.add(js.statement('this.# = #;', [memberName, value])); | |
| 902 }); | 849 }); |
| 903 | 850 |
| 904 return _statement(body); | 851 return _statement(body); |
| 905 } | 852 } |
| 906 | 853 |
| 907 FormalParameterList _parametersOf(node) { | 854 FormalParameterList _parametersOf(node) { |
| 908 // Note: ConstructorDeclaration is intentionally skipped here so we can | 855 // Note: ConstructorDeclaration is intentionally skipped here so we can |
| 909 // emit the argument initializers in a different place. | 856 // emit the argument initializers in a different place. |
| 910 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we | 857 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we |
| 911 // could handle argument initializers more consistently in a separate | 858 // could handle argument initializers more consistently in a separate |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 953 } | 900 } |
| 954 | 901 |
| 955 JS.Expression _defaultParamValue(FormalParameter param) { | 902 JS.Expression _defaultParamValue(FormalParameter param) { |
| 956 if (param is DefaultFormalParameter && param.defaultValue != null) { | 903 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 957 return _visit(param.defaultValue); | 904 return _visit(param.defaultValue); |
| 958 } else { | 905 } else { |
| 959 return new JS.LiteralNull(); | 906 return new JS.LiteralNull(); |
| 960 } | 907 } |
| 961 } | 908 } |
| 962 | 909 |
| 963 JS.Method _emitMethodDeclaration( | 910 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
| 964 MethodDeclaration node, Iterable<InterfaceType> extensionTypes) { | |
| 965 if (node.isAbstract || _externalOrNative(node)) { | 911 if (node.isAbstract || _externalOrNative(node)) { |
| 966 return null; | 912 return null; |
| 967 } | 913 } |
| 968 | 914 |
| 969 var params = _visit(node.parameters); | 915 var params = _visit(node.parameters); |
| 970 if (params == null) params = []; | 916 if (params == null) params = []; |
| 971 | 917 |
| 972 var memberName; | 918 var memberName = _emitMemberName(node.name.name, |
| 973 var extensionLibrary; | 919 type: type, unary: params.isEmpty, isStatic: node.isStatic); |
| 974 | |
| 975 if (!node.isStatic) { | |
| 976 extensionLibrary = getExtensionLibrary(extensionTypes, node.name.name); | |
| 977 } | |
| 978 | |
| 979 if (extensionLibrary != null) { | |
| 980 var extensionMethodName = _extensionMethodName(node.name.name); | |
| 981 if (extensionLibrary == libraryInfo.library.library) { | |
| 982 // TODO(jacobr): need to do a better job ensuring that extension method | |
| 983 // name symbols do not conflict with other symbols before we can let | |
| 984 // user defined libraries define extension methods. | |
| 985 if (_extensionMethodNames.add(extensionMethodName)) { | |
| 986 _pendingExtensionMethodNames.add(extensionMethodName); | |
| 987 _addExport(extensionMethodName); | |
| 988 } | |
| 989 } | |
| 990 memberName = js.call('#.#', [ | |
| 991 _libraryName(extensionLibrary), | |
| 992 js.string(extensionMethodName, "'") | |
| 993 ]); | |
| 994 } else { | |
| 995 memberName = _emitMemberName(node.name.name, isStatic: node.isStatic); | |
| 996 } | |
| 997 return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)), | 920 return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)), |
| 998 isGetter: node.isGetter, | 921 isGetter: node.isGetter, |
| 999 isSetter: node.isSetter, | 922 isSetter: node.isSetter, |
| 1000 isStatic: node.isStatic); | 923 isStatic: node.isStatic); |
| 1001 } | 924 } |
| 1002 | 925 |
| 1003 @override | 926 @override |
| 1004 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 927 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 1005 assert(node.parent is CompilationUnit); | 928 assert(node.parent is CompilationUnit); |
| 1006 | 929 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1071 /// Writes a simple identifier. This can handle implicit `this` as well as | 994 /// Writes a simple identifier. This can handle implicit `this` as well as |
| 1072 /// going through the qualified library name if necessary. | 995 /// going through the qualified library name if necessary. |
| 1073 @override | 996 @override |
| 1074 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { | 997 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { |
| 1075 var e = node.staticElement; | 998 var e = node.staticElement; |
| 1076 if (e == null) { | 999 if (e == null) { |
| 1077 return js.commentExpression( | 1000 return js.commentExpression( |
| 1078 'Unimplemented unknown name', new JS.Identifier(node.name)); | 1001 'Unimplemented unknown name', new JS.Identifier(node.name)); |
| 1079 } | 1002 } |
| 1080 | 1003 |
| 1081 var name = node.name; | |
| 1082 var variable = e is PropertyAccessorElement ? e.variable : e; | 1004 var variable = e is PropertyAccessorElement ? e.variable : e; |
| 1005 var name = variable.name; | |
| 1083 | 1006 |
| 1084 // library member | 1007 // library member |
| 1085 if (e.enclosingElement is CompilationUnitElement && | 1008 if (e.enclosingElement is CompilationUnitElement && |
| 1086 (e.library != libraryInfo.library || | 1009 (e.library != libraryInfo.library || |
| 1087 variable is TopLevelVariableElement && !variable.isConst)) { | 1010 variable is TopLevelVariableElement && !variable.isConst)) { |
| 1088 return js.call('#.#', [_libraryName(e.library), name]); | 1011 return js.call('#.#', [_libraryName(e.library), name]); |
| 1089 } | 1012 } |
| 1090 | 1013 |
| 1091 // instance member | 1014 // instance member |
| 1092 if (currentClass != null && _needsImplicitThis(e)) { | 1015 if (currentClass != null && _needsImplicitThis(e)) { |
| 1093 return js.call('this.#', _emitMemberName(name)); | 1016 return js.call( |
| 1017 'this.#', _emitMemberName(name, type: currentClass.element.type)); | |
| 1094 } | 1018 } |
| 1095 | 1019 |
| 1096 // static member | 1020 // static member |
| 1097 if (e is ExecutableElement && | 1021 if (e is ExecutableElement && |
| 1098 e.isStatic && | 1022 e.isStatic && |
| 1099 variable.enclosingElement is ClassElement) { | 1023 variable.enclosingElement is ClassElement) { |
| 1100 var className = (variable.enclosingElement as ClassElement).name; | 1024 var className = (variable.enclosingElement as ClassElement).name; |
| 1101 return js.call('#.#', [className, _emitMemberName(name, isStatic: true)]); | 1025 return js.call('#.#', [className, _emitMemberName(name, isStatic: true)]); |
| 1102 } | 1026 } |
| 1103 | 1027 |
| 1104 // initializing formal parameter, e.g. `Point(this.x)` | 1028 // initializing formal parameter, e.g. `Point(this.x)` |
| 1105 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { | 1029 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { |
| 1106 /// Rename private names so they don't shadow the private field symbol. | 1030 /// Rename private names so they don't shadow the private field symbol. |
| 1107 /// The renamer would handle this, but it would prefer to rename the | 1031 /// The renamer would handle this, but it would prefer to rename the |
| 1108 /// temporary used for the private symbol. Instead rename the parameter. | 1032 /// temporary used for the private symbol. Instead rename the parameter. |
| 1109 return new JSTemporary('${name.substring(1)}'); | 1033 return new JSTemporary('${name.substring(1)}'); |
| 1110 } | 1034 } |
| 1111 | 1035 |
| 1112 if (_isTemporary(e)) { | 1036 if (_isTemporary(e)) { |
| 1113 if (name[0] == '#') { | 1037 if (name[0] == '#') { |
| 1114 return new JS.InterpolatedExpression(name.substring(1)); | 1038 return new JS.InterpolatedExpression(name.substring(1)); |
| 1115 } else { | 1039 } else { |
| 1116 return new JSTemporary(e.name); | 1040 return new JSTemporary(name); |
| 1117 } | 1041 } |
| 1118 } | 1042 } |
| 1119 | 1043 |
| 1120 return new JS.Identifier(name); | 1044 return new JS.Identifier(name); |
| 1121 } | 1045 } |
| 1122 | 1046 |
| 1123 JS.ArrayInitializer _emitTypeNames(List<DartType> types) { | 1047 JS.ArrayInitializer _emitTypeNames(List<DartType> types) { |
| 1124 return new JS.ArrayInitializer(types.map(_emitTypeName).toList()); | 1048 return new JS.ArrayInitializer(types.map(_emitTypeName).toList()); |
| 1125 } | 1049 } |
| 1126 | 1050 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1176 // TODO(jmesserly): remove when we're using coercion reifier. | 1100 // TODO(jmesserly): remove when we're using coercion reifier. |
| 1177 return _unimplementedCall('Unimplemented type $type'); | 1101 return _unimplementedCall('Unimplemented type $type'); |
| 1178 } | 1102 } |
| 1179 | 1103 |
| 1180 var typeArgs = null; | 1104 var typeArgs = null; |
| 1181 if (type is ParameterizedType) { | 1105 if (type is ParameterizedType) { |
| 1182 // TODO(jmesserly): this is a workaround for an analyzer bug, see: | 1106 // TODO(jmesserly): this is a workaround for an analyzer bug, see: |
| 1183 // https://github.com/dart-lang/dev_compiler/commit/a212d59ad046085a626dd8 d16881cdb8e8b9c3fa | 1107 // https://github.com/dart-lang/dev_compiler/commit/a212d59ad046085a626dd8 d16881cdb8e8b9c3fa |
| 1184 if (type is! FunctionType || element is FunctionTypeAlias) { | 1108 if (type is! FunctionType || element is FunctionTypeAlias) { |
| 1185 var args = type.typeArguments; | 1109 var args = type.typeArguments; |
| 1186 if (args.any((a) => a != rules.provider.dynamicType)) { | 1110 if (args.any((a) => a != types.dynamicType)) { |
| 1187 name = '$name\$'; | 1111 name = '$name\$'; |
| 1188 typeArgs = args.map(_emitTypeName); | 1112 typeArgs = args.map(_emitTypeName); |
| 1189 } | 1113 } |
| 1190 } | 1114 } |
| 1191 } | 1115 } |
| 1192 | 1116 |
| 1193 JS.Expression result; | 1117 JS.Expression result; |
| 1194 if (_needQualifiedName(element)) { | 1118 if (_needQualifiedName(element)) { |
| 1195 result = js.call('#.#', [_libraryName(element.library), name]); | 1119 result = js.call('#.#', [_libraryName(element.library), name]); |
| 1196 } else { | 1120 } else { |
| 1197 result = new JS.Identifier(name); | 1121 result = new JS.Identifier(name); |
| 1198 } | 1122 } |
| 1199 | 1123 |
| 1200 if (typeArgs != null) { | 1124 if (typeArgs != null) { |
| 1201 result = js.call('#(#)', [result, typeArgs]); | 1125 result = js.call('#(#)', [result, typeArgs]); |
| 1202 } | 1126 } |
| 1203 return result; | 1127 return result; |
| 1204 } | 1128 } |
| 1205 | 1129 |
| 1206 bool _needQualifiedName(Element element) { | 1130 bool _needQualifiedName(Element element) { |
| 1207 var lib = element.library; | 1131 var lib = element.library; |
| 1208 if (lib == null) return false; | 1132 if (lib == null) return false; |
| 1209 if (lib != currentLibrary) return true; | 1133 if (lib != currentLibrary) return true; |
| 1210 if (element is ClassElement) return _lazyClass(element.type); | 1134 if (element is ClassElement) return _lazyClass(element.type); |
| 1211 if (element is FunctionTypeAliasElement) return _lazyClass(element.type); | 1135 if (element is FunctionTypeAliasElement) return _lazyClass(element.type); |
| 1212 return false; | 1136 return false; |
| 1213 } | 1137 } |
| 1214 | 1138 |
| 1215 JS.Node _emitDSetIfDynamic( | |
| 1216 Expression target, SimpleIdentifier id, Expression rhs) { | |
| 1217 if (rules.isDynamicTarget(target)) { | |
| 1218 return js.call('dart.dput(#, #, #)', [ | |
| 1219 _visit(target), | |
| 1220 js.string(id.name, "'"), | |
| 1221 _visit(rhs) | |
| 1222 ]); | |
| 1223 } else { | |
| 1224 return null; | |
| 1225 } | |
| 1226 } | |
| 1227 | |
| 1228 @override | 1139 @override |
| 1229 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 1140 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
| 1230 var left = node.leftHandSide; | 1141 var left = node.leftHandSide; |
| 1231 var right = node.rightHandSide; | 1142 var right = node.rightHandSide; |
| 1232 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 1143 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
| 1233 return _emitOpAssign(left, right, node.operator.lexeme[0], context: node); | 1144 return _emitOpAssign( |
| 1145 left, right, node.operator.lexeme[0], node.staticElement, | |
| 1146 context: node); | |
| 1234 } | 1147 } |
| 1235 | 1148 |
| 1236 JSMetaLet _emitOpAssign(Expression left, Expression right, String op, | 1149 JSMetaLet _emitOpAssign( |
| 1150 Expression left, Expression right, String op, ExecutableElement element, | |
| 1237 {Expression context}) { | 1151 {Expression context}) { |
| 1238 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions | 1152 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions |
| 1239 // (for example, x is IndexExpression) we evaluate those once. | 1153 // (for example, x is IndexExpression) we evaluate those once. |
| 1240 var vars = {}; | 1154 var vars = {}; |
| 1241 var lhs = _bindLeftHandSide(vars, left, context: context); | 1155 var lhs = _bindLeftHandSide(vars, left, context: context); |
| 1242 var inc = AstBuilder.binaryExpression(lhs, op, right); | 1156 var inc = AstBuilder.binaryExpression(lhs, op, right); |
| 1243 inc.staticType = rules.getStaticType(left); | 1157 inc.staticElement = element; |
| 1158 inc.staticType = getStaticType(left); | |
| 1244 return new JSMetaLet(vars, [_emitSet(lhs, inc)]); | 1159 return new JSMetaLet(vars, [_emitSet(lhs, inc)]); |
| 1245 } | 1160 } |
| 1246 | 1161 |
| 1247 JS.Expression _emitSet(Expression lhs, Expression rhs) { | 1162 JS.Expression _emitSet(Expression lhs, Expression rhs) { |
| 1248 if (lhs is IndexExpression) { | 1163 if (lhs is IndexExpression) { |
| 1249 String code; | 1164 return _emitSend(_getTarget(lhs), '[]=', [lhs.index, rhs]); |
| 1250 var target = _getTarget(lhs); | 1165 } |
| 1251 if (rules.isDynamicTarget(target)) { | 1166 |
| 1252 code = 'dart.dsetindex(#, #, #)'; | 1167 Expression target = null; |
| 1253 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); | 1168 SimpleIdentifier id; |
| 1254 } | 1169 if (lhs is PropertyAccess) { |
| 1255 return js.call('#.#(#, #)', [ | 1170 target = _getTarget(lhs); |
| 1171 id = lhs.propertyName; | |
| 1172 } else if (lhs is PrefixedIdentifier) { | |
| 1173 target = lhs.prefix; | |
| 1174 id = lhs.identifier; | |
| 1175 } | |
| 1176 | |
| 1177 if (target != null && rules.isDynamicTarget(target)) { | |
| 1178 return js.call('dart.$DPUT(#, #, #)', [ | |
| 1256 _visit(target), | 1179 _visit(target), |
| 1257 _emitMemberName('[]=', target: target), | 1180 _emitMemberName(id.name, type: getStaticType(target)), |
| 1258 _visit(lhs.index), | |
| 1259 _visit(rhs) | 1181 _visit(rhs) |
| 1260 ]); | 1182 ]); |
| 1261 } | 1183 } |
| 1262 | 1184 |
| 1263 if (lhs is PropertyAccess) { | |
| 1264 var result = _emitDSetIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); | |
| 1265 if (result != null) return result; | |
| 1266 } else if (lhs is PrefixedIdentifier) { | |
| 1267 // TODO(vsm): Is this the right code if the prefix is a library? | |
| 1268 var result = _emitDSetIfDynamic(lhs.prefix, lhs.identifier, rhs); | |
| 1269 if (result != null) return result; | |
| 1270 } | |
| 1271 return _visit(rhs).toAssignExpression(_visit(lhs)); | 1185 return _visit(rhs).toAssignExpression(_visit(lhs)); |
| 1272 } | 1186 } |
| 1273 | 1187 |
| 1274 @override | 1188 @override |
| 1275 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { | 1189 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 1276 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); | 1190 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); |
| 1277 var ret = new JS.Return(_visit(node.expression)); | 1191 var ret = new JS.Return(_visit(node.expression)); |
| 1278 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); | 1192 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); |
| 1279 } | 1193 } |
| 1280 | 1194 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1292 @override | 1206 @override |
| 1293 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); | 1207 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); |
| 1294 | 1208 |
| 1295 @override | 1209 @override |
| 1296 visitMethodInvocation(MethodInvocation node) { | 1210 visitMethodInvocation(MethodInvocation node) { |
| 1297 var target = node.isCascaded ? _cascadeTarget : node.target; | 1211 var target = node.isCascaded ? _cascadeTarget : node.target; |
| 1298 | 1212 |
| 1299 var result = _emitForeignJS(node); | 1213 var result = _emitForeignJS(node); |
| 1300 if (result != null) return result; | 1214 if (result != null) return result; |
| 1301 | 1215 |
| 1302 if (rules.isDynamicCall(node.methodName)) { | 1216 // TODO(jmesserly): if we try to call a getter returning a function with |
| 1303 var args = _visit(node.argumentList); | 1217 // a call method, we don't generate the `.call` correctly. |
| 1304 if (target != null) { | 1218 String code; |
| 1305 return js.call('dart.dsend(#, #, #)', [ | 1219 if (target == null) { |
| 1306 _visit(target), | 1220 if (rules.isDynamicCall(node.methodName)) { |
| 1307 js.string(node.methodName.name, "'"), | 1221 code = 'dart.$DCALL(#, #)'; |
| 1308 args | |
| 1309 ]); | |
| 1310 } else { | 1222 } else { |
| 1311 return js.call('dart.dcall(#, #)', [_visit(node.methodName), args]); | 1223 code = '#(#)'; |
| 1312 } | 1224 } |
| 1225 return js.call( | |
| 1226 code, [_visit(node.methodName), _visit(node.argumentList)]); | |
| 1313 } | 1227 } |
| 1314 | 1228 |
| 1315 // TODO(jmesserly): if this resolves to a getter returning a function with | 1229 // TODO(jmesserly): if the methodName resolves statically but the call is |
| 1316 // a call method, we don't generate the `.call` correctly. | 1230 // dynamic (e.g. `obj.method` is resolved to a field of type `Function`), we |
| 1317 | 1231 // could generate call(#.#, #). Not sure if that's worth it. |
| 1318 var targetJs; | 1232 if (rules.isDynamicCall(node.methodName)) { |
| 1319 if (target != null) { | 1233 code = 'dart.$DSEND(#, #, #)'; |
| 1320 targetJs = js.call('#.#', [ | |
| 1321 _visit(target), | |
| 1322 _emitMemberName(node.methodName.name, target: target) | |
| 1323 ]); | |
| 1324 } else { | 1234 } else { |
| 1325 targetJs = _visit(node.methodName); | 1235 code = '#.#(#)'; |
| 1326 } | 1236 } |
| 1327 | 1237 return js.call(code, [ |
| 1328 return js.call('#(#)', [targetJs, _visit(node.argumentList)]); | 1238 _visit(target), |
| 1239 _emitMemberName(node.methodName.name, type: getStaticType(target)), | |
| 1240 _visit(node.argumentList) | |
| 1241 ]); | |
| 1329 } | 1242 } |
| 1330 | 1243 |
| 1331 /// Emits code for the `JS(...)` builtin. | 1244 /// Emits code for the `JS(...)` builtin. |
| 1332 _emitForeignJS(MethodInvocation node) { | 1245 _emitForeignJS(MethodInvocation node) { |
| 1333 var e = node.methodName.staticElement; | 1246 var e = node.methodName.staticElement; |
| 1334 if (e is FunctionElement && | 1247 if (e is FunctionElement && |
| 1335 e.library.name == '_foreign_helper' && | 1248 e.library.name == '_foreign_helper' && |
| 1336 e.name == 'JS') { | 1249 e.name == 'JS') { |
| 1337 var args = node.argumentList.arguments; | 1250 var args = node.argumentList.arguments; |
| 1338 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 1251 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
| 1339 var code = args[1] as StringLiteral; | 1252 var code = args[1] as StringLiteral; |
| 1340 | 1253 |
| 1341 var template = js.parseForeignJS(code.stringValue); | 1254 var template = js.parseForeignJS(code.stringValue); |
| 1342 var result = template.instantiate(_visitList(args.skip(2))); | 1255 var result = template.instantiate(_visitList(args.skip(2))); |
| 1343 // `throw` is emitted as a statement by `parseForeignJS`. | 1256 // `throw` is emitted as a statement by `parseForeignJS`. |
| 1344 assert(result is JS.Expression || node.parent is ExpressionStatement); | 1257 assert(result is JS.Expression || node.parent is ExpressionStatement); |
| 1345 return result; | 1258 return result; |
| 1346 } | 1259 } |
| 1347 return null; | 1260 return null; |
| 1348 } | 1261 } |
| 1349 | 1262 |
| 1350 @override | 1263 @override |
| 1351 JS.Expression visitFunctionExpressionInvocation( | 1264 JS.Expression visitFunctionExpressionInvocation( |
| 1352 FunctionExpressionInvocation node) { | 1265 FunctionExpressionInvocation node) { |
| 1353 var code; | 1266 var code; |
| 1354 if (rules.isDynamicCall(node.function)) { | 1267 if (rules.isDynamicCall(node.function)) { |
| 1355 code = 'dart.dcall(#, #)'; | 1268 code = 'dart.$DCALL(#, #)'; |
| 1356 } else { | 1269 } else { |
| 1357 code = '#(#)'; | 1270 code = '#(#)'; |
| 1358 } | 1271 } |
| 1359 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); | 1272 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
| 1360 } | 1273 } |
| 1361 | 1274 |
| 1362 @override | 1275 @override |
| 1363 List<JS.Expression> visitArgumentList(ArgumentList node) { | 1276 List<JS.Expression> visitArgumentList(ArgumentList node) { |
| 1364 var args = <JS.Expression>[]; | 1277 var args = <JS.Expression>[]; |
| 1365 var named = <JS.Property>[]; | 1278 var named = <JS.Property>[]; |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1511 _exportsVar, | 1424 _exportsVar, |
| 1512 _properties.map(_emitTopLevelProperty) | 1425 _properties.map(_emitTopLevelProperty) |
| 1513 ])); | 1426 ])); |
| 1514 _properties.clear(); | 1427 _properties.clear(); |
| 1515 } | 1428 } |
| 1516 | 1429 |
| 1517 @override | 1430 @override |
| 1518 visitConstructorName(ConstructorName node) { | 1431 visitConstructorName(ConstructorName node) { |
| 1519 var typeName = _visit(node.type); | 1432 var typeName = _visit(node.type); |
| 1520 if (node.name != null) { | 1433 if (node.name != null) { |
| 1521 return js.call( | 1434 return js.call('#.#', [typeName, _emitMemberName(node.name.name)]); |
| 1522 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); | |
| 1523 } | 1435 } |
| 1524 return typeName; | 1436 return typeName; |
| 1525 } | 1437 } |
| 1526 | 1438 |
| 1527 @override | 1439 @override |
| 1528 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1440 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1529 return js.call( | 1441 return js.call( |
| 1530 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); | 1442 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
| 1531 } | 1443 } |
| 1532 | 1444 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1546 bool typeIsNonNullablePrimitiveInJS(DartType t) => | 1458 bool typeIsNonNullablePrimitiveInJS(DartType t) => |
| 1547 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); | 1459 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); |
| 1548 | 1460 |
| 1549 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 1461 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| 1550 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 1462 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
| 1551 | 1463 |
| 1552 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 1464 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
| 1553 | 1465 |
| 1554 bool _isNonNullableExpression(Expression expr) { | 1466 bool _isNonNullableExpression(Expression expr) { |
| 1555 // If the type is non-nullable, no further checking needed. | 1467 // If the type is non-nullable, no further checking needed. |
| 1556 if (rules.isNonNullableType(rules.getStaticType(expr))) return true; | 1468 if (rules.isNonNullableType(getStaticType(expr))) return true; |
| 1557 | 1469 |
| 1558 // TODO(vsm): Revisit whether we really need this when we get | 1470 // TODO(vsm): Revisit whether we really need this when we get |
| 1559 // better non-nullability in the type system. | 1471 // better non-nullability in the type system. |
| 1560 | 1472 |
| 1561 if (expr is Literal && expr is! NullLiteral) { | 1473 if (expr is Literal && expr is! NullLiteral) return true; |
| 1562 return true; | 1474 if (expr is IsExpression) return true; |
| 1563 } | |
| 1564 if (expr is ParenthesizedExpression) { | 1475 if (expr is ParenthesizedExpression) { |
| 1565 return _isNonNullableExpression(expr.expression); | 1476 return _isNonNullableExpression(expr.expression); |
| 1566 } | 1477 } |
| 1567 if (expr is Conversion) { | 1478 if (expr is Conversion) { |
| 1568 return _isNonNullableExpression(expr.expression); | 1479 return _isNonNullableExpression(expr.expression); |
| 1569 } | 1480 } |
| 1570 DartType type = null; | 1481 DartType type = null; |
| 1571 if (expr is BinaryExpression) { | 1482 if (expr is BinaryExpression) { |
| 1572 type = rules.getStaticType(expr.leftOperand); | 1483 type = getStaticType(expr.leftOperand); |
| 1573 } else if (expr is PrefixExpression) { | 1484 } else if (expr is PrefixExpression) { |
| 1574 type = rules.getStaticType(expr.operand); | 1485 type = getStaticType(expr.operand); |
| 1575 } else if (expr is PostfixExpression) { | 1486 } else if (expr is PostfixExpression) { |
| 1576 type = rules.getStaticType(expr.operand); | 1487 type = getStaticType(expr.operand); |
| 1577 } | 1488 } |
| 1578 if (type != null && typeIsPrimitiveInJS(type)) { | 1489 if (type != null && typeIsPrimitiveInJS(type)) { |
| 1579 return true; | 1490 return true; |
| 1580 } | 1491 } |
| 1581 if (expr is MethodInvocation) { | 1492 if (expr is MethodInvocation) { |
| 1582 // TODO(vsm): This logic overlaps with the resolver. | 1493 // TODO(vsm): This logic overlaps with the resolver. |
| 1583 // Where is the best place to put this? | 1494 // Where is the best place to put this? |
| 1584 var e = expr.methodName.staticElement; | 1495 var e = expr.methodName.staticElement; |
| 1585 if (e is FunctionElement && | 1496 if (e is FunctionElement && |
| 1586 e.library.name == '_foreign_helper' && | 1497 e.library.name == '_foreign_helper' && |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 1610 } else { | 1521 } else { |
| 1611 return js.call('dart.notNull(#)', _visit(expr)); | 1522 return js.call('dart.notNull(#)', _visit(expr)); |
| 1612 } | 1523 } |
| 1613 } | 1524 } |
| 1614 | 1525 |
| 1615 @override | 1526 @override |
| 1616 JS.Expression visitBinaryExpression(BinaryExpression node) { | 1527 JS.Expression visitBinaryExpression(BinaryExpression node) { |
| 1617 var op = node.operator; | 1528 var op = node.operator; |
| 1618 var left = node.leftOperand; | 1529 var left = node.leftOperand; |
| 1619 var right = node.rightOperand; | 1530 var right = node.rightOperand; |
| 1620 var leftType = rules.getStaticType(left); | 1531 var leftType = getStaticType(left); |
| 1621 var rightType = rules.getStaticType(right); | 1532 var rightType = getStaticType(right); |
| 1622 | 1533 |
| 1623 var code; | 1534 var code; |
| 1624 if (op.type.isEqualityOperator) { | 1535 if (op.type.isEqualityOperator) { |
| 1625 // If we statically know LHS or RHS is null we can generate a clean check. | 1536 // If we statically know LHS or RHS is null we can generate a clean check. |
| 1626 // We can also do this if both sides are the same primitive type. | 1537 // We can also do this if both sides are the same primitive type. |
| 1627 if (_canUsePrimitiveEquality(left, right)) { | 1538 if (_canUsePrimitiveEquality(left, right)) { |
| 1628 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #'; | 1539 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #'; |
| 1629 } else { | 1540 } else { |
| 1630 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; | 1541 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; |
| 1631 code = '${bang}dart.equals(#, #)'; | 1542 code = '${bang}dart.equals(#, #)'; |
| 1632 } | 1543 } |
| 1633 return js.call(code, [_visit(left), _visit(right)]); | 1544 return js.call(code, [_visit(left), _visit(right)]); |
| 1634 } else if (binaryOperationIsPrimitive(leftType, rightType)) { | 1545 } |
| 1546 | |
| 1547 if (binaryOperationIsPrimitive(leftType, rightType)) { | |
| 1635 // special cases where we inline the operation | 1548 // special cases where we inline the operation |
| 1636 // these values are assumed to be non-null (determined by the checker) | 1549 // these values are assumed to be non-null (determined by the checker) |
| 1637 // TODO(jmesserly): it would be nice to just inline the method from core, | 1550 // TODO(jmesserly): it would be nice to just inline the method from core, |
| 1638 // instead of special cases here. | 1551 // instead of special cases here. |
| 1639 if (op.type == TokenType.TILDE_SLASH) { | 1552 if (op.type == TokenType.TILDE_SLASH) { |
| 1640 // `a ~/ b` is equivalent to `(a / b).truncate()` | 1553 // `a ~/ b` is equivalent to `(a / b).truncate()` |
| 1641 code = '(# / #).truncate()'; | 1554 code = '(# / #).truncate()'; |
| 1642 } else { | 1555 } else { |
| 1643 // TODO(vsm): When do Dart ops not map to JS? | 1556 // TODO(vsm): When do Dart ops not map to JS? |
| 1644 code = '# $op #'; | 1557 code = '# $op #'; |
| 1645 } | 1558 } |
| 1646 return js.call(code, [notNull(left), notNull(right)]); | 1559 return js.call(code, [notNull(left), notNull(right)]); |
| 1647 } else { | |
| 1648 var opString = js.string(op.lexeme, "'"); | |
| 1649 if (rules.isDynamicTarget(left)) { | |
| 1650 // dynamic dispatch | |
| 1651 return js.call( | |
| 1652 'dart.dsend(#, #, #)', [_visit(left), opString, _visit(right)]); | |
| 1653 } else if (_isJSBuiltinType(leftType)) { | |
| 1654 // TODO(jmesserly): we'd get better readability from the static-dispatch | |
| 1655 // pattern below. Consider: | |
| 1656 // | |
| 1657 // "hello"['+']"world" | |
| 1658 // vs | |
| 1659 // core.String['+']("hello", "world") | |
| 1660 // | |
| 1661 // Infix notation is much more readable, which is a bit part of why | |
| 1662 // C# added its extension methods feature. However this would require | |
| 1663 // adding these methods to String.prototype/Number.prototype in JS. | |
| 1664 return js.call('#.#(#, #)', [ | |
| 1665 _emitTypeName(leftType), | |
| 1666 opString, | |
| 1667 _visit(left), | |
| 1668 _visit(right) | |
| 1669 ]); | |
| 1670 } else { | |
| 1671 // Generic static-dispatch, user-defined operator code path. | |
| 1672 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); | |
| 1673 } | |
| 1674 } | 1560 } |
| 1561 | |
| 1562 return _emitSend(left, op.lexeme, [right]); | |
| 1675 } | 1563 } |
| 1676 | 1564 |
| 1677 /// If the type [t] is [int] or [double], returns [num]. | 1565 /// If the type [t] is [int] or [double], returns [num]. |
| 1678 /// Otherwise returns [t]. | 1566 /// Otherwise returns [t]. |
| 1679 DartType _canonicalizeNumTypes(DartType t) { | 1567 DartType _canonicalizeNumTypes(DartType t) { |
| 1680 var numType = rules.provider.numType; | 1568 var numType = types.numType; |
| 1681 if (t is InterfaceType && t.superclass == numType) return numType; | 1569 if (t is InterfaceType && t.superclass == numType) return numType; |
| 1682 return t; | 1570 return t; |
| 1683 } | 1571 } |
| 1684 | 1572 |
| 1685 bool _canUsePrimitiveEquality(Expression left, Expression right) { | 1573 bool _canUsePrimitiveEquality(Expression left, Expression right) { |
| 1686 if (_isNull(left) || _isNull(right)) return true; | 1574 if (_isNull(left) || _isNull(right)) return true; |
| 1687 | 1575 |
| 1688 var leftType = _canonicalizeNumTypes(rules.getStaticType(left)); | 1576 var leftType = _canonicalizeNumTypes(getStaticType(left)); |
| 1689 var rightType = _canonicalizeNumTypes(rules.getStaticType(right)); | 1577 var rightType = _canonicalizeNumTypes(getStaticType(right)); |
| 1690 return _isJSBuiltinType(leftType) && leftType == rightType; | 1578 return _isJSBuiltinType(leftType) && leftType == rightType; |
| 1691 } | 1579 } |
| 1692 | 1580 |
| 1693 bool _isNull(Expression expr) => expr is NullLiteral; | 1581 bool _isNull(Expression expr) => expr is NullLiteral; |
| 1694 | 1582 |
| 1695 SimpleIdentifier _createTemporary(String name, DartType type) { | 1583 SimpleIdentifier _createTemporary(String name, DartType type) { |
| 1696 // We use an invalid source location to signal that this is a temporary. | 1584 // We use an invalid source location to signal that this is a temporary. |
| 1697 // See [_isTemporary]. | 1585 // See [_isTemporary]. |
| 1698 // TODO(jmesserly): alternatives are | 1586 // TODO(jmesserly): alternatives are |
| 1699 // * (ab)use Element.isSynthetic, which isn't currently used for | 1587 // * (ab)use Element.isSynthetic, which isn't currently used for |
| 1700 // LocalVariableElementImpl, so we could repurpose to mean "temp". | 1588 // LocalVariableElementImpl, so we could repurpose to mean "temp". |
| 1701 // * add a new property to LocalVariableElementImpl. | 1589 // * add a new property to LocalVariableElementImpl. |
| 1702 // * create a new subtype of LocalVariableElementImpl to mark a temp. | 1590 // * create a new subtype of LocalVariableElementImpl to mark a temp. |
| 1703 var id = | 1591 var id = |
| 1704 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); | 1592 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); |
| 1705 id.staticElement = new LocalVariableElementImpl.forNode(id); | 1593 id.staticElement = new LocalVariableElementImpl.forNode(id); |
| 1706 id.staticType = type; | 1594 id.staticType = type; |
| 1707 return id; | 1595 return id; |
| 1708 } | 1596 } |
| 1709 | 1597 |
| 1710 bool _isTemporary(Element node) => node.nameOffset == -1; | 1598 bool _isTemporary(Element node) => node.nameOffset == -1; |
| 1711 | 1599 |
| 1712 /// Desugars postfix increment. | |
| 1713 /// | |
| 1714 /// In the general case [expr] can be one of [IndexExpression], | |
| 1715 /// [PrefixExpression] or [PropertyAccess] and we need to | |
| 1716 /// ensure sub-expressions are evaluated once. | |
| 1717 /// | |
| 1718 /// We also need to ensure we can return the original value of the expression, | |
| 1719 /// and that it is only evaluated once. | |
| 1720 /// | |
| 1721 /// We desugar this using let*. | |
| 1722 /// | |
| 1723 /// For example, `expr1[expr2]++` can be transformed to this: | |
| 1724 /// | |
| 1725 /// // psuedocode mix of Scheme and JS: | |
| 1726 /// (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t }) | |
| 1727 /// | |
| 1728 /// The [JSMetaLet] nodes automatically simplify themselves if they can. | |
| 1729 /// For example, if the result value is not used, then `t` goes away. | |
| 1730 JSMetaLet _emitPostfixIncrement(Expression expr, Token op) { | |
| 1731 var type = rules.getStaticType(expr); | |
| 1732 assert(type != null); | |
| 1733 | |
| 1734 // Handle the left hand side, to ensure each of its subexpressions are | |
| 1735 // evaluated only once. | |
| 1736 var vars = {}; | |
| 1737 var left = _bindLeftHandSide(vars, expr, context: expr); | |
| 1738 | |
| 1739 // Desugar `x++` as `(x1 = x0 + 1, x0)` where `x0` is the original value | |
| 1740 // and `x1` is the new value for `x`. | |
| 1741 var x = _bindValue(vars, 'x', left, context: expr); | |
| 1742 | |
| 1743 var one = AstBuilder.integerLiteral(1); | |
| 1744 one.staticType = rules.provider.intType; | |
| 1745 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one); | |
| 1746 increment.staticType = type; | |
| 1747 | |
| 1748 var body = [_emitSet(left, increment), _visit(x)]; | |
| 1749 return new JSMetaLet(vars, body, statelessResult: true); | |
| 1750 } | |
| 1751 | |
| 1752 /// Returns a new expression, which can be be used safely *once* on the | 1600 /// Returns a new expression, which can be be used safely *once* on the |
| 1753 /// left hand side, and *once* on the right side of an assignment. | 1601 /// left hand side, and *once* on the right side of an assignment. |
| 1754 /// For example: `expr1[expr2] += y` can be compiled as | 1602 /// For example: `expr1[expr2] += y` can be compiled as |
| 1755 /// `expr1[expr2] = expr1[expr2] + y`. | 1603 /// `expr1[expr2] = expr1[expr2] + y`. |
| 1756 /// | 1604 /// |
| 1757 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated | 1605 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated |
| 1758 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. | 1606 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. |
| 1759 /// | 1607 /// |
| 1760 /// If the expression does not end up using `x1` or `x2` more than once, or | 1608 /// If the expression does not end up using `x1` or `x2` more than once, or |
| 1761 /// if those expressions can be treated as stateless (e.g. they are | 1609 /// if those expressions can be treated as stateless (e.g. they are |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1802 Map<String, JS.Expression> scope, String name, Expression expr, | 1650 Map<String, JS.Expression> scope, String name, Expression expr, |
| 1803 {Expression context}) { | 1651 {Expression context}) { |
| 1804 // No need to do anything for stateless expressions. | 1652 // No need to do anything for stateless expressions. |
| 1805 if (_isStateless(expr, context)) return expr; | 1653 if (_isStateless(expr, context)) return expr; |
| 1806 | 1654 |
| 1807 var t = _createTemporary('#$name', expr.staticType); | 1655 var t = _createTemporary('#$name', expr.staticType); |
| 1808 scope[name] = _visit(expr); | 1656 scope[name] = _visit(expr); |
| 1809 return t; | 1657 return t; |
| 1810 } | 1658 } |
| 1811 | 1659 |
| 1660 /// Desugars postfix increment. | |
| 1661 /// | |
| 1662 /// In the general case [expr] can be one of [IndexExpression], | |
| 1663 /// [PrefixExpression] or [PropertyAccess] and we need to | |
| 1664 /// ensure sub-expressions are evaluated once. | |
| 1665 /// | |
| 1666 /// We also need to ensure we can return the original value of the expression, | |
| 1667 /// and that it is only evaluated once. | |
| 1668 /// | |
| 1669 /// We desugar this using let*. | |
| 1670 /// | |
| 1671 /// For example, `expr1[expr2]++` can be transformed to this: | |
| 1672 /// | |
| 1673 /// // psuedocode mix of Scheme and JS: | |
| 1674 /// (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t }) | |
| 1675 /// | |
| 1676 /// The [JSMetaLet] nodes automatically simplify themselves if they can. | |
| 1677 /// For example, if the result value is not used, then `t` goes away. | |
| 1812 @override | 1678 @override |
| 1813 JS.Expression visitPostfixExpression(PostfixExpression node) { | 1679 JS.Expression visitPostfixExpression(PostfixExpression node) { |
| 1814 var op = node.operator; | 1680 var op = node.operator; |
| 1815 var expr = node.operand; | 1681 var expr = node.operand; |
| 1816 | 1682 |
| 1817 var dispatchType = rules.getStaticType(expr); | 1683 var dispatchType = getStaticType(expr); |
| 1818 if (unaryOperationIsPrimitive(dispatchType)) { | 1684 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1819 if (_isNonNullableExpression(expr)) { | 1685 if (_isNonNullableExpression(expr)) { |
| 1820 return js.call('#$op', _visit(expr)); | 1686 return js.call('#$op', _visit(expr)); |
| 1821 } | 1687 } |
| 1822 } | 1688 } |
| 1823 | 1689 |
| 1824 assert(op.lexeme == '++' || op.lexeme == '--'); | 1690 assert(op.lexeme == '++' || op.lexeme == '--'); |
| 1825 return _emitPostfixIncrement(expr, op); | 1691 |
| 1692 // Handle the left hand side, to ensure each of its subexpressions are | |
| 1693 // evaluated only once. | |
| 1694 var vars = {}; | |
| 1695 var left = _bindLeftHandSide(vars, expr, context: expr); | |
| 1696 | |
| 1697 // Desugar `x++` as `(x1 = x0 + 1, x0)` where `x0` is the original value | |
| 1698 // and `x1` is the new value for `x`. | |
| 1699 var x = _bindValue(vars, 'x', left, context: expr); | |
| 1700 | |
| 1701 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; | |
| 1702 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one) | |
| 1703 ..staticElement = node.staticElement | |
| 1704 ..staticType = getStaticType(expr); | |
| 1705 | |
| 1706 var body = [_emitSet(left, increment), _visit(x)]; | |
| 1707 return new JSMetaLet(vars, body, statelessResult: true); | |
| 1826 } | 1708 } |
| 1827 | 1709 |
| 1828 @override | 1710 @override |
| 1829 JS.Expression visitPrefixExpression(PrefixExpression node) { | 1711 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 1830 return _emitPrefixExpression(node.operator, node.operand); | 1712 var op = node.operator; |
| 1831 } | 1713 var expr = node.operand; |
| 1832 | 1714 |
| 1833 JS.Expression _emitPrefixExpression(Token op, Expression expr) { | 1715 var dispatchType = getStaticType(expr); |
| 1834 var dispatchType = rules.getStaticType(expr); | |
| 1835 if (unaryOperationIsPrimitive(dispatchType)) { | 1716 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1836 if (_isNonNullableExpression(expr)) { | 1717 if (_isNonNullableExpression(expr)) { |
| 1837 return js.call('$op#', _visit(expr)); | 1718 return js.call('$op#', _visit(expr)); |
| 1838 } else if (op.lexeme == '++' || op.lexeme == '--') { | 1719 } else if (op.lexeme == '++' || op.lexeme == '--') { |
| 1839 // We need a null check, so the increment must be expanded out. | 1720 // We need a null check, so the increment must be expanded out. |
| 1840 var mathop = op.lexeme[0]; | 1721 var mathop = op.lexeme[0]; |
| 1841 var vars = {}; | 1722 var vars = {}; |
| 1842 var x = _bindLeftHandSide(vars, expr, context: expr); | 1723 var x = _bindLeftHandSide(vars, expr, context: expr); |
| 1843 var body = js.call('# = # $mathop 1', [_visit(x), notNull(x)]); | 1724 var body = js.call('# = # $mathop 1', [_visit(x), notNull(x)]); |
| 1844 return new JSMetaLet(vars, [body]); | 1725 return new JSMetaLet(vars, [body]); |
| 1845 } else { | 1726 } else { |
| 1846 return js.call('$op#', notNull(expr)); | 1727 return js.call('$op#', notNull(expr)); |
| 1847 } | 1728 } |
| 1848 } | 1729 } |
| 1849 | 1730 |
| 1850 if (op.lexeme == '++' || op.lexeme == '--') { | 1731 if (op.lexeme == '++' || op.lexeme == '--') { |
| 1851 // Increment or decrement requires expansion. | 1732 // Increment or decrement requires expansion. |
| 1852 // Desugar `++x` as `x = x + 1`, ensuring that if `x` has subexpressions | 1733 // Desugar `++x` as `x = x + 1`, ensuring that if `x` has subexpressions |
| 1853 // (for example, x is IndexExpression) we evaluate those once. | 1734 // (for example, x is IndexExpression) we evaluate those once. |
| 1854 var one = AstBuilder.integerLiteral(1) | 1735 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; |
| 1855 ..staticType = rules.provider.intType; | 1736 return _emitOpAssign(expr, one, op.lexeme[0], node.staticElement, |
| 1856 return _emitOpAssign(expr, one, op.lexeme[0], context: expr); | 1737 context: expr); |
| 1857 } | 1738 } |
| 1858 | 1739 |
| 1859 // Call the operator | 1740 return _emitSend(expr, op.lexeme[0], []); |
| 1860 var opString = _emitMemberName(op.lexeme, unary: true); | |
| 1861 if (rules.isDynamicTarget(expr)) { | |
| 1862 // dynamic dispatch | |
| 1863 return js.call('dart.dsend(#, #)', [_visit(expr), opString]); | |
| 1864 } else if (_isJSBuiltinType(dispatchType)) { | |
| 1865 return js.call( | |
| 1866 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); | |
| 1867 } else { | |
| 1868 // Generic static-dispatch, user-defined operator code path. | |
| 1869 return js.call('#.#()', [_visit(expr), opString]); | |
| 1870 } | |
| 1871 } | 1741 } |
| 1872 | 1742 |
| 1873 // Cascades can contain [IndexExpression], [MethodInvocation] and | 1743 // Cascades can contain [IndexExpression], [MethodInvocation] and |
| 1874 // [PropertyAccess]. The code generation for those is handled in their | 1744 // [PropertyAccess]. The code generation for those is handled in their |
| 1875 // respective visit methods. | 1745 // respective visit methods. |
| 1876 @override | 1746 @override |
| 1877 JS.Node visitCascadeExpression(CascadeExpression node) { | 1747 JS.Node visitCascadeExpression(CascadeExpression node) { |
| 1878 var savedCascadeTemp = _cascadeTarget; | 1748 var savedCascadeTemp = _cascadeTarget; |
| 1879 | 1749 |
| 1880 var vars = {}; | 1750 var vars = {}; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 1899 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1769 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
| 1900 | 1770 |
| 1901 @override | 1771 @override |
| 1902 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1772 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
| 1903 | 1773 |
| 1904 @override | 1774 @override |
| 1905 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1775 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 1906 if (node.prefix.staticElement is PrefixElement) { | 1776 if (node.prefix.staticElement is PrefixElement) { |
| 1907 return _visit(node.identifier); | 1777 return _visit(node.identifier); |
| 1908 } else { | 1778 } else { |
| 1909 return _emitGet(node.prefix, node.identifier); | 1779 return _emitGet(node.prefix, node.identifier.name); |
| 1910 } | 1780 } |
| 1911 } | 1781 } |
| 1912 | 1782 |
| 1913 @override | 1783 @override |
| 1914 visitPropertyAccess(PropertyAccess node) => | 1784 visitPropertyAccess(PropertyAccess node) => |
| 1915 _emitGet(_getTarget(node), node.propertyName); | 1785 _emitGet(_getTarget(node), node.propertyName.name); |
| 1916 | 1786 |
| 1917 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1787 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 1918 _emitGet(Expression target, SimpleIdentifier name) { | 1788 JS.Expression _emitGet(Expression target, String memberName) { |
| 1789 var name = _emitMemberName(memberName, type: getStaticType(target)); | |
| 1919 if (rules.isDynamicTarget(target)) { | 1790 if (rules.isDynamicTarget(target)) { |
| 1920 return js.call( | 1791 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); |
| 1921 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); | |
| 1922 } else { | 1792 } else { |
| 1923 var e = name.staticElement; | 1793 return js.call('#.#', [_visit(target), name]); |
| 1924 var ret = js.call('#.#', [ | 1794 } |
| 1795 } | |
| 1796 | |
| 1797 JS.Expression _emitSend( | |
| 1798 Expression target, String name, List<Expression> args) { | |
| 1799 var type = getStaticType(target); | |
| 1800 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); | |
| 1801 if (rules.isDynamicTarget(target)) { | |
| 1802 // dynamic dispatch | |
| 1803 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; | |
| 1804 if (dynamicHelper != null) { | |
| 1805 return js.call( | |
| 1806 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); | |
| 1807 } | |
| 1808 return js.call('dart.$DSEND(#, #, #)', [ | |
| 1925 _visit(target), | 1809 _visit(target), |
| 1926 _emitMemberName(name.name, | 1810 memberName, |
| 1927 isStatic: e is ExecutableElement && e.isStatic, target: target) | 1811 _visitList(args) |
| 1928 ]); | 1812 ]); |
| 1929 return ret; | |
| 1930 } | 1813 } |
| 1814 if (_isJSBuiltinType(type)) { | |
| 1815 // static call pattern for bultins. | |
| 1816 return js.call('#.#(#, #)', [ | |
| 1817 _emitTypeName(type), | |
| 1818 memberName, | |
| 1819 _visit(target), | |
| 1820 _visitList(args) | |
| 1821 ]); | |
| 1822 } | |
| 1823 // Generic dispatch to a statically known method. | |
| 1824 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | |
| 1931 } | 1825 } |
| 1932 | 1826 |
| 1933 @override | 1827 @override |
| 1934 visitIndexExpression(IndexExpression node) { | 1828 visitIndexExpression(IndexExpression node) { |
| 1935 var target = _getTarget(node); | 1829 return _emitSend(_getTarget(node), '[]', [node.index]); |
| 1936 if (rules.isDynamicTarget(target)) { | |
| 1937 return js.call('dart.dindex(#, #)', [_visit(target), _visit(node.index)]); | |
| 1938 } | |
| 1939 | |
| 1940 return js.call('#.#(#)', [ | |
| 1941 _visit(target), | |
| 1942 _emitMemberName('[]', target: target), | |
| 1943 _visit(node.index) | |
| 1944 ]); | |
| 1945 } | 1830 } |
| 1946 | 1831 |
| 1947 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 1832 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
| 1948 /// Those two nodes are special because they're both allowed on left side of | 1833 /// Those two nodes are special because they're both allowed on left side of |
| 1949 /// an assignment expression and cascades. | 1834 /// an assignment expression and cascades. |
| 1950 Expression _getTarget(node) { | 1835 Expression _getTarget(node) { |
| 1951 assert(node is IndexExpression || node is PropertyAccess); | 1836 assert(node is IndexExpression || node is PropertyAccess); |
| 1952 return node.isCascaded ? _cascadeTarget : node.target; | 1837 return node.isCascaded ? _cascadeTarget : node.target; |
| 1953 } | 1838 } |
| 1954 | 1839 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2036 if (clauses == null || clauses.isEmpty) return null; | 1921 if (clauses == null || clauses.isEmpty) return null; |
| 2037 | 1922 |
| 2038 // TODO(jmesserly): need a better way to get a temporary variable. | 1923 // TODO(jmesserly): need a better way to get a temporary variable. |
| 2039 // This could incorrectly shadow a user's name. | 1924 // This could incorrectly shadow a user's name. |
| 2040 var savedCatch = _catchParameter; | 1925 var savedCatch = _catchParameter; |
| 2041 | 1926 |
| 2042 if (clauses.length == 1 && clauses.single.exceptionParameter != null) { | 1927 if (clauses.length == 1 && clauses.single.exceptionParameter != null) { |
| 2043 // Special case for a single catch. | 1928 // Special case for a single catch. |
| 2044 _catchParameter = clauses.single.exceptionParameter; | 1929 _catchParameter = clauses.single.exceptionParameter; |
| 2045 } else { | 1930 } else { |
| 2046 _catchParameter = _createTemporary('e', rules.provider.dynamicType); | 1931 _catchParameter = _createTemporary('e', types.dynamicType); |
| 2047 } | 1932 } |
| 2048 | 1933 |
| 2049 JS.Statement catchBody = js.statement('throw #;', _visit(_catchParameter)); | 1934 JS.Statement catchBody = js.statement('throw #;', _visit(_catchParameter)); |
| 2050 for (var clause in clauses.reversed) { | 1935 for (var clause in clauses.reversed) { |
| 2051 catchBody = _catchClauseGuard(clause, catchBody); | 1936 catchBody = _catchClauseGuard(clause, catchBody); |
| 2052 } | 1937 } |
| 2053 | 1938 |
| 2054 var catchVarDecl = _visit(_catchParameter); | 1939 var catchVarDecl = _visit(_catchParameter); |
| 2055 _catchParameter = savedCatch; | 1940 _catchParameter = savedCatch; |
| 2056 return new JS.Catch(catchVarDecl, new JS.Block([catchBody])); | 1941 return new JS.Catch(catchVarDecl, new JS.Block([catchBody])); |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2312 /// This follows the same pattern as EcmaScript 6 Map: | 2197 /// This follows the same pattern as EcmaScript 6 Map: |
| 2313 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ Objects/Map> | 2198 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ Objects/Map> |
| 2314 /// | 2199 /// |
| 2315 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 2200 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| 2316 /// for this transformation to happen, otherwise binary minus is assumed. | 2201 /// for this transformation to happen, otherwise binary minus is assumed. |
| 2317 /// | 2202 /// |
| 2318 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2203 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 2319 /// helper, that checks for null. The user defined method is called '=='. | 2204 /// helper, that checks for null. The user defined method is called '=='. |
| 2320 /// | 2205 /// |
| 2321 JS.Expression _emitMemberName(String name, | 2206 JS.Expression _emitMemberName(String name, |
| 2322 {bool unary: false, bool isStatic: false, Expression target}) { | 2207 {DartType type, bool unary: false, bool isStatic: false}) { |
| 2323 if (isStatic == false && target != null) { | |
| 2324 var ret = nameIfExtension(target, name); | |
| 2325 if (ret != null) return ret; | |
| 2326 } | |
| 2327 if (name.startsWith('_')) { | 2208 if (name.startsWith('_')) { |
| 2328 if (_privateNames.add(name)) _pendingPrivateNames.add(name); | 2209 if (_privateNames.add(name)) _pendingPrivateNames.add(name); |
| 2329 return new JSTemporary(name); | 2210 return new JSTemporary(name); |
| 2330 } | 2211 } |
| 2331 return _propertyName(_jsMemberName(name, unary: unary, isStatic: isStatic)); | 2212 // Check for extension method: |
| 2332 } | 2213 var extLibrary = _findExtensionLibrary(name, type); |
| 2333 | 2214 |
| 2334 String _jsMemberName(String name, {bool unary: false, bool isStatic: false}) { | 2215 if (name == '[]') { |
| 2335 if (name == '[]') return 'get'; | 2216 name = 'get'; |
| 2336 if (name == '[]=') return 'set'; | 2217 } else if (name == '[]=') { |
| 2337 if (unary && name == '-') return 'unary-'; | 2218 name = 'set'; |
| 2219 } else if (name == '-' && unary) { | |
| 2220 name = 'unary-'; | |
| 2221 } | |
| 2222 | |
| 2338 if (isStatic && invalidJSStaticMethodName(name)) { | 2223 if (isStatic && invalidJSStaticMethodName(name)) { |
| 2339 // Choose an string name. Use an invalid identifier so it won't conflict | 2224 // Choose an string name. Use an invalid identifier so it won't conflict |
| 2340 // with any valid member names. | 2225 // with any valid member names. |
| 2341 // TODO(jmesserly): this works around the problem, but I'm pretty sure we | 2226 // TODO(jmesserly): this works around the problem, but I'm pretty sure we |
| 2342 // don't need it, as static methods seemed to work. The only concrete | 2227 // don't need it, as static methods seemed to work. The only concrete |
| 2343 // issue we saw was in the defineNamedConstructor helper function. | 2228 // issue we saw was in the defineNamedConstructor helper function. |
| 2344 return '$name*'; | 2229 name = '$name*'; |
| 2345 } | 2230 } |
| 2346 return name; | 2231 |
| 2232 if (extLibrary != null) { | |
| 2233 return js.call('#.#', [ | |
| 2234 _libraryName(extLibrary), | |
| 2235 _propertyName(_addExtensionMethodName(name, extLibrary)) | |
| 2236 ]); | |
| 2237 } | |
| 2238 | |
| 2239 return _propertyName(name); | |
| 2347 } | 2240 } |
| 2348 | 2241 |
| 2349 JS.LiteralString _emitExtensionMethodName(String name) => | 2242 LibraryElement _findExtensionLibrary(String name, DartType type) { |
| 2350 js.string(_extensionMethodName(name), "'"); | 2243 if (type is! InterfaceType) return null; |
| 2351 | 2244 |
| 2352 String _extensionMethodName(String name) => '\$${_jsMemberName(name)}'; | 2245 var extLibrary = null; |
| 2246 var extensionTypes = _extensionMethods[name]; | |
| 2247 if (extensionTypes != null) { | |
| 2248 // Normalize the type to ignore generics. | |
| 2249 type = fillDynamicTypeArgs(type, types); | |
| 2250 for (var t in extensionTypes) { | |
| 2251 if (rules.isSubTypeOf(type, t)) { | |
| 2252 assert(extLibrary == null || extLibrary == t.element.library); | |
| 2253 extLibrary = t.element.library; | |
| 2254 } | |
| 2255 } | |
| 2256 } | |
| 2257 return extLibrary; | |
| 2258 } | |
| 2259 | |
| 2260 String _addExtensionMethodName(String name, LibraryElement extLibrary) { | |
| 2261 var extensionMethodName = '\$$name'; | |
| 2262 if (extLibrary == currentLibrary) { | |
| 2263 // TODO(jacobr): need to do a better job ensuring that extension method | |
| 2264 // name symbols do not conflict with other symbols before we can let | |
| 2265 // user defined libraries define extension methods. | |
| 2266 if (_extensionMethodNames.add(extensionMethodName)) { | |
| 2267 _pendingExtensionMethodNames.add(extensionMethodName); | |
| 2268 _addExport(extensionMethodName); | |
| 2269 } | |
| 2270 } | |
| 2271 return extensionMethodName; | |
| 2272 } | |
| 2353 | 2273 |
| 2354 bool _externalOrNative(node) => | 2274 bool _externalOrNative(node) => |
| 2355 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2275 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 2356 | 2276 |
| 2357 FunctionBody _functionBody(node) => | 2277 FunctionBody _functionBody(node) => |
| 2358 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2278 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 2359 | 2279 |
| 2360 /// Choose a canonical name from the library element. | 2280 /// Choose a canonical name from the library element. |
| 2361 /// This never uses the library's name (the identifier in the `library` | 2281 /// This never uses the library's name (the identifier in the `library` |
| 2362 /// declaration) as it doesn't have any meaningful rules enforced. | 2282 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2363 JS.Identifier _libraryName(LibraryElement library) { | 2283 JS.Identifier _libraryName(LibraryElement library) { |
| 2364 if (library == libraryInfo.library) return _exportsVar; | 2284 if (library == libraryInfo.library) return _exportsVar; |
| 2365 return new JS.Identifier(jsLibraryName(library)); | 2285 return new JS.Identifier(jsLibraryName(library)); |
| 2366 } | 2286 } |
| 2367 | 2287 |
| 2288 DartType getStaticType(Expression e) => rules.getStaticType(e); | |
| 2289 | |
| 2368 static bool _needsImplicitThis(Element e) => | 2290 static bool _needsImplicitThis(Element e) => |
| 2369 e is PropertyAccessorElement && !e.variable.isStatic || | 2291 e is PropertyAccessorElement && !e.variable.isStatic || |
| 2370 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; | 2292 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; |
| 2293 | |
| 2294 static const DPUT = 'dput'; | |
|
Jacob
2015/04/14 21:41:06
nit: no idea if dart has an official convention bu
Jennifer Messerly
2015/04/14 22:17:19
Hmm. I guess I absorbed a rule at some point from
Jacob
2015/04/14 22:25:00
I don't care deeply about it but I'd personally ma
Jennifer Messerly
2015/04/14 22:45:28
OK. Moved to top, added comment.
| |
| 2295 static const DLOAD = 'dload'; | |
| 2296 static const DINDEX = 'dindex'; | |
| 2297 static const DSETINDEX = 'dsetindex'; | |
| 2298 static const DCALL = 'dcall'; | |
| 2299 static const DSEND = 'dsend'; | |
| 2371 } | 2300 } |
| 2372 | 2301 |
| 2373 class JSGenerator extends CodeGenerator { | 2302 class JSGenerator extends CodeGenerator { |
| 2374 final JSCodeOptions options; | 2303 final JSCodeOptions options; |
| 2375 | 2304 |
| 2305 /// For fast lookup of extension methods, we first check the name, then do a | |
| 2306 /// (possibly expensive) subtype test to see if it matches one of the types | |
| 2307 /// that declares that method. | |
| 2308 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); | |
| 2309 | |
| 2376 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) | 2310 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) |
| 2377 : super(outDir, root, rules); | 2311 : super(outDir, root, rules) { |
| 2312 | |
| 2313 // TODO(jacobr): determine the the set of types with extension methods from | |
| 2314 // the annotations rather than hard coding the list once the analyzer | |
| 2315 // supports summaries. | |
| 2316 var extensionTypes = [types.listType, types.iterableType]; | |
| 2317 for (var type in extensionTypes) { | |
| 2318 type = fillDynamicTypeArgs(type, rules.provider); | |
| 2319 var e = type.element; | |
| 2320 var names = new HashSet<String>() | |
| 2321 ..addAll(e.methods.map((m) => m.name)) | |
| 2322 ..addAll(e.accessors.map((m) => m.name)); | |
| 2323 for (var name in names) { | |
| 2324 _extensionMethods.putIfAbsent(name, () => []).add(type); | |
| 2325 } | |
| 2326 } | |
| 2327 } | |
| 2328 | |
| 2329 TypeProvider get types => rules.provider; | |
| 2378 | 2330 |
| 2379 String generateLibrary(LibraryUnit unit, LibraryInfo info) { | 2331 String generateLibrary(LibraryUnit unit, LibraryInfo info) { |
| 2380 var jsTree = new JSCodegenVisitor(info, rules).emitLibrary(unit); | 2332 var jsTree = |
| 2333 new JSCodegenVisitor(info, rules, _extensionMethods).emitLibrary(unit); | |
| 2381 | 2334 |
| 2382 var outputPath = path.join(outDir, jsOutputPath(info, root)); | 2335 var outputPath = path.join(outDir, jsOutputPath(info, root)); |
| 2383 new Directory(path.dirname(outputPath)).createSync(recursive: true); | 2336 new Directory(path.dirname(outputPath)).createSync(recursive: true); |
| 2384 | 2337 |
| 2385 if (options.emitSourceMaps) { | 2338 if (options.emitSourceMaps) { |
| 2386 var outFilename = path.basename(outputPath); | 2339 var outFilename = path.basename(outputPath); |
| 2387 var printer = new srcmaps.Printer(outFilename); | 2340 var printer = new srcmaps.Printer(outFilename); |
| 2388 _writeNode( | 2341 _writeNode( |
| 2389 new SourceMapPrintingContext(printer, path.dirname(outputPath)), | 2342 new SourceMapPrintingContext(printer, path.dirname(outputPath)), |
| 2390 jsTree); | 2343 jsTree); |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2600 if (args.isNotEmpty && args[0] is NamedExpression) { | 2553 if (args.isNotEmpty && args[0] is NamedExpression) { |
| 2601 NamedExpression named = args[0]; | 2554 NamedExpression named = args[0]; |
| 2602 if (named.name.label.name == argName && | 2555 if (named.name.label.name == argName && |
| 2603 named.expression is StringLiteral) { | 2556 named.expression is StringLiteral) { |
| 2604 return (named.expression as StringLiteral).stringValue; | 2557 return (named.expression as StringLiteral).stringValue; |
| 2605 } | 2558 } |
| 2606 } | 2559 } |
| 2607 } | 2560 } |
| 2608 return null; | 2561 return null; |
| 2609 } | 2562 } |
| OLD | NEW |