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