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

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

Issue 1965213003: simplify constructors, fixes #564 (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 7 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
« no previous file with comments | « lib/runtime/dart_sdk.js ('k') | test/browser/language_tests.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // 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
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
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
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
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
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
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
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
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';
OLDNEW
« no previous file with comments | « lib/runtime/dart_sdk.js ('k') | test/browser/language_tests.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698