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 |