Index: lib/src/compiler/code_generator.dart |
diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
index 8eea4be80d5297944d1d943d89ab9ad7a31a2008..05d603f8de8c428c91f543740d95d541006ad1c7 100644 |
--- a/lib/src/compiler/code_generator.dart |
+++ b/lib/src/compiler/code_generator.dart |
@@ -754,14 +754,13 @@ class CodeGenerator extends GeneralizingAstVisitor |
JS.Statement visitEnumDeclaration(EnumDeclaration node) { |
var element = node.element; |
var type = element.type; |
- var name = js.string(type.name); |
// Generate a class per section 13 of the spec. |
// TODO(vsm): Generate any accompanying metadata |
// Create constructor and initialize index |
- var constructor = new JS.Method( |
- name, js.call('function(index) { this.index = index; }') as JS.Fun); |
+ var constructor = new JS.Method(_propertyName('new'), |
+ js.call('function(index) { this.index = index; }') as JS.Fun); |
var fields = new List<FieldElement>.from( |
element.fields.where((f) => f.type == type)); |
@@ -916,7 +915,28 @@ class CodeGenerator extends GeneralizingAstVisitor |
// Iff no constructor is specified for a class C, it implicitly has a |
// default constructor `C() : super() {}`, unless C is class Object. |
var jsMethods = <JS.Method>[]; |
- if (ctors.isEmpty && !isObject) { |
+ if (isObject) { |
+ // Implements Dart constructor behavior. |
+ // |
+ // Because of ES6 constructor restrictions (`this` is not available until |
+ // `super` is called), we cannot emit an actual ES6 `constructor` on our |
+ // classes and preserve the Dart initialization order. |
+ // |
+ // Instead we use the same trick as named constructors, and do them as |
+ // instance methods that perform initialization. |
+ // |
+ // Therefore, dart:core Object gets the one real `constructor` and |
+ // immediately bounces to the `new() { ... }` initializer, letting us |
+ // bypass the ES6 restrictions. |
+ // |
+ // TODO(jmesserly): we'll need to rethink this. |
+ // See <https://github.com/dart-lang/dev_compiler/issues/51>. |
+ // This level of indirection will hurt performance. |
+ jsMethods.add(new JS.Method( |
+ _propertyName('constructor'), |
+ js.call('function(...args) { return this.new.apply(this, args); }') |
+ as JS.Fun)); |
+ } else if (ctors.isEmpty) { |
jsMethods.add(_emitImplicitConstructor(node, fields, virtualFields)); |
} |
@@ -1117,7 +1137,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
for (ConstructorDeclaration member in ctors) { |
if (member.name != null && member.factoryKeyword == null) { |
body.add(js.statement('dart.defineNamedConstructor(#, #);', |
- [className, _emitMemberName(member.name.name, isStatic: true)])); |
+ [className, _constructorName(member.element)])); |
} |
} |
} |
@@ -1320,15 +1340,14 @@ class CodeGenerator extends GeneralizingAstVisitor |
var superCall = _superConstructorCall(node.element); |
if (fields.isEmpty && superCall == null) return null; |
- dynamic body = _initializeFields(node, fields, virtualFields); |
+ var initFields = _initializeFields(node, fields, virtualFields); |
+ List<JS.Statement> body = [initFields]; |
if (superCall != null) { |
- body = [ |
- [body, superCall] |
- ]; |
+ body.add(superCall); |
} |
var name = _constructorName(node.element.unnamedConstructor); |
return annotate( |
- new JS.Method(name, js.call('function() { #; }', body) as JS.Fun), |
+ new JS.Method(name, js.call('function() { #; }', [body]) as JS.Fun), |
node, |
node.element); |
} |
@@ -1382,36 +1401,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
// Code generation for Object's constructor. |
- JS.Block body; |
- if (isObject && |
- node.body is EmptyFunctionBody && |
- node.constKeyword != null && |
- node.name == null) { |
- // Implements Dart constructor behavior. Because of V8 `super` |
- // [constructor restrictions] |
- // (https://code.google.com/p/v8/issues/detail?id=3330#c65) |
- // we cannot currently emit actual ES6 constructors with super calls. |
- // Instead we use the same trick as named constructors, and do them as |
- // instance methods that perform initialization. |
- // TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8 |
- // settles. See <https://github.com/dart-lang/dev_compiler/issues/51>. |
- // Performance of this pattern is likely to be bad. |
- name = _propertyName('constructor'); |
- // Mark the parameter as no-rename. |
- body = js.statement('''{ |
- // Get the class name for this instance. |
- let name = this.constructor.name; |
- // Call the default constructor. |
- let result = void 0; |
- if (name in this) result = this[name].apply(this, arguments); |
- return result === void 0 ? this : result; |
- }''') as JS.Block; |
- } else { |
- var savedFunction = _currentFunction; |
- _currentFunction = node.body; |
- body = _emitConstructorBody(node, fields, virtualFields); |
- _currentFunction = savedFunction; |
- } |
+ var savedFunction = _currentFunction; |
+ _currentFunction = node.body; |
+ var body = _emitConstructorBody(node, fields, virtualFields); |
+ _currentFunction = savedFunction; |
// We generate constructors as initializer methods in the class; |
// this allows use of `super` for instance methods/properties. |
@@ -1424,16 +1417,11 @@ class CodeGenerator extends GeneralizingAstVisitor |
JS.Expression _constructorName(ConstructorElement ctor) { |
var name = ctor.name; |
- if (name != '') { |
- return _emitMemberName(name, isStatic: true); |
+ if (name == '') { |
+ // Default constructors (factory or not) use `new` as their name. |
+ return _propertyName('new'); |
} |
- |
- // Factory default constructors use `new` as their name, for readability |
- // Other default constructors use the class name, as they aren't called |
- // from call sites, but rather from Object's constructor. |
- // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning |
- // up constructors to integrate more closely with ES6. |
- return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); |
+ return _emitMemberName(name, isStatic: true); |
} |
JS.Block _emitConstructorBody( |
@@ -1487,30 +1475,40 @@ class CodeGenerator extends GeneralizingAstVisitor |
@override |
JS.Statement visitRedirectingConstructorInvocation( |
RedirectingConstructorInvocation node) { |
- var name = _constructorName(node.staticElement); |
- return js.statement('this.#(#);', [name, _visit(node.argumentList)]); |
+ var ctor = node.staticElement; |
+ var cls = ctor.enclosingElement as ClassElement; |
+ // We can't dispatch to the constructor with `this.new` as that might hit a |
+ // derived class constructor with the same name. |
+ return js.statement('#.prototype.#.call(this, #);', [ |
+ new JS.Identifier(cls.name), |
+ _constructorName(ctor), |
+ _visit(node.argumentList) |
+ ]); |
} |
JS.Statement _superConstructorCall(ClassElement element, |
[SuperConstructorInvocation node]) { |
+ if (element.supertype == null) { |
+ assert(element.type.isObject || options.unsafeForceCompile); |
+ return null; |
+ } |
+ |
ConstructorElement superCtor; |
if (node != null) { |
superCtor = node.staticElement; |
} else { |
// Get the supertype's unnamed constructor. |
superCtor = element.supertype.element.unnamedConstructor; |
- if (superCtor == null) { |
- // This will only happen if the code has errors: |
- // we're trying to generate an implicit constructor for a type where |
- // we don't have a default constructor in the supertype. |
- assert(options.unsafeForceCompile); |
- return null; |
- } |
} |
if (superCtor == null) { |
- print('Error generating: ${element.displayName}'); |
+ // This will only happen if the code has errors: |
+ // we're trying to generate an implicit constructor for a type where |
+ // we don't have a default constructor in the supertype. |
+ assert(options.unsafeForceCompile); |
+ return null; |
} |
+ |
if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { |
return null; |
} |
@@ -3071,7 +3069,6 @@ class CodeGenerator extends GeneralizingAstVisitor |
JS.Expression emitNew() { |
JS.Expression ctor; |
bool isFactory = false; |
- // var element = node.staticElement; |
if (element == null) { |
// TODO(jmesserly): this only happens if we had a static error. |
// Should we generate a throw instead? |