Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(362)

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

Issue 2954523002: fix #27259, implement covariance checking for strong mode and DDC (Closed)
Patch Set: rebase Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698