OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:collection' show HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 import 'dart:math' show min, max; | 6 import 'dart:math' show min, max; |
7 | 7 |
8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
(...skipping 736 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
747 decl.name.name, | 747 decl.name.name, |
748 type: emitTypeRef(decl.element.type)), | 748 type: emitTypeRef(decl.element.type)), |
749 null)) | 749 null)) |
750 .toList(growable: false)); | 750 .toList(growable: false)); |
751 } | 751 } |
752 | 752 |
753 @override | 753 @override |
754 JS.Statement visitEnumDeclaration(EnumDeclaration node) { | 754 JS.Statement visitEnumDeclaration(EnumDeclaration node) { |
755 var element = node.element; | 755 var element = node.element; |
756 var type = element.type; | 756 var type = element.type; |
757 var name = js.string(type.name); | |
758 | 757 |
759 // Generate a class per section 13 of the spec. | 758 // Generate a class per section 13 of the spec. |
760 // TODO(vsm): Generate any accompanying metadata | 759 // TODO(vsm): Generate any accompanying metadata |
761 | 760 |
762 // Create constructor and initialize index | 761 // Create constructor and initialize index |
763 var constructor = new JS.Method( | 762 var constructor = new JS.Method(_propertyName('new'), |
764 name, js.call('function(index) { this.index = index; }') as JS.Fun); | 763 js.call('function(index) { this.index = index; }') as JS.Fun); |
765 var fields = new List<FieldElement>.from( | 764 var fields = new List<FieldElement>.from( |
766 element.fields.where((f) => f.type == type)); | 765 element.fields.where((f) => f.type == type)); |
767 | 766 |
768 // Create toString() method | 767 // Create toString() method |
769 var properties = new List<JS.Property>(); | 768 var properties = new List<JS.Property>(); |
770 for (var i = 0; i < fields.length; ++i) { | 769 for (var i = 0; i < fields.length; ++i) { |
771 properties.add(new JS.Property( | 770 properties.add(new JS.Property( |
772 js.number(i), js.string('${type.name}.${fields[i].name}'))); | 771 js.number(i), js.string('${type.name}.${fields[i].name}'))); |
773 } | 772 } |
774 var nameMap = new JS.ObjectInitializer(properties, multiline: true); | 773 var nameMap = new JS.ObjectInitializer(properties, multiline: true); |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
909 List<FieldDeclaration> fields, | 908 List<FieldDeclaration> fields, |
910 List<ClassElement> superclasses, | 909 List<ClassElement> superclasses, |
911 Map<FieldElement, JS.TemporaryId> virtualFields) { | 910 Map<FieldElement, JS.TemporaryId> virtualFields) { |
912 var element = node.element; | 911 var element = node.element; |
913 var type = element.type; | 912 var type = element.type; |
914 var isObject = type.isObject; | 913 var isObject = type.isObject; |
915 | 914 |
916 // Iff no constructor is specified for a class C, it implicitly has a | 915 // Iff no constructor is specified for a class C, it implicitly has a |
917 // default constructor `C() : super() {}`, unless C is class Object. | 916 // default constructor `C() : super() {}`, unless C is class Object. |
918 var jsMethods = <JS.Method>[]; | 917 var jsMethods = <JS.Method>[]; |
919 if (ctors.isEmpty && !isObject) { | 918 if (isObject) { |
| 919 // Implements Dart constructor behavior. |
| 920 // |
| 921 // Because of ES6 constructor restrictions (`this` is not available until |
| 922 // `super` is called), we cannot emit an actual ES6 `constructor` on our |
| 923 // classes and preserve the Dart initialization order. |
| 924 // |
| 925 // Instead we use the same trick as named constructors, and do them as |
| 926 // instance methods that perform initialization. |
| 927 // |
| 928 // Therefore, dart:core Object gets the one real `constructor` and |
| 929 // immediately bounces to the `new() { ... }` initializer, letting us |
| 930 // bypass the ES6 restrictions. |
| 931 // |
| 932 // TODO(jmesserly): we'll need to rethink this. |
| 933 // See <https://github.com/dart-lang/dev_compiler/issues/51>. |
| 934 // This level of indirection will hurt performance. |
| 935 jsMethods.add(new JS.Method( |
| 936 _propertyName('constructor'), |
| 937 js.call('function(...args) { return this.new.apply(this, args); }') |
| 938 as JS.Fun)); |
| 939 } else if (ctors.isEmpty) { |
920 jsMethods.add(_emitImplicitConstructor(node, fields, virtualFields)); | 940 jsMethods.add(_emitImplicitConstructor(node, fields, virtualFields)); |
921 } | 941 } |
922 | 942 |
923 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; | 943 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; |
924 | 944 |
925 bool hasIterator = false; | 945 bool hasIterator = false; |
926 for (var m in node.members) { | 946 for (var m in node.members) { |
927 if (m is ConstructorDeclaration) { | 947 if (m is ConstructorDeclaration) { |
928 jsMethods | 948 jsMethods |
929 .add(_emitConstructor(m, type, fields, virtualFields, isObject)); | 949 .add(_emitConstructor(m, type, fields, virtualFields, isObject)); |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1110 body.add( | 1130 body.add( |
1111 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); | 1131 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); |
1112 } | 1132 } |
1113 } | 1133 } |
1114 | 1134 |
1115 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, | 1135 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, |
1116 List<JS.Statement> body, JS.Expression className) { | 1136 List<JS.Statement> body, JS.Expression className) { |
1117 for (ConstructorDeclaration member in ctors) { | 1137 for (ConstructorDeclaration member in ctors) { |
1118 if (member.name != null && member.factoryKeyword == null) { | 1138 if (member.name != null && member.factoryKeyword == null) { |
1119 body.add(js.statement('dart.defineNamedConstructor(#, #);', | 1139 body.add(js.statement('dart.defineNamedConstructor(#, #);', |
1120 [className, _emitMemberName(member.name.name, isStatic: true)])); | 1140 [className, _constructorName(member.element)])); |
1121 } | 1141 } |
1122 } | 1142 } |
1123 } | 1143 } |
1124 | 1144 |
1125 /// Emits static fields for a class, and initialize them eagerly if possible, | 1145 /// Emits static fields for a class, and initialize them eagerly if possible, |
1126 /// otherwise define them as lazy properties. | 1146 /// otherwise define them as lazy properties. |
1127 void _emitStaticFields( | 1147 void _emitStaticFields( |
1128 List<FieldDeclaration> staticFields, | 1148 List<FieldDeclaration> staticFields, |
1129 Set<FieldElement> staticFieldOverrides, | 1149 Set<FieldElement> staticFieldOverrides, |
1130 ClassElement classElem, | 1150 ClassElement classElem, |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1313 JS.Method _emitImplicitConstructor( | 1333 JS.Method _emitImplicitConstructor( |
1314 ClassDeclaration node, | 1334 ClassDeclaration node, |
1315 List<FieldDeclaration> fields, | 1335 List<FieldDeclaration> fields, |
1316 Map<FieldElement, JS.TemporaryId> virtualFields) { | 1336 Map<FieldElement, JS.TemporaryId> virtualFields) { |
1317 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); | 1337 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
1318 | 1338 |
1319 // If we don't have a method body, skip this. | 1339 // If we don't have a method body, skip this. |
1320 var superCall = _superConstructorCall(node.element); | 1340 var superCall = _superConstructorCall(node.element); |
1321 if (fields.isEmpty && superCall == null) return null; | 1341 if (fields.isEmpty && superCall == null) return null; |
1322 | 1342 |
1323 dynamic body = _initializeFields(node, fields, virtualFields); | 1343 var initFields = _initializeFields(node, fields, virtualFields); |
| 1344 List<JS.Statement> body = [initFields]; |
1324 if (superCall != null) { | 1345 if (superCall != null) { |
1325 body = [ | 1346 body.add(superCall); |
1326 [body, superCall] | |
1327 ]; | |
1328 } | 1347 } |
1329 var name = _constructorName(node.element.unnamedConstructor); | 1348 var name = _constructorName(node.element.unnamedConstructor); |
1330 return annotate( | 1349 return annotate( |
1331 new JS.Method(name, js.call('function() { #; }', body) as JS.Fun), | 1350 new JS.Method(name, js.call('function() { #; }', [body]) as JS.Fun), |
1332 node, | 1351 node, |
1333 node.element); | 1352 node.element); |
1334 } | 1353 } |
1335 | 1354 |
1336 JS.Method _emitConstructor( | 1355 JS.Method _emitConstructor( |
1337 ConstructorDeclaration node, | 1356 ConstructorDeclaration node, |
1338 InterfaceType type, | 1357 InterfaceType type, |
1339 List<FieldDeclaration> fields, | 1358 List<FieldDeclaration> fields, |
1340 Map<FieldElement, JS.TemporaryId> virtualFields, | 1359 Map<FieldElement, JS.TemporaryId> virtualFields, |
1341 bool isObject) { | 1360 bool isObject) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1375 var body = <JS.Statement>[]; | 1394 var body = <JS.Statement>[]; |
1376 var init = _emitArgumentInitializers(node, constructor: true); | 1395 var init = _emitArgumentInitializers(node, constructor: true); |
1377 if (init != null) body.add(init); | 1396 if (init != null) body.add(init); |
1378 body.add(_visit(node.body)); | 1397 body.add(_visit(node.body)); |
1379 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType); | 1398 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType); |
1380 return annotate( | 1399 return annotate( |
1381 new JS.Method(name, fun, isStatic: true), node, node.element); | 1400 new JS.Method(name, fun, isStatic: true), node, node.element); |
1382 } | 1401 } |
1383 | 1402 |
1384 // Code generation for Object's constructor. | 1403 // Code generation for Object's constructor. |
1385 JS.Block body; | 1404 var savedFunction = _currentFunction; |
1386 if (isObject && | 1405 _currentFunction = node.body; |
1387 node.body is EmptyFunctionBody && | 1406 var body = _emitConstructorBody(node, fields, virtualFields); |
1388 node.constKeyword != null && | 1407 _currentFunction = savedFunction; |
1389 node.name == null) { | |
1390 // Implements Dart constructor behavior. Because of V8 `super` | |
1391 // [constructor restrictions] | |
1392 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) | |
1393 // we cannot currently emit actual ES6 constructors with super calls. | |
1394 // Instead we use the same trick as named constructors, and do them as | |
1395 // instance methods that perform initialization. | |
1396 // TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8 | |
1397 // settles. See <https://github.com/dart-lang/dev_compiler/issues/51>. | |
1398 // Performance of this pattern is likely to be bad. | |
1399 name = _propertyName('constructor'); | |
1400 // Mark the parameter as no-rename. | |
1401 body = js.statement('''{ | |
1402 // Get the class name for this instance. | |
1403 let name = this.constructor.name; | |
1404 // Call the default constructor. | |
1405 let result = void 0; | |
1406 if (name in this) result = this[name].apply(this, arguments); | |
1407 return result === void 0 ? this : result; | |
1408 }''') as JS.Block; | |
1409 } else { | |
1410 var savedFunction = _currentFunction; | |
1411 _currentFunction = node.body; | |
1412 body = _emitConstructorBody(node, fields, virtualFields); | |
1413 _currentFunction = savedFunction; | |
1414 } | |
1415 | 1408 |
1416 // We generate constructors as initializer methods in the class; | 1409 // We generate constructors as initializer methods in the class; |
1417 // this allows use of `super` for instance methods/properties. | 1410 // this allows use of `super` for instance methods/properties. |
1418 // It also avoids V8 restrictions on `super` in default constructors. | 1411 // It also avoids V8 restrictions on `super` in default constructors. |
1419 return annotate( | 1412 return annotate( |
1420 new JS.Method(name, new JS.Fun(params, body, returnType: returnType)), | 1413 new JS.Method(name, new JS.Fun(params, body, returnType: returnType)), |
1421 node, | 1414 node, |
1422 node.element); | 1415 node.element); |
1423 } | 1416 } |
1424 | 1417 |
1425 JS.Expression _constructorName(ConstructorElement ctor) { | 1418 JS.Expression _constructorName(ConstructorElement ctor) { |
1426 var name = ctor.name; | 1419 var name = ctor.name; |
1427 if (name != '') { | 1420 if (name == '') { |
1428 return _emitMemberName(name, isStatic: true); | 1421 // Default constructors (factory or not) use `new` as their name. |
| 1422 return _propertyName('new'); |
1429 } | 1423 } |
1430 | 1424 return _emitMemberName(name, isStatic: true); |
1431 // Factory default constructors use `new` as their name, for readability | |
1432 // Other default constructors use the class name, as they aren't called | |
1433 // from call sites, but rather from Object's constructor. | |
1434 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning | |
1435 // up constructors to integrate more closely with ES6. | |
1436 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); | |
1437 } | 1425 } |
1438 | 1426 |
1439 JS.Block _emitConstructorBody( | 1427 JS.Block _emitConstructorBody( |
1440 ConstructorDeclaration node, | 1428 ConstructorDeclaration node, |
1441 List<FieldDeclaration> fields, | 1429 List<FieldDeclaration> fields, |
1442 Map<FieldElement, JS.TemporaryId> virtualFields) { | 1430 Map<FieldElement, JS.TemporaryId> virtualFields) { |
1443 var body = <JS.Statement>[]; | 1431 var body = <JS.Statement>[]; |
1444 ClassDeclaration cls = node.parent; | 1432 ClassDeclaration cls = node.parent; |
1445 | 1433 |
1446 // Generate optional/named argument value assignment. These can not have | 1434 // Generate optional/named argument value assignment. These can not have |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1480 var jsSuper = _superConstructorCall(cls.element, superCall); | 1468 var jsSuper = _superConstructorCall(cls.element, superCall); |
1481 if (jsSuper != null) body.add(jsSuper); | 1469 if (jsSuper != null) body.add(jsSuper); |
1482 | 1470 |
1483 body.add(_visit(node.body)); | 1471 body.add(_visit(node.body)); |
1484 return new JS.Block(body)..sourceInformation = node; | 1472 return new JS.Block(body)..sourceInformation = node; |
1485 } | 1473 } |
1486 | 1474 |
1487 @override | 1475 @override |
1488 JS.Statement visitRedirectingConstructorInvocation( | 1476 JS.Statement visitRedirectingConstructorInvocation( |
1489 RedirectingConstructorInvocation node) { | 1477 RedirectingConstructorInvocation node) { |
1490 var name = _constructorName(node.staticElement); | 1478 var ctor = node.staticElement; |
1491 return js.statement('this.#(#);', [name, _visit(node.argumentList)]); | 1479 var cls = ctor.enclosingElement as ClassElement; |
| 1480 // We can't dispatch to the constructor with `this.new` as that might hit a |
| 1481 // derived class constructor with the same name. |
| 1482 return js.statement('#.prototype.#.call(this, #);', [ |
| 1483 new JS.Identifier(cls.name), |
| 1484 _constructorName(ctor), |
| 1485 _visit(node.argumentList) |
| 1486 ]); |
1492 } | 1487 } |
1493 | 1488 |
1494 JS.Statement _superConstructorCall(ClassElement element, | 1489 JS.Statement _superConstructorCall(ClassElement element, |
1495 [SuperConstructorInvocation node]) { | 1490 [SuperConstructorInvocation node]) { |
| 1491 if (element.supertype == null) { |
| 1492 assert(element.type.isObject || options.unsafeForceCompile); |
| 1493 return null; |
| 1494 } |
| 1495 |
1496 ConstructorElement superCtor; | 1496 ConstructorElement superCtor; |
1497 if (node != null) { | 1497 if (node != null) { |
1498 superCtor = node.staticElement; | 1498 superCtor = node.staticElement; |
1499 } else { | 1499 } else { |
1500 // Get the supertype's unnamed constructor. | 1500 // Get the supertype's unnamed constructor. |
1501 superCtor = element.supertype.element.unnamedConstructor; | 1501 superCtor = element.supertype.element.unnamedConstructor; |
1502 if (superCtor == null) { | |
1503 // This will only happen if the code has errors: | |
1504 // we're trying to generate an implicit constructor for a type where | |
1505 // we don't have a default constructor in the supertype. | |
1506 assert(options.unsafeForceCompile); | |
1507 return null; | |
1508 } | |
1509 } | 1502 } |
1510 | 1503 |
1511 if (superCtor == null) { | 1504 if (superCtor == null) { |
1512 print('Error generating: ${element.displayName}'); | 1505 // This will only happen if the code has errors: |
| 1506 // we're trying to generate an implicit constructor for a type where |
| 1507 // we don't have a default constructor in the supertype. |
| 1508 assert(options.unsafeForceCompile); |
| 1509 return null; |
1513 } | 1510 } |
| 1511 |
1514 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { | 1512 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { |
1515 return null; | 1513 return null; |
1516 } | 1514 } |
1517 | 1515 |
1518 var name = _constructorName(superCtor); | 1516 var name = _constructorName(superCtor); |
1519 var args = node != null ? _visit(node.argumentList) : []; | 1517 var args = node != null ? _visit(node.argumentList) : []; |
1520 return annotate(js.statement('super.#(#);', [name, args]), node); | 1518 return annotate(js.statement('super.#(#);', [name, args]), node); |
1521 } | 1519 } |
1522 | 1520 |
1523 bool _shouldCallUnnamedSuperCtor(ClassElement e) { | 1521 bool _shouldCallUnnamedSuperCtor(ClassElement e) { |
(...skipping 1540 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3064 | 3062 |
3065 JS.Expression _emitInstanceCreationExpression( | 3063 JS.Expression _emitInstanceCreationExpression( |
3066 ConstructorElement element, | 3064 ConstructorElement element, |
3067 DartType type, | 3065 DartType type, |
3068 SimpleIdentifier name, | 3066 SimpleIdentifier name, |
3069 ArgumentList argumentList, | 3067 ArgumentList argumentList, |
3070 bool isConst) { | 3068 bool isConst) { |
3071 JS.Expression emitNew() { | 3069 JS.Expression emitNew() { |
3072 JS.Expression ctor; | 3070 JS.Expression ctor; |
3073 bool isFactory = false; | 3071 bool isFactory = false; |
3074 // var element = node.staticElement; | |
3075 if (element == null) { | 3072 if (element == null) { |
3076 // TODO(jmesserly): this only happens if we had a static error. | 3073 // TODO(jmesserly): this only happens if we had a static error. |
3077 // Should we generate a throw instead? | 3074 // Should we generate a throw instead? |
3078 ctor = _emitType(type); | 3075 ctor = _emitType(type); |
3079 if (name != null) { | 3076 if (name != null) { |
3080 ctor = new JS.PropertyAccess(ctor, _propertyName(name.name)); | 3077 ctor = new JS.PropertyAccess(ctor, _propertyName(name.name)); |
3081 } | 3078 } |
3082 } else { | 3079 } else { |
3083 ctor = _emitConstructorName(element, type, name); | 3080 ctor = _emitConstructorName(element, type, name); |
3084 isFactory = element.isFactory; | 3081 isFactory = element.isFactory; |
(...skipping 1412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4497 } | 4494 } |
4498 | 4495 |
4499 bool isLibraryPrefix(Expression node) => | 4496 bool isLibraryPrefix(Expression node) => |
4500 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4497 node is SimpleIdentifier && node.staticElement is PrefixElement; |
4501 | 4498 |
4502 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4499 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
4503 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4500 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
4504 | 4501 |
4505 bool _isDartRuntime(LibraryElement l) => | 4502 bool _isDartRuntime(LibraryElement l) => |
4506 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4503 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |