Chromium Code Reviews| Index: lib/src/codegen/js_codegen.dart |
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
| index f8f50a54fcfa4652f7e44c5b9cc6968b7b156b75..8561ee487ca0006daec7282a84e29d0d2e875802 100644 |
| --- a/lib/src/codegen/js_codegen.dart |
| +++ b/lib/src/codegen/js_codegen.dart |
| @@ -467,13 +467,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| var element = node.element; |
| var type = element.type; |
| var isObject = type.isObject; |
| - var name = node.name.name; |
| // 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) { |
| - jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
| + jsMethods.add(_emitImplicitConstructor(node, fields)); |
| } |
| bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null; |
| @@ -481,7 +480,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| bool hasIterator = false; |
| for (var m in node.members) { |
| if (m is ConstructorDeclaration) { |
| - jsMethods.add(_emitConstructor(m, name, fields, isObject)); |
| + jsMethods.add(_emitConstructor(m, type, fields, isObject)); |
| } else if (m is MethodDeclaration) { |
| jsMethods.add(_emitMethodDeclaration(type, m)); |
| @@ -564,7 +563,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| // Named constructors |
| for (ConstructorDeclaration member in ctors) { |
| - if (member.name != null) { |
| + if (member.name != null && member.factoryKeyword == null) { |
| body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
| name, |
| _emitMemberName(member.name.name, isStatic: true) |
| @@ -611,7 +610,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| } |
| var tCtors = []; |
| for (ConstructorDeclaration node in ctors) { |
| - var memberName = _constructorName(classElem.name, node.name); |
| + var memberName = _constructorName(node.element); |
| var element = node.element; |
| var parts = |
| _emitFunctionTypeParts(element.type, dynamicIsBottom: false); |
| @@ -657,24 +656,50 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| /// Generates the implicit default constructor for class C of the form |
| /// `C() : super() {}`. |
| JS.Method _emitImplicitConstructor( |
| - ClassDeclaration node, String name, List<FieldDeclaration> fields) { |
| + ClassDeclaration node, List<FieldDeclaration> fields) { |
| assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
| // If we don't have a method body, skip this. |
| - var superCall = _superConstructorCall(node); |
| + var superCall = _superConstructorCall(node.element); |
| if (fields.isEmpty && superCall == null) return null; |
| dynamic body = _initializeFields(node, fields); |
| if (superCall != null) body = [[body, superCall]]; |
| - return new JS.Method( |
| - _propertyName(name), js.call('function() { #; }', body)); |
| + var name = _constructorName(node.element.unnamedConstructor); |
| + return new JS.Method(name, js.call('function() { #; }', body)); |
| } |
| - JS.Method _emitConstructor(ConstructorDeclaration node, String className, |
| + JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type, |
| List<FieldDeclaration> fields, bool isObject) { |
| if (_externalOrNative(node)) return null; |
| - var name = _constructorName(className, node.name); |
| + var name = _constructorName(node.element); |
| + |
| + // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
|
Jennifer Messerly
2015/05/29 19:54:53
this code moved from _emitConstructorBody, which w
|
| + var redirect = node.redirectedConstructor; |
| + if (redirect != null) { |
| + var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; |
| + // Pass along all arguments verbatim, and let the callee handle them. |
| + // TODO(jmesserly): we'll need something different once we have |
| + // rest/spread support, but this should work for now. |
| + var params = _visit(node.parameters); |
| + var fun = js.call('function(#) { return $newKeyword #(#); }', [ |
| + params, |
| + _visit(redirect), |
| + params, |
| + ]); |
| + return new JS.Method(name, fun, isStatic: true)..sourceInformation = node; |
| + } |
| + |
| + // Factory constructors are essentially static methods. |
| + if (node.factoryKeyword != null) { |
| + var body = <JS.Statement>[]; |
| + var init = _emitArgumentInitializers(node, constructor: true); |
| + if (init != null) body.add(init); |
| + body.add(_visit(node.body)); |
| + var fun = new JS.Fun(_visit(node.parameters), new JS.Block(body)); |
| + return new JS.Method(name, fun, isStatic: true)..sourceInformation = node; |
| + } |
| // Code generation for Object's constructor. |
| JS.Block body; |
| @@ -714,21 +739,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| ..sourceInformation = node; |
| } |
| - JS.Expression _constructorName(String className, SimpleIdentifier name) { |
| - if (name == null) return _propertyName(className); |
| - return _emitMemberName(name.name, isStatic: true); |
| + JS.Expression _constructorName(ConstructorElement ctor) { |
| + var name = ctor.name; |
| + if (name != '') { |
| + return _emitMemberName(name, isStatic: true); |
| + } |
| + |
| + // 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); |
| } |
| JS.Block _emitConstructorBody( |
| ConstructorDeclaration node, List<FieldDeclaration> fields) { |
| - // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
| - if (node.redirectedConstructor != null) { |
| - return js.statement('{ return new #(#); }', [ |
| - _visit(node.redirectedConstructor), |
| - _visit(node.parameters) |
| - ]); |
| - } |
| - |
| var body = <JS.Statement>[]; |
| // Generate optional/named argument value assignment. These can not have |
| @@ -752,23 +778,20 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| return new JS.Block(body); |
| } |
| - // Initializers only run for non-factory constructors. |
| - if (node.factoryKeyword == null) { |
| - // Generate field initializers. |
| - // These are expanded into each non-redirecting constructor. |
| - // In the future we may want to create an initializer function if we have |
| - // multiple constructors, but it needs to be balanced against readability. |
| - body.add(_initializeFields(node.parent, fields, node)); |
| + // Generate field initializers. |
| + // These are expanded into each non-redirecting constructor. |
| + // In the future we may want to create an initializer function if we have |
| + // multiple constructors, but it needs to be balanced against readability. |
| + body.add(_initializeFields(node.parent, fields, node)); |
| - var superCall = node.initializers.firstWhere( |
| - (i) => i is SuperConstructorInvocation, orElse: () => null); |
| + var superCall = node.initializers.firstWhere( |
| + (i) => i is SuperConstructorInvocation, orElse: () => null); |
| - // If no superinitializer is provided, an implicit superinitializer of the |
| - // form `super()` is added at the end of the initializer list, unless the |
| - // enclosing class is class Object. |
| - var jsSuper = _superConstructorCall(node.parent, superCall); |
| - if (jsSuper != null) body.add(jsSuper); |
| - } |
| + // If no superinitializer is provided, an implicit superinitializer of the |
| + // form `super()` is added at the end of the initializer list, unless the |
| + // enclosing class is class Object. |
| + var jsSuper = _superConstructorCall(cls.element, superCall); |
| + if (jsSuper != null) body.add(jsSuper); |
| body.add(_visit(node.body)); |
| return new JS.Block(body)..sourceInformation = node; |
| @@ -777,25 +800,33 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| @override |
| JS.Statement visitRedirectingConstructorInvocation( |
| RedirectingConstructorInvocation node) { |
| - ClassDeclaration classDecl = node.parent.parent; |
| - var className = classDecl.name.name; |
| - |
| - var name = _constructorName(className, node.constructorName); |
| + var name = _constructorName(node.staticElement); |
| return js.statement('this.#(#);', [name, _visit(node.argumentList)]); |
| } |
| - JS.Statement _superConstructorCall(ClassDeclaration clazz, |
| + JS.Statement _superConstructorCall(ClassElement element, |
| [SuperConstructorInvocation node]) { |
| - var superCtorName = node != null ? node.constructorName : null; |
| - var element = clazz.element; |
| - if (superCtorName == null && !_shouldCallUnnamedSuperCtor(element)) { |
| - 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.forceCompile); |
| + return null; |
| + } |
| } |
| - var supertypeName = element.supertype.name; |
| - var name = _constructorName(supertypeName, superCtorName); |
| + if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { |
| + return null; |
| + } |
| + var name = _constructorName(superCtor); |
| var args = node != null ? _visit(node.argumentList) : []; |
| return js.statement('super.#(#);', [name, args])..sourceInformation = node; |
| } |
| @@ -1636,17 +1667,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| @override |
| visitConstructorName(ConstructorName node) { |
| var typeName = _visit(node.type); |
| - if (node.name != null) { |
| - return js.call( |
| - '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); |
| + if (node.name != null || node.staticElement.isFactory) { |
| + var namedCtor = _constructorName(node.staticElement); |
| + return new JS.PropertyAccess(typeName, namedCtor); |
| } |
| return typeName; |
| } |
| @override |
| visitInstanceCreationExpression(InstanceCreationExpression node) { |
| - emitNew() => js.call( |
| - 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
| + emitNew() { |
| + var ctor = _visit(node.constructorName); |
| + var args = _visit(node.argumentList); |
| + var isFactory = node.staticElement.isFactory; |
| + return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args); |
| + } |
| if (node.isConst) return _emitConst(node, emitNew); |
| return emitNew(); |
| } |