Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 | 2 |
| 3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
| 4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
| 5 | 5 |
| 6 import 'dart:collection' show HashMap, HashSet; | 6 import 'dart:collection' show HashMap, HashSet; |
| 7 import 'dart:math' show min, max; | 7 import 'dart:math' show min, max; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
| 11 import 'package:analyzer/dart/ast/standard_ast_factory.dart'; | 11 import 'package:analyzer/dart/ast/standard_ast_factory.dart'; |
| 12 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 12 import 'package:analyzer/dart/ast/token.dart' show TokenType; |
| 13 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; | 13 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| 14 import 'package:analyzer/dart/element/element.dart'; | 14 import 'package:analyzer/dart/element/element.dart'; |
| 15 import 'package:analyzer/dart/element/type.dart'; | 15 import 'package:analyzer/dart/element/type.dart'; |
| 16 import 'package:analyzer/src/dart/ast/token.dart' show StringToken; | 16 import 'package:analyzer/src/dart/ast/token.dart' show StringToken; |
| 17 import 'package:analyzer/src/dart/element/element.dart' | 17 import 'package:analyzer/src/dart/element/element.dart' |
| 18 show FieldElementImpl, LocalVariableElementImpl; | 18 show FieldElementImpl, LocalVariableElementImpl; |
| 19 import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl; | 19 import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl; |
| 20 import 'package:analyzer/src/dart/sdk/sdk.dart'; | 20 import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| 21 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | 21 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
| 22 import 'package:analyzer/src/generated/resolver.dart' | 22 import 'package:analyzer/src/generated/resolver.dart' |
| 23 show TypeProvider, NamespaceBuilder; | 23 show TypeProvider, NamespaceBuilder; |
| 24 import 'package:analyzer/src/generated/type_system.dart' | 24 import 'package:analyzer/src/generated/type_system.dart' |
| 25 show StrongTypeSystemImpl; | 25 show StrongTypeSystemImpl; |
| 26 import 'package:analyzer/src/summary/idl.dart' show UnlinkedUnit; | 26 import 'package:analyzer/src/summary/idl.dart' show UnlinkedUnit; |
| 27 import 'package:analyzer/src/summary/link.dart' as summary_link; | 27 import 'package:analyzer/src/summary/link.dart' as summary_link; |
| 28 import 'package:analyzer/src/summary/package_bundle_reader.dart'; | 28 import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
| 29 import 'package:analyzer/src/summary/summarize_ast.dart' | 29 import 'package:analyzer/src/summary/summarize_ast.dart' |
| 30 show serializeAstUnlinked; | 30 show serializeAstUnlinked; |
| 31 import 'package:analyzer/src/summary/summarize_elements.dart' | 31 import 'package:analyzer/src/summary/summarize_elements.dart' |
| 32 show PackageBundleAssembler; | 32 show PackageBundleAssembler; |
| 33 import 'package:analyzer/src/summary/summary_sdk.dart'; | 33 import 'package:analyzer/src/summary/summary_sdk.dart'; |
| 34 import 'package:analyzer/src/task/strong/ast_properties.dart' | 34 import 'package:analyzer/src/task/strong/ast_properties.dart'; |
| 35 show isDynamicInvoke, setIsDynamicInvoke, getImplicitAssignmentCast; | |
| 36 import 'package:path/path.dart' show isWithin, relative, separator; | 35 import 'package:path/path.dart' show isWithin, relative, separator; |
| 37 | 36 |
| 38 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 37 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
| 39 import '../js_ast/js_ast.dart' as JS; | 38 import '../js_ast/js_ast.dart' as JS; |
| 40 import '../js_ast/js_ast.dart' show js; | 39 import '../js_ast/js_ast.dart' show js; |
| 41 import 'ast_builder.dart' show AstBuilder; | 40 import 'ast_builder.dart' show AstBuilder; |
| 42 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; | 41 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; |
| 43 import 'element_helpers.dart'; | 42 import 'element_helpers.dart'; |
| 44 import 'extension_types.dart' show ExtensionTypeSet; | 43 import 'extension_types.dart' show ExtensionTypeSet; |
| 45 import 'js_interop.dart'; | 44 import 'js_interop.dart'; |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 bool _isInForeignJS = false; | 182 bool _isInForeignJS = false; |
| 184 | 183 |
| 185 /// Information about virtual and overridden fields/getters/setters in the | 184 /// Information about virtual and overridden fields/getters/setters in the |
| 186 /// class we're currently compiling, or `null` if we aren't compiling a class. | 185 /// class we're currently compiling, or `null` if we aren't compiling a class. |
| 187 ClassPropertyModel _classProperties; | 186 ClassPropertyModel _classProperties; |
| 188 | 187 |
| 189 /// Information about virtual fields for all libraries in the current build | 188 /// Information about virtual fields for all libraries in the current build |
| 190 /// unit. | 189 /// unit. |
| 191 final virtualFields = new VirtualFieldModel(); | 190 final virtualFields = new VirtualFieldModel(); |
| 192 | 191 |
| 192 final _usedCovariantPrivateMembers = new HashSet<ExecutableElement>(); | |
| 193 | |
| 193 CodeGenerator( | 194 CodeGenerator( |
| 194 AnalysisContext c, this.summaryData, this.options, this._extensionTypes) | 195 AnalysisContext c, this.summaryData, this.options, this._extensionTypes) |
| 195 : context = c, | 196 : context = c, |
| 196 rules = new StrongTypeSystemImpl(c.typeProvider), | 197 rules = new StrongTypeSystemImpl(c.typeProvider), |
| 197 types = c.typeProvider, | 198 types = c.typeProvider, |
| 198 _asyncStreamIterator = | 199 _asyncStreamIterator = |
| 199 _getLibrary(c, 'dart:async').getType('StreamIterator').type, | 200 _getLibrary(c, 'dart:async').getType('StreamIterator').type, |
| 200 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), | 201 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), |
| 201 interceptorClass = | 202 interceptorClass = |
| 202 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), | 203 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 273 // Preserve only API-level information in the summary. | 274 // Preserve only API-level information in the summary. |
| 274 bundle.flushInformative(); | 275 bundle.flushInformative(); |
| 275 return bundle.toBuffer(); | 276 return bundle.toBuffer(); |
| 276 } | 277 } |
| 277 | 278 |
| 278 JS.Program _emitModule(List<CompilationUnit> compilationUnits, String name) { | 279 JS.Program _emitModule(List<CompilationUnit> compilationUnits, String name) { |
| 279 if (_moduleItems.isNotEmpty) { | 280 if (_moduleItems.isNotEmpty) { |
| 280 throw new StateError('Can only call emitModule once.'); | 281 throw new StateError('Can only call emitModule once.'); |
| 281 } | 282 } |
| 282 | 283 |
| 284 for (var unit in compilationUnits) { | |
| 285 _usedCovariantPrivateMembers.addAll(getCovariantPrivateMembers(unit)); | |
| 286 } | |
| 287 | |
| 283 // Transform the AST to make coercions explicit. | 288 // Transform the AST to make coercions explicit. |
| 284 compilationUnits = CoercionReifier.reify(compilationUnits); | 289 compilationUnits = CoercionReifier.reify(compilationUnits); |
| 285 | 290 |
| 286 if (compilationUnits.any((u) => isSdkInternalRuntime( | 291 if (compilationUnits.any((u) => isSdkInternalRuntime( |
| 287 resolutionMap.elementDeclaredByCompilationUnit(u).library))) { | 292 resolutionMap.elementDeclaredByCompilationUnit(u).library))) { |
| 288 // Don't allow these to be renamed when we're building the SDK. | 293 // Don't allow these to be renamed when we're building the SDK. |
| 289 // There is JS code in dart:* that depends on their names. | 294 // There is JS code in dart:* that depends on their names. |
| 290 _runtimeModule = new JS.Identifier('dart'); | 295 _runtimeModule = new JS.Identifier('dart'); |
| 291 _extensionSymbolsModule = new JS.Identifier('dartx'); | 296 _extensionSymbolsModule = new JS.Identifier('dartx'); |
| 292 } else { | 297 } else { |
| (...skipping 391 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 684 _moduleItems.add(js.statement( | 689 _moduleItems.add(js.statement( |
| 685 '#.# = #;', [emitLibraryName(currentLibrary), name.selector, name])); | 690 '#.# = #;', [emitLibraryName(currentLibrary), name.selector, name])); |
| 686 } | 691 } |
| 687 } | 692 } |
| 688 | 693 |
| 689 @override | 694 @override |
| 690 visitAsExpression(AsExpression node) { | 695 visitAsExpression(AsExpression node) { |
| 691 Expression fromExpr = node.expression; | 696 Expression fromExpr = node.expression; |
| 692 var from = getStaticType(fromExpr); | 697 var from = getStaticType(fromExpr); |
| 693 var to = node.type.type; | 698 var to = node.type.type; |
| 694 | |
| 695 JS.Expression jsFrom = _visit(fromExpr); | 699 JS.Expression jsFrom = _visit(fromExpr); |
| 696 | 700 |
| 701 bool isImplicit = CoercionReifier.isImplicitCast(node); | |
| 702 | |
| 697 // Skip the cast if it's not needed. | 703 // Skip the cast if it's not needed. |
| 698 if (rules.isSubtypeOf(from, to)) return jsFrom; | 704 if (!isImplicit && rules.isSubtypeOf(from, to)) return jsFrom; |
|
vsm
2017/06/23 15:59:07
I'd like to understand this better.
If *from* is
Jennifer Messerly
2017/06/23 18:36:21
sure! this code was here already though? happy to
| |
| 699 | 705 |
| 700 // All Dart number types map to a JS double. | 706 // All Dart number types map to a JS double. |
| 701 if (_isNumberInJS(from) && _isNumberInJS(to)) { | 707 if (_isNumberInJS(from) && _isNumberInJS(to)) { |
| 702 // Make sure to check when converting to int. | 708 // Make sure to check when converting to int. |
| 703 if (from != types.intType && to == types.intType) { | 709 if (from != types.intType && to == types.intType) { |
| 704 // TODO(jmesserly): fuse this with notNull check. | 710 // TODO(jmesserly): fuse this with notNull check. |
| 705 return _callHelper('asInt(#)', jsFrom); | 711 return _callHelper('asInt(#)', jsFrom); |
| 706 } | 712 } |
| 707 | 713 |
| 708 // A no-op in JavaScript. | 714 // A no-op in JavaScript. |
| 709 return jsFrom; | 715 return jsFrom; |
| 710 } | 716 } |
| 711 | 717 |
| 712 var type = _emitType(to); | 718 var type = _emitType(to); |
| 713 if (CoercionReifier.isImplicitCast(node)) { | 719 return js.call(isImplicit ? '#._check(#)' : '#.as(#)', [type, jsFrom]); |
| 714 return js.call('#._check(#)', [type, jsFrom]); | |
| 715 } else { | |
| 716 return js.call('#.as(#)', [type, jsFrom]); | |
| 717 } | |
| 718 } | 720 } |
| 719 | 721 |
| 720 @override | 722 @override |
| 721 visitIsExpression(IsExpression node) { | 723 visitIsExpression(IsExpression node) { |
| 722 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 724 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
| 723 JS.Expression result; | 725 JS.Expression result; |
| 724 var type = node.type.type; | 726 var type = node.type.type; |
| 725 var lhs = _visit(node.expression); | 727 var lhs = _visit(node.expression); |
| 726 var typeofName = _jsTypeofName(type); | 728 var typeofName = _jsTypeofName(type); |
| 727 // Inline primitives other than int (which requires a Math.floor check). | 729 // Inline primitives other than int (which requires a Math.floor check). |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 801 ClassElement classElem = node.element; | 803 ClassElement classElem = node.element; |
| 802 var supertype = classElem.supertype; | 804 var supertype = classElem.supertype; |
| 803 | 805 |
| 804 var typeFormals = classElem.typeParameters; | 806 var typeFormals = classElem.typeParameters; |
| 805 var isGeneric = typeFormals.isNotEmpty; | 807 var isGeneric = typeFormals.isNotEmpty; |
| 806 | 808 |
| 807 // Special case where supertype is Object, and we mixin a single class. | 809 // Special case where supertype is Object, and we mixin a single class. |
| 808 // The resulting 'class' is a mixable class in this case. | 810 // The resulting 'class' is a mixable class in this case. |
| 809 bool isMixinAlias = supertype.isObject && classElem.mixins.length == 1; | 811 bool isMixinAlias = supertype.isObject && classElem.mixins.length == 1; |
| 810 | 812 |
| 813 // TODO(jmesserly): what do we do if the mixin alias has implied superclass | |
| 814 // covariance checks (due to new interfaces)? We can't add them without | |
| 815 // messing up the inheritance chain and breaking the ability of the mixin | |
| 816 // alias to be mixed in elsewhere. We're going to need something special, | |
| 817 // like adding these checks when we copy in the methods. | |
| 818 var jsMethods = <JS.Method>[]; | |
| 819 _emitSuperclassCovarianceChecks(node, jsMethods); | |
| 811 var classExpr = isMixinAlias | 820 var classExpr = isMixinAlias |
| 812 ? _emitClassHeritage(classElem) | 821 ? _emitClassHeritage(classElem) |
| 813 : _emitClassExpression(classElem, []); | 822 : _emitClassExpression(classElem, jsMethods); |
| 814 var className = isGeneric | 823 var className = isGeneric |
| 815 ? new JS.Identifier(classElem.name) | 824 ? new JS.Identifier(classElem.name) |
| 816 : _emitTopLevelName(classElem); | 825 : _emitTopLevelName(classElem); |
| 817 var block = <JS.Statement>[]; | 826 var block = <JS.Statement>[]; |
| 818 | 827 |
| 819 if (isGeneric) { | 828 if (isGeneric) { |
| 820 if (isMixinAlias) { | 829 if (isMixinAlias) { |
| 821 block.add(js.statement('const # = #;', [className, classExpr])); | 830 block.add(js.statement('const # = #;', [className, classExpr])); |
| 822 } else { | 831 } else { |
| 823 block.add(new JS.ClassDeclaration(classExpr)); | 832 block.add(new JS.ClassDeclaration(classExpr)); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 887 JS.Expression className; | 896 JS.Expression className; |
| 888 if (classElem.typeParameters.isNotEmpty) { | 897 if (classElem.typeParameters.isNotEmpty) { |
| 889 // Generic classes will be defined inside a function that closes over the | 898 // Generic classes will be defined inside a function that closes over the |
| 890 // type parameter. So we can use their local variable name directly. | 899 // type parameter. So we can use their local variable name directly. |
| 891 className = new JS.Identifier(classElem.name); | 900 className = new JS.Identifier(classElem.name); |
| 892 } else { | 901 } else { |
| 893 className = _emitTopLevelName(classElem); | 902 className = _emitTopLevelName(classElem); |
| 894 } | 903 } |
| 895 | 904 |
| 896 var savedClassProperties = _classProperties; | 905 var savedClassProperties = _classProperties; |
| 897 _classProperties = | 906 _classProperties = new ClassPropertyModel.build( |
| 898 new ClassPropertyModel.build(_extensionTypes, virtualFields, classElem); | 907 _extensionTypes, |
| 908 virtualFields, | |
| 909 classElem, | |
| 910 getClassCovariantParameters(node), | |
| 911 _usedCovariantPrivateMembers); | |
| 899 | 912 |
| 900 var jsCtors = _defineConstructors(classElem, className, fields, ctors); | 913 var jsCtors = _defineConstructors(classElem, className, fields, ctors); |
| 901 var classExpr = _emitClassExpression(classElem, _emitClassMethods(node), | 914 var classExpr = _emitClassExpression(classElem, _emitClassMethods(node), |
| 902 fields: allFields); | 915 fields: allFields); |
| 903 | 916 |
| 904 var body = <JS.Statement>[]; | 917 var body = <JS.Statement>[]; |
| 905 _initExtensionSymbols(classElem, methods, fields, body); | 918 _initExtensionSymbols(classElem, methods, fields, body); |
| 906 _emitSuperHelperSymbols(body); | 919 _emitSuperHelperSymbols(body); |
| 907 | 920 |
| 908 // Emit the class, e.g. `core.Object = class Object { ... }` | 921 // Emit the class, e.g. `core.Object = class Object { ... }` |
| (...skipping 515 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1424 // (We could do this same optimization for any interface with an `iterator` | 1437 // (We could do this same optimization for any interface with an `iterator` |
| 1425 // method, but that's more expensive to check for, so it doesn't seem worth | 1438 // method, but that's more expensive to check for, so it doesn't seem worth |
| 1426 // it. The above case for an explicit `iterator` method will catch those.) | 1439 // it. The above case for an explicit `iterator` method will catch those.) |
| 1427 if (!hasJsPeer && !hasIterator && _implementsIterable(type)) { | 1440 if (!hasJsPeer && !hasIterator && _implementsIterable(type)) { |
| 1428 jsMethods.add(_emitIterable(type)); | 1441 jsMethods.add(_emitIterable(type)); |
| 1429 } | 1442 } |
| 1430 | 1443 |
| 1431 // Add all of the super helper methods | 1444 // Add all of the super helper methods |
| 1432 jsMethods.addAll(_superHelpers.values); | 1445 jsMethods.addAll(_superHelpers.values); |
| 1433 | 1446 |
| 1447 _emitSuperclassCovarianceChecks(node, jsMethods); | |
| 1434 return jsMethods.where((m) => m != null).toList(growable: false); | 1448 return jsMethods.where((m) => m != null).toList(growable: false); |
| 1435 } | 1449 } |
| 1436 | 1450 |
| 1451 void _emitSuperclassCovarianceChecks( | |
| 1452 Declaration node, List<JS.Method> methods) { | |
| 1453 var covariantParams = getSuperclassCovariantParameters(node); | |
| 1454 if (covariantParams == null) return; | |
| 1455 | |
| 1456 for (var member in covariantParams.map((p) => p.enclosingElement).toSet()) { | |
| 1457 var name = _declareMemberName(member); | |
| 1458 if (member is PropertyAccessorElement) { | |
| 1459 var param = member.parameters[0]; | |
| 1460 assert(covariantParams.contains(param)); | |
| 1461 methods.add(new JS.Method( | |
| 1462 name, | |
| 1463 js.call('function(x) { return super.#(#._check(x)); }', | |
| 1464 [name, _emitType(param.type)]), | |
| 1465 isSetter: true)); | |
| 1466 methods.add(new JS.Method( | |
| 1467 name, js.call('function() { return super.#; }', [name]), | |
| 1468 isGetter: true)); | |
| 1469 } else if (member is MethodElement) { | |
| 1470 var type = member.type; | |
| 1471 | |
| 1472 var body = <JS.Statement>[]; | |
| 1473 var typeFormals = _emitTypeFormals(type.typeFormals); | |
| 1474 if (type.typeFormals.any(covariantParams.contains)) { | |
| 1475 body.add(js.statement( | |
| 1476 '#.checkBounds([#]);', [_emitType(type), typeFormals])); | |
| 1477 } | |
| 1478 | |
| 1479 var jsParams = <JS.Parameter>[]; | |
| 1480 bool foundNamedParams = false; | |
| 1481 for (var param in member.parameters) { | |
| 1482 JS.Parameter jsParam; | |
| 1483 if (param.kind == ParameterKind.NAMED) { | |
| 1484 foundNamedParams = true; | |
| 1485 if (covariantParams.contains(param)) { | |
| 1486 var name = _propertyName(param.name); | |
| 1487 body.add(js.statement('if (# in #) #._check(#.#);', [ | |
| 1488 name, | |
| 1489 namedArgumentTemp, | |
| 1490 _emitType(param.type), | |
| 1491 namedArgumentTemp, | |
| 1492 name | |
| 1493 ])); | |
| 1494 } | |
| 1495 } else { | |
| 1496 jsParam = _emitParameter(param); | |
| 1497 jsParams.add(jsParam); | |
| 1498 if (covariantParams.contains(param)) { | |
| 1499 if (param.kind == ParameterKind.POSITIONAL) { | |
| 1500 body.add(js.statement('if (# !== void 0) #._check(#);', | |
| 1501 [jsParam, _emitType(param.type), jsParam])); | |
| 1502 } else { | |
| 1503 body.add(js.statement( | |
| 1504 '#._check(#);', [_emitType(param.type), jsParam])); | |
| 1505 } | |
| 1506 } | |
| 1507 } | |
| 1508 } | |
| 1509 | |
| 1510 if (foundNamedParams) jsParams.add(namedArgumentTemp); | |
| 1511 | |
| 1512 if (typeFormals.isEmpty) { | |
| 1513 body.add(js.statement('return super.#(#);', [name, jsParams])); | |
| 1514 } else { | |
| 1515 body.add(js.statement( | |
| 1516 'return super.#(#)(#);', [name, typeFormals, jsParams])); | |
| 1517 } | |
| 1518 var fn = new JS.Fun(jsParams, new JS.Block(body), | |
| 1519 typeParams: typeFormals, returnType: emitTypeRef(type.returnType)); | |
| 1520 methods.add(new JS.Method(name, _makeGenericFunction(fn))); | |
| 1521 } else { | |
| 1522 throw new StateError( | |
| 1523 'unable to generate a covariant check for element: `$member` ' | |
| 1524 '(${member.runtimeType})'); | |
| 1525 } | |
| 1526 } | |
| 1527 } | |
| 1528 | |
| 1437 /// Emits a Dart factory constructor to a JS static method. | 1529 /// Emits a Dart factory constructor to a JS static method. |
| 1438 JS.Method _emitFactoryConstructor(ConstructorDeclaration node) { | 1530 JS.Method _emitFactoryConstructor(ConstructorDeclaration node) { |
| 1439 var element = node.element; | 1531 var element = node.element; |
| 1440 var returnType = emitTypeRef(element.returnType); | 1532 var returnType = emitTypeRef(element.returnType); |
| 1441 var name = _constructorName(element); | 1533 var name = _constructorName(element); |
| 1442 JS.Fun fun; | 1534 JS.Fun fun; |
| 1443 | 1535 |
| 1444 var redirect = node.redirectedConstructor; | 1536 var redirect = node.redirectedConstructor; |
| 1445 if (redirect != null) { | 1537 if (redirect != null) { |
| 1446 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 1538 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1567 if (!mocks.containsKey(element.name)) { | 1659 if (!mocks.containsKey(element.name)) { |
| 1568 var getter = js.call('function() { return this[#]; }', [virtualField]); | 1660 var getter = js.call('function() { return this[#]; }', [virtualField]); |
| 1569 result.add(new JS.Method(name, getter, isGetter: true)); | 1661 result.add(new JS.Method(name, getter, isGetter: true)); |
| 1570 } | 1662 } |
| 1571 | 1663 |
| 1572 if (!mocks.containsKey(element.name + '=')) { | 1664 if (!mocks.containsKey(element.name + '=')) { |
| 1573 var args = field.isFinal | 1665 var args = field.isFinal |
| 1574 ? [new JS.Super(), name] | 1666 ? [new JS.Super(), name] |
| 1575 : [new JS.This(), virtualField]; | 1667 : [new JS.This(), virtualField]; |
| 1576 | 1668 |
| 1577 result.add(new JS.Method( | 1669 String jsCode; |
| 1578 name, js.call('function(value) { #[#] = value; }', args), | 1670 var setter = element.setter; |
| 1579 isSetter: true)); | 1671 var covariantParams = _classProperties.covariantParameters; |
| 1672 if (setter != null && | |
| 1673 covariantParams != null && | |
| 1674 covariantParams.contains(setter.parameters[0])) { | |
| 1675 args.add(_emitType(setter.parameters[0].type)); | |
| 1676 jsCode = 'function(value) { #[#] = #._check(value); }'; | |
| 1677 } else { | |
| 1678 jsCode = 'function(value) { #[#] = value; }'; | |
| 1679 } | |
| 1680 | |
| 1681 result.add(new JS.Method(name, js.call(jsCode, args), isSetter: true)); | |
| 1580 } | 1682 } |
| 1581 | 1683 |
| 1582 return result; | 1684 return result; |
| 1583 } | 1685 } |
| 1584 | 1686 |
| 1585 /// Emit a getter or setter that simply forwards to the superclass getter or | 1687 /// Emit a getter or setter that simply forwards to the superclass getter or |
| 1586 /// setter. This is needed because in ES6, if you only override a getter | 1688 /// setter. This is needed because in ES6, if you only override a getter |
| 1587 /// (alternatively, a setter), then there is an implicit override of the | 1689 /// (alternatively, a setter), then there is an implicit override of the |
| 1588 /// setter (alternatively, the getter) that does nothing. | 1690 /// setter (alternatively, the getter) that does nothing. |
| 1589 JS.Method _emitSuperAccessorWrapper( | 1691 JS.Method _emitSuperAccessorWrapper( |
| (...skipping 683 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2273 /// Emits argument initializers, which handles optional/named args, as well | 2375 /// Emits argument initializers, which handles optional/named args, as well |
| 2274 /// as generic type checks needed due to our covariance. | 2376 /// as generic type checks needed due to our covariance. |
| 2275 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { | 2377 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { |
| 2276 // Constructor argument initializers are emitted earlier in the code, rather | 2378 // Constructor argument initializers are emitted earlier in the code, rather |
| 2277 // than always when we visit the function body, so we control it explicitly. | 2379 // than always when we visit the function body, so we control it explicitly. |
| 2278 if (node is ConstructorDeclaration != constructor) return null; | 2380 if (node is ConstructorDeclaration != constructor) return null; |
| 2279 | 2381 |
| 2280 var parameters = _parametersOf(node); | 2382 var parameters = _parametersOf(node); |
| 2281 if (parameters == null) return null; | 2383 if (parameters == null) return null; |
| 2282 | 2384 |
| 2385 var covariantParams = _classProperties?.covariantParameters; | |
| 2386 | |
| 2283 var body = <JS.Statement>[]; | 2387 var body = <JS.Statement>[]; |
| 2284 for (var param in parameters.parameters) { | 2388 for (var param in parameters.parameters) { |
| 2285 var jsParam = _emitSimpleIdentifier(param.identifier); | 2389 var jsParam = _emitSimpleIdentifier(param.identifier); |
| 2286 | 2390 |
| 2287 if (!options.destructureNamedParams) { | 2391 if (!options.destructureNamedParams) { |
| 2288 if (param.kind == ParameterKind.NAMED) { | 2392 if (param.kind == ParameterKind.NAMED) { |
| 2289 // Parameters will be passed using their real names, not the (possibly | 2393 // Parameters will be passed using their real names, not the (possibly |
| 2290 // renamed) local variable. | 2394 // renamed) local variable. |
| 2291 var paramName = js.string(param.identifier.name, "'"); | 2395 var paramName = js.string(param.identifier.name, "'"); |
| 2292 | 2396 |
| 2293 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. | 2397 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. |
| 2294 body.add(js.statement('let # = # && # in # ? #.# : #;', [ | 2398 body.add(js.statement('let # = # && # in # ? #.# : #;', [ |
| 2295 jsParam, | 2399 jsParam, |
| 2296 namedArgumentTemp, | 2400 namedArgumentTemp, |
| 2297 paramName, | 2401 paramName, |
| 2298 namedArgumentTemp, | 2402 namedArgumentTemp, |
| 2299 namedArgumentTemp, | 2403 namedArgumentTemp, |
| 2300 paramName, | 2404 paramName, |
| 2301 _defaultParamValue(param), | 2405 _defaultParamValue(param), |
| 2302 ])); | 2406 ])); |
| 2303 } else if (param.kind == ParameterKind.POSITIONAL) { | 2407 } else if (param.kind == ParameterKind.POSITIONAL) { |
| 2304 body.add(js.statement('if (# === void 0) # = #;', | 2408 body.add(js.statement('if (# === void 0) # = #;', |
| 2305 [jsParam, jsParam, _defaultParamValue(param)])); | 2409 [jsParam, jsParam, _defaultParamValue(param)])); |
| 2306 } | 2410 } |
| 2307 } | 2411 } |
| 2308 | 2412 |
| 2309 // TODO(jmesserly): various problems here, see: | 2413 var paramElement = resolutionMap.elementDeclaredByFormalParameter(param); |
| 2310 // https://github.com/dart-lang/sdk/issues/27259 | 2414 if (paramElement.isCovariant || |
| 2311 var paramType = | 2415 covariantParams != null && covariantParams.contains(paramElement)) { |
| 2312 resolutionMap.elementDeclaredByFormalParameter(param).type; | 2416 var castType = _emitType(paramElement.type); |
| 2313 if (node is MethodDeclaration && | |
| 2314 (resolutionMap.elementDeclaredByFormalParameter(param).isCovariant || | |
| 2315 _unsoundCovariant(paramType, true))) { | |
| 2316 var castType = _emitType(paramType); | |
| 2317 body.add(js.statement('#._check(#);', [castType, jsParam])); | 2417 body.add(js.statement('#._check(#);', [castType, jsParam])); |
| 2318 } | 2418 } |
| 2319 } | 2419 } |
| 2320 return body.isEmpty ? null : _statement(body); | 2420 return body.isEmpty ? null : _statement(body); |
| 2321 } | 2421 } |
| 2322 | 2422 |
| 2323 /// Given a type [t], return whether or not t is unsoundly covariant. | |
| 2324 /// If [contravariant] is true, then t appears in a contravariant | |
| 2325 /// position. | |
| 2326 bool _unsoundCovariant(DartType t, bool contravariant) { | |
| 2327 if (t is TypeParameterType) { | |
| 2328 return contravariant && t.element.enclosingElement is ClassElement; | |
| 2329 } | |
| 2330 if (t is FunctionType) { | |
| 2331 if (_unsoundCovariant(t.returnType, contravariant)) return true; | |
| 2332 return t.parameters.any((p) => _unsoundCovariant(p.type, !contravariant)); | |
| 2333 } | |
| 2334 if (t is ParameterizedType) { | |
| 2335 return t.typeArguments.any((t) => _unsoundCovariant(t, contravariant)); | |
| 2336 } | |
| 2337 return false; | |
| 2338 } | |
| 2339 | |
| 2340 JS.Expression _defaultParamValue(FormalParameter param) { | 2423 JS.Expression _defaultParamValue(FormalParameter param) { |
| 2341 if (param is DefaultFormalParameter && param.defaultValue != null) { | 2424 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 2342 return _visit(param.defaultValue); | 2425 return _visit(param.defaultValue); |
| 2343 } else { | 2426 } else { |
| 2344 return new JS.LiteralNull(); | 2427 return new JS.LiteralNull(); |
| 2345 } | 2428 } |
| 2346 } | 2429 } |
| 2347 | 2430 |
| 2348 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { | 2431 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { |
| 2349 String name = | 2432 String name = |
| (...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2599 FunctionType type = element.type; | 2682 FunctionType type = element.type; |
| 2600 | 2683 |
| 2601 // normal function (sync), vs (sync*, async, async*) | 2684 // normal function (sync), vs (sync*, async, async*) |
| 2602 var stdFn = !(element.isAsynchronous || element.isGenerator); | 2685 var stdFn = !(element.isAsynchronous || element.isGenerator); |
| 2603 var formals = _emitFormalParameterList(parameters, destructure: stdFn); | 2686 var formals = _emitFormalParameterList(parameters, destructure: stdFn); |
| 2604 var code = (stdFn) | 2687 var code = (stdFn) |
| 2605 ? _visit(body) | 2688 ? _visit(body) |
| 2606 : new JS.Block( | 2689 : new JS.Block( |
| 2607 [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); | 2690 [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); |
| 2608 var typeFormals = _emitTypeFormals(type.typeFormals); | 2691 var typeFormals = _emitTypeFormals(type.typeFormals); |
| 2692 | |
| 2609 var returnType = emitTypeRef(type.returnType); | 2693 var returnType = emitTypeRef(type.returnType); |
| 2610 if (type.typeFormals.isNotEmpty) { | 2694 if (type.typeFormals.isNotEmpty) { |
| 2611 code = new JS.Block(<JS.Statement>[ | 2695 var block = <JS.Statement>[ |
| 2612 new JS.Block(_typeTable.discharge(type.typeFormals)), | 2696 new JS.Block(_typeTable.discharge(type.typeFormals)) |
| 2613 code | 2697 ]; |
| 2614 ]); | 2698 |
| 2699 var covariantParams = _classProperties?.covariantParameters; | |
| 2700 if (covariantParams != null && | |
| 2701 type.typeFormals.any(covariantParams.contains)) { | |
| 2702 block.add(js.statement('#.checkBounds(#);', | |
| 2703 [_emitType(type), new JS.ArrayInitializer(typeFormals)])); | |
| 2704 } | |
| 2705 | |
| 2706 code = new JS.Block(block..add(code)); | |
| 2615 } | 2707 } |
| 2616 | 2708 |
| 2617 if (element.isOperator && element.name == '[]=' && formals.isNotEmpty) { | 2709 if (element.isOperator && element.name == '[]=' && formals.isNotEmpty) { |
| 2618 // []= methods need to return the value. We could also address this at | 2710 // []= methods need to return the value. We could also address this at |
| 2619 // call sites, but it's cleaner to instead transform the operator method. | 2711 // call sites, but it's cleaner to instead transform the operator method. |
| 2620 code = _alwaysReturnLastParameter(code, formals.last); | 2712 code = _alwaysReturnLastParameter(code, formals.last); |
| 2621 } | 2713 } |
| 2622 | 2714 |
| 2623 return _makeGenericFunction(new JS.Fun(formals, code, | 2715 return _makeGenericFunction(new JS.Fun(formals, code, |
| 2624 typeParams: typeFormals, returnType: returnType)); | 2716 typeParams: typeFormals, returnType: returnType)); |
| (...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3150 } | 3242 } |
| 3151 | 3243 |
| 3152 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions | 3244 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions |
| 3153 // (for example, x is IndexExpression) we evaluate those once. | 3245 // (for example, x is IndexExpression) we evaluate those once. |
| 3154 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 3246 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
| 3155 var lhs = _bindLeftHandSide(vars, left, context: context); | 3247 var lhs = _bindLeftHandSide(vars, left, context: context); |
| 3156 Expression inc = AstBuilder.binaryExpression(lhs, op, right) | 3248 Expression inc = AstBuilder.binaryExpression(lhs, op, right) |
| 3157 ..staticElement = element | 3249 ..staticElement = element |
| 3158 ..staticType = getStaticType(lhs); | 3250 ..staticType = getStaticType(lhs); |
| 3159 | 3251 |
| 3160 var castTo = getImplicitAssignmentCast(left); | 3252 var castTo = getImplicitOperationCast(left); |
| 3161 if (castTo != null) inc = CoercionReifier.castExpression(inc, castTo); | 3253 if (castTo != null) inc = CoercionReifier.castExpression(inc, castTo); |
| 3162 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 3254 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
| 3163 } | 3255 } |
| 3164 | 3256 |
| 3165 JS.Expression _emitSet(Expression left, Expression right) { | 3257 JS.Expression _emitSet(Expression left, Expression right) { |
| 3166 if (left is IndexExpression) { | 3258 if (left is IndexExpression) { |
| 3167 var target = _getTarget(left); | 3259 var target = _getTarget(left); |
| 3168 if (_useNativeJsIndexer(target.staticType)) { | 3260 if (_useNativeJsIndexer(target.staticType)) { |
| 3169 return js.call( | 3261 return js.call( |
| 3170 '#[#] = #', [_visit(target), _visit(left.index), _visit(right)]); | 3262 '#[#] = #', [_visit(target), _visit(left.index), _visit(right)]); |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3474 } else { | 3566 } else { |
| 3475 return _callHelper('#(#, #, #)', | 3567 return _callHelper('#(#, #, #)', |
| 3476 [_emitDynamicOperationName('dsend'), jsTarget, memberName, args]); | 3568 [_emitDynamicOperationName('dsend'), jsTarget, memberName, args]); |
| 3477 } | 3569 } |
| 3478 } | 3570 } |
| 3479 if (_isObjectMemberCall(target, name)) { | 3571 if (_isObjectMemberCall(target, name)) { |
| 3480 assert(typeArgs == null); // Object methods don't take type args. | 3572 assert(typeArgs == null); // Object methods don't take type args. |
| 3481 return _callHelper('#(#, #)', [name, jsTarget, args]); | 3573 return _callHelper('#(#, #)', [name, jsTarget, args]); |
| 3482 } | 3574 } |
| 3483 jsTarget = _emitTargetAccess(jsTarget, memberName, element); | 3575 jsTarget = _emitTargetAccess(jsTarget, memberName, element); |
| 3576 var castTo = getImplicitOperationCast(node.methodName); | |
| 3577 if (castTo != null) { | |
| 3578 jsTarget = js.call('#._check(#)', [_emitType(castTo), jsTarget]); | |
| 3579 } | |
| 3484 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 3580 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
| 3485 | |
| 3486 return new JS.Call(jsTarget, args); | 3581 return new JS.Call(jsTarget, args); |
| 3487 } | 3582 } |
| 3488 | 3583 |
| 3489 JS.Expression _emitDynamicInvoke( | 3584 JS.Expression _emitDynamicInvoke( |
| 3490 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { | 3585 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { |
| 3491 var typeArgs = _emitInvokeTypeArguments(node); | 3586 var typeArgs = _emitInvokeTypeArguments(node); |
| 3492 if (typeArgs != null) { | 3587 if (typeArgs != null) { |
| 3493 return _callHelper( | 3588 return _callHelper( |
| 3494 'dgcall(#, #, #)', [fn, new JS.ArrayInitializer(typeArgs), args]); | 3589 'dgcall(#, #, #)', [fn, new JS.ArrayInitializer(typeArgs), args]); |
| 3495 } else { | 3590 } else { |
| 3496 return _callHelper('dcall(#, #)', [fn, args]); | 3591 return _callHelper('dcall(#, #)', [fn, args]); |
| 3497 } | 3592 } |
| 3498 } | 3593 } |
| 3499 | 3594 |
| 3500 /// Emits a function call, to a top-level function, local function, or | 3595 /// Emits a function call, to a top-level function, local function, or |
| 3501 /// an expression. | 3596 /// an expression. |
| 3502 JS.Expression _emitFunctionCall(InvocationExpression node, | 3597 JS.Expression _emitFunctionCall(InvocationExpression node, |
| 3503 [Expression function]) { | 3598 [Expression function]) { |
| 3504 if (function == null) { | 3599 function ??= node.function; |
| 3505 function = node.function; | 3600 var castTo = getImplicitOperationCast(function); |
| 3601 if (castTo != null) { | |
| 3602 function = CoercionReifier.castExpression(function, castTo); | |
| 3506 } | 3603 } |
| 3604 | |
| 3507 var fn = _visit(function); | 3605 var fn = _visit(function); |
| 3508 var args = _emitArgumentList(node.argumentList); | 3606 var args = _emitArgumentList(node.argumentList); |
| 3509 if (isDynamicInvoke(function)) { | 3607 if (isDynamicInvoke(function)) { |
| 3510 return _emitDynamicInvoke(node, fn, args); | 3608 return _emitDynamicInvoke(node, fn, args); |
| 3511 } else { | 3609 } else { |
| 3512 return new JS.Call(_applyInvokeTypeArguments(fn, node), args); | 3610 return new JS.Call(_applyInvokeTypeArguments(fn, node), args); |
| 3513 } | 3611 } |
| 3514 } | 3612 } |
| 3515 | 3613 |
| 3516 JS.Expression _applyInvokeTypeArguments( | 3614 JS.Expression _applyInvokeTypeArguments( |
| (...skipping 1159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4676 // Desugar `obj?.name` as ((x) => x == null ? null : x.name)(obj) | 4774 // Desugar `obj?.name` as ((x) => x == null ? null : x.name)(obj) |
| 4677 var target = _getTarget(node); | 4775 var target = _getTarget(node); |
| 4678 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 4776 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
| 4679 var t = _bindValue(vars, 't', target, context: target); | 4777 var t = _bindValue(vars, 't', target, context: target); |
| 4680 return new JS.MetaLet(vars, [ | 4778 return new JS.MetaLet(vars, [ |
| 4681 js.call('# == null ? null : #', | 4779 js.call('# == null ? null : #', |
| 4682 [_visit(t), _visit(_stripNullAwareOp(node, t))]) | 4780 [_visit(t), _visit(_stripNullAwareOp(node, t))]) |
| 4683 ]); | 4781 ]); |
| 4684 } | 4782 } |
| 4685 | 4783 |
| 4686 static Token _getOperator(Expression node) { | |
| 4687 if (node is PropertyAccess) return node.operator; | |
| 4688 if (node is MethodInvocation) return node.operator; | |
| 4689 return null; | |
| 4690 } | |
| 4691 | |
| 4692 // TODO(jmesserly): this is dropping source location. | 4784 // TODO(jmesserly): this is dropping source location. |
| 4693 Expression _stripNullAwareOp(Expression node, Expression newTarget) { | 4785 Expression _stripNullAwareOp(Expression node, Expression newTarget) { |
| 4694 if (node is PropertyAccess) { | 4786 if (node is PropertyAccess) { |
| 4695 return AstBuilder.propertyAccess(newTarget, node.propertyName); | 4787 return AstBuilder.propertyAccess(newTarget, node.propertyName); |
| 4696 } else { | 4788 } else { |
| 4697 var invoke = node as MethodInvocation; | 4789 var invoke = node as MethodInvocation; |
| 4698 return AstBuilder.methodInvoke(newTarget, invoke.methodName, | 4790 return AstBuilder.methodInvoke(newTarget, invoke.methodName, |
| 4699 invoke.typeArguments, invoke.argumentList.arguments) | 4791 invoke.typeArguments, invoke.argumentList.arguments) |
| 4700 ..staticInvokeType = invoke.staticInvokeType; | 4792 ..staticInvokeType = invoke.staticInvokeType; |
| 4701 } | 4793 } |
| (...skipping 1110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5812 if (targetIdentifier.staticElement is! PrefixElement) return false; | 5904 if (targetIdentifier.staticElement is! PrefixElement) return false; |
| 5813 var prefix = targetIdentifier.staticElement as PrefixElement; | 5905 var prefix = targetIdentifier.staticElement as PrefixElement; |
| 5814 | 5906 |
| 5815 // The library the prefix is referring to must come from a deferred import. | 5907 // The library the prefix is referring to must come from a deferred import. |
| 5816 var containingLibrary = resolutionMap | 5908 var containingLibrary = resolutionMap |
| 5817 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 5909 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
| 5818 .library; | 5910 .library; |
| 5819 var imports = containingLibrary.getImportsWithPrefix(prefix); | 5911 var imports = containingLibrary.getImportsWithPrefix(prefix); |
| 5820 return imports.length == 1 && imports[0].isDeferred; | 5912 return imports.length == 1 && imports[0].isDeferred; |
| 5821 } | 5913 } |
| OLD | NEW |