| Index: pkg/dev_compiler/lib/src/compiler/code_generator.dart
 | 
| diff --git a/pkg/dev_compiler/lib/src/compiler/code_generator.dart b/pkg/dev_compiler/lib/src/compiler/code_generator.dart
 | 
| index c52c77cffd8e3465c146b7d23ad4c4b93164939b..e9e17f69c49d497e846e5434f5d2318493cf1c7d 100644
 | 
| --- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart
 | 
| +++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart
 | 
| @@ -799,69 +799,49 @@ class CodeGenerator extends Object
 | 
|  
 | 
|    @override
 | 
|    JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
 | 
| -    ClassElement element = node.element;
 | 
| -    var supertype = element.supertype;
 | 
| -
 | 
| -    // Forward all generative constructors from the base class.
 | 
| -    var methods = <JS.Method>[];
 | 
| -    if (!supertype.isObject) {
 | 
| -      for (var ctor in element.constructors) {
 | 
| -        var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library);
 | 
| -        // TODO(jmesserly): this avoids spread args for perf. Revisit.
 | 
| -        var jsParams = <JS.Identifier>[];
 | 
| -        for (var p in ctor.parameters) {
 | 
| -          if (p.parameterKind != ParameterKind.NAMED) {
 | 
| -            jsParams.add(new JS.Identifier(p.name));
 | 
| -          } else {
 | 
| -            jsParams.add(new JS.TemporaryId('namedArgs'));
 | 
| -            break;
 | 
| -          }
 | 
| -        }
 | 
| -        var fun = js.call('function(#) { super.#(#); }',
 | 
| -            [jsParams, _constructorName(parentCtor), jsParams]) as JS.Fun;
 | 
| -        methods.add(new JS.Method(_constructorName(ctor), fun));
 | 
| -      }
 | 
| -    }
 | 
| +    ClassElement classElem = node.element;
 | 
| +    var supertype = classElem.supertype;
 | 
|  
 | 
| -    var typeFormals = element.typeParameters;
 | 
| +    var typeFormals = classElem.typeParameters;
 | 
|      var isGeneric = typeFormals.isNotEmpty;
 | 
| -    var className = isGeneric ? element.name : _emitTopLevelName(element);
 | 
| -    JS.Statement declareInterfaces(JS.Statement decl) {
 | 
| -      if (element.interfaces.isNotEmpty) {
 | 
| -        var body = [decl]..add(js.statement('#[#.implements] = () => #;', [
 | 
| -            className,
 | 
| -            _runtimeModule,
 | 
| -            new JS.ArrayInitializer(
 | 
| -                new List<JS.Expression>.from(element.interfaces.map(_emitType)))
 | 
| -          ]));
 | 
| -        decl = _statement(body);
 | 
| -      }
 | 
| -      return decl;
 | 
| -    }
 | 
|  
 | 
| -    if (supertype.isObject && element.mixins.length == 1) {
 | 
| -      // Special case where supertype is Object, and we mixin a single class.
 | 
| -      // The resulting 'class' is a mixable class in this case.
 | 
| -      var classExpr = _emitClassHeritage(element);
 | 
| -      if (isGeneric) {
 | 
| -        var classStmt = js.statement('const # = #;', [className, classExpr]);
 | 
| -        return _defineClassTypeArguments(
 | 
| -            element, typeFormals, declareInterfaces(classStmt));
 | 
| +    // Special case where supertype is Object, and we mixin a single class.
 | 
| +    // The resulting 'class' is a mixable class in this case.
 | 
| +    bool isMixinAlias = supertype.isObject && classElem.mixins.length == 1;
 | 
| +
 | 
| +    var classExpr = isMixinAlias
 | 
| +        ? _emitClassHeritage(classElem)
 | 
| +        : _emitClassExpression(classElem, []);
 | 
| +    var className = isGeneric
 | 
| +        ? new JS.Identifier(classElem.name)
 | 
| +        : _emitTopLevelName(classElem);
 | 
| +    var block = <JS.Statement>[];
 | 
| +
 | 
| +    if (isGeneric) {
 | 
| +      if (isMixinAlias) {
 | 
| +        block.add(js.statement('const # = #;', [className, classExpr]));
 | 
|        } else {
 | 
| -        var classStmt = js.statement('# = #;', [className, classExpr]);
 | 
| -        return declareInterfaces(classStmt);
 | 
| +        block.add(new JS.ClassDeclaration(classExpr));
 | 
|        }
 | 
| +    } else {
 | 
| +      block.add(js.statement('# = #;', [className, classExpr]));
 | 
| +    }
 | 
| +
 | 
| +    if (!isMixinAlias) _defineConstructors(classElem, className, [], block, []);
 | 
| +
 | 
| +    if (classElem.interfaces.isNotEmpty) {
 | 
| +      block.add(js.statement('#[#.implements] = () => #;', [
 | 
| +        className,
 | 
| +        _runtimeModule,
 | 
| +        new JS.ArrayInitializer(classElem.interfaces.map(_emitType).toList())
 | 
| +      ]));
 | 
|      }
 | 
|  
 | 
| -    var classExpr = _emitClassExpression(element, methods);
 | 
|      if (isGeneric) {
 | 
| -      var classStmt = new JS.ClassDeclaration(classExpr);
 | 
|        return _defineClassTypeArguments(
 | 
| -          element, typeFormals, declareInterfaces(classStmt));
 | 
| -    } else {
 | 
| -      var classStmt = js.statement('# = #;', [className, classExpr]);
 | 
| -      return declareInterfaces(classStmt);
 | 
| +          classElem, typeFormals, _statement(block));
 | 
|      }
 | 
| +    return _statement(block);
 | 
|    }
 | 
|  
 | 
|    JS.Statement _emitJsType(Element e) {
 | 
| @@ -892,9 +872,6 @@ class CodeGenerator extends Object
 | 
|      var staticFields = <FieldDeclaration>[];
 | 
|      var methods = <MethodDeclaration>[];
 | 
|  
 | 
| -    // True if a "call" method or getter exists directly on this class.
 | 
| -    // If so, we need to install a Function prototype.
 | 
| -    bool isCallable = false;
 | 
|      for (var member in node.members) {
 | 
|        if (member is ConstructorDeclaration) {
 | 
|          ctors.add(member);
 | 
| @@ -903,35 +880,9 @@ class CodeGenerator extends Object
 | 
|          (member.isStatic ? staticFields : fields).add(member);
 | 
|        } else if (member is MethodDeclaration) {
 | 
|          methods.add(member);
 | 
| -        if (member.name.name == 'call' && !member.isSetter) {
 | 
| -          //
 | 
| -          // Make sure "call" has a statically known function type:
 | 
| -          //
 | 
| -          // - if it's a method, then it does because all methods do,
 | 
| -          // - if it's a getter, check the return type.
 | 
| -          //
 | 
| -          // Other cases like a getter returning dynamic/Object/Function will be
 | 
| -          // handled at runtime by the dynamic call mechanism. So we only
 | 
| -          // concern ourselves with statically known function types.
 | 
| -          //
 | 
| -          // For the same reason, we can ignore "noSuchMethod".
 | 
| -          // call-implemented-by-nSM will be dispatched by dcall at runtime.
 | 
| -          //
 | 
| -          isCallable = !member.isGetter || member.returnType is FunctionType;
 | 
| -        }
 | 
|        }
 | 
|      }
 | 
|  
 | 
| -    // True if a "call" method or getter exists directly or indirectly on this
 | 
| -    // class.  If so, we need special constructor handling.
 | 
| -    bool isCallableTransitive =
 | 
| -        classElem.lookUpMethod('call', currentLibrary) != null;
 | 
| -    if (!isCallableTransitive) {
 | 
| -      var callGetter = classElem.lookUpGetter('call', currentLibrary);
 | 
| -      isCallableTransitive =
 | 
| -          callGetter != null && callGetter.returnType is FunctionType;
 | 
| -    }
 | 
| -
 | 
|      JS.Expression className;
 | 
|      if (classElem.typeParameters.isNotEmpty) {
 | 
|        // Generic classes will be defined inside a function that closes over the
 | 
| @@ -945,8 +896,7 @@ class CodeGenerator extends Object
 | 
|      _classProperties =
 | 
|          new ClassPropertyModel.build(_extensionTypes, virtualFields, classElem);
 | 
|  
 | 
| -    var classExpr = _emitClassExpression(
 | 
| -        classElem, _emitClassMethods(node, ctors, fields),
 | 
| +    var classExpr = _emitClassExpression(classElem, _emitClassMethods(node),
 | 
|          fields: allFields);
 | 
|  
 | 
|      var body = <JS.Statement>[];
 | 
| @@ -954,7 +904,8 @@ class CodeGenerator extends Object
 | 
|      _emitSuperHelperSymbols(_superHelperSymbols, body);
 | 
|  
 | 
|      // Emit the class, e.g. `core.Object = class Object { ... }`
 | 
| -    _defineClass(classElem, className, classExpr, isCallable, body);
 | 
| +    _defineClass(classElem, className, classExpr, body);
 | 
| +    _defineConstructors(classElem, className, fields, body, ctors);
 | 
|  
 | 
|      // Emit things that come after the ES6 `class ... { ... }`.
 | 
|      var jsPeerNames = _getJSPeerNames(classElem);
 | 
| @@ -963,7 +914,6 @@ class CodeGenerator extends Object
 | 
|  
 | 
|      _emitClassTypeTests(classElem, className, body);
 | 
|  
 | 
| -    _defineNamedConstructors(ctors, body, className, isCallableTransitive);
 | 
|      _emitVirtualFieldSymbols(classElem, body);
 | 
|      _emitClassSignature(methods, allFields, classElem, ctors, className, body);
 | 
|      _defineExtensionMembers(className, body);
 | 
| @@ -987,35 +937,6 @@ class CodeGenerator extends Object
 | 
|      return _statement(body);
 | 
|    }
 | 
|  
 | 
| -  /// Emits code to support a class with a "call" method and an unnamed
 | 
| -  /// constructor.
 | 
| -  ///
 | 
| -  /// This ensures instances created by the unnamed constructor are functions.
 | 
| -  /// Named constructors are handled elsewhere, see [_defineNamedConstructors].
 | 
| -  JS.Expression _emitCallableClass(
 | 
| -      JS.ClassExpression classExpr, ConstructorElement unnamedCtor) {
 | 
| -    var ctor = new JS.NamedFunction(
 | 
| -        classExpr.name, _emitCallableClassConstructor(unnamedCtor));
 | 
| -
 | 
| -    // Name the constructor function the same as the class.
 | 
| -    return _callHelper('callableClass(#, #)', [ctor, classExpr]);
 | 
| -  }
 | 
| -
 | 
| -  /// Emits a constructor that ensures instances of this class are callable as
 | 
| -  /// functions in JavaScript.
 | 
| -  JS.Fun _emitCallableClassConstructor(ConstructorElement ctor) {
 | 
| -    return js.call(
 | 
| -        r'''function (...args) {
 | 
| -          function call(...args) {
 | 
| -            return call.call.apply(call, args);
 | 
| -          }
 | 
| -          call.__proto__ = this.__proto__;
 | 
| -          call.#.apply(call, args);
 | 
| -          return call;
 | 
| -        }''',
 | 
| -        [_constructorName(ctor)]);
 | 
| -  }
 | 
| -
 | 
|    void _emitClassTypeTests(ClassElement classElem, JS.Expression className,
 | 
|        List<JS.Statement> body) {
 | 
|      if (classElem == objectClass) {
 | 
| @@ -1229,21 +1150,11 @@ class CodeGenerator extends Object
 | 
|    }
 | 
|  
 | 
|    void _defineClass(ClassElement classElem, JS.Expression className,
 | 
| -      JS.ClassExpression classExpr, bool isCallable, List<JS.Statement> body) {
 | 
| -    JS.Expression callableClass;
 | 
| -    if (isCallable && classElem.unnamedConstructor != null) {
 | 
| -      callableClass =
 | 
| -          _emitCallableClass(classExpr, classElem.unnamedConstructor);
 | 
| -    }
 | 
| -
 | 
| +      JS.ClassExpression classExpr, List<JS.Statement> body) {
 | 
|      if (classElem.typeParameters.isNotEmpty) {
 | 
| -      if (callableClass != null) {
 | 
| -        body.add(js.statement('const # = #;', [classExpr.name, callableClass]));
 | 
| -      } else {
 | 
| -        body.add(new JS.ClassDeclaration(classExpr));
 | 
| -      }
 | 
| +      body.add(new JS.ClassDeclaration(classExpr));
 | 
|      } else {
 | 
| -      body.add(js.statement('# = #;', [className, callableClass ?? classExpr]));
 | 
| +      body.add(js.statement('# = #;', [className, classExpr]));
 | 
|      }
 | 
|    }
 | 
|  
 | 
| @@ -1278,11 +1189,7 @@ class CodeGenerator extends Object
 | 
|      // 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(_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));
 | 
| +    var fields = element.fields.where((f) => f.type == type).toList();
 | 
|  
 | 
|      // Create toString() method
 | 
|      var nameProperties = new List<JS.Property>(fields.length);
 | 
| @@ -1295,8 +1202,8 @@ class CodeGenerator extends Object
 | 
|          js.call('function() { return #[this.index]; }', nameMap) as JS.Fun);
 | 
|  
 | 
|      // Create enum class
 | 
| -    var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
 | 
| -        _emitClassHeritage(element), [constructor, toStringF]);
 | 
| +    var classExpr = new JS.ClassExpression(
 | 
| +        new JS.Identifier(type.name), _emitClassHeritage(element), [toStringF]);
 | 
|      var id = _emitTopLevelName(element);
 | 
|  
 | 
|      // Emit metadata for synthetic enum index member.
 | 
| @@ -1311,6 +1218,9 @@ class CodeGenerator extends Object
 | 
|  
 | 
|      var result = [
 | 
|        js.statement('# = #', [id, classExpr]),
 | 
| +      js.statement(
 | 
| +          '(#.new = function(x) { this.index = x; }).prototype = #.prototype;',
 | 
| +          [id, id]),
 | 
|        _callHelperStatement('setSignature(#, #);', [id, sig])
 | 
|      ];
 | 
|  
 | 
| @@ -1451,48 +1361,32 @@ class CodeGenerator extends Object
 | 
|      return jsMethods;
 | 
|    }
 | 
|  
 | 
| -  List<JS.Method> _emitClassMethods(ClassDeclaration node,
 | 
| -      List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
 | 
| +  List<JS.Method> _emitClassMethods(ClassDeclaration node) {
 | 
|      var element = resolutionMap.elementDeclaredByClassDeclaration(node);
 | 
|      var type = element.type;
 | 
| -    var isObject = type.isObject;
 | 
|      var virtualFields = _classProperties.virtualFields;
 | 
|  
 | 
| -    // 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 (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/sdk/issues/28322.
 | 
| -      // This level of indirection will hurt performance.
 | 
| +    bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null;
 | 
| +    bool hasIterator = false;
 | 
| +
 | 
| +    if (type.isObject) {
 | 
| +      // Dart does not use ES6 constructors.
 | 
| +      // Add an error to catch any invalid usage.
 | 
|        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));
 | 
| +          js.call(
 | 
| +              r'''function() {
 | 
| +                  throw Error("use `new " + #.typeName(#.getReifiedType(this)) +
 | 
| +                      ".new(...)` to create a Dart object");
 | 
| +              }''',
 | 
| +              [_runtimeModule, _runtimeModule])));
 | 
|      }
 | 
| -
 | 
| -    bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null;
 | 
| -
 | 
| -    bool hasIterator = false;
 | 
|      for (var m in node.members) {
 | 
|        if (m is ConstructorDeclaration) {
 | 
| -        jsMethods
 | 
| -            .add(_emitConstructor(m, type, fields, virtualFields, isObject));
 | 
| +        if (m.factoryKeyword != null && !_externalOrNative(m)) {
 | 
| +          jsMethods.add(_emitFactoryConstructor(m));
 | 
| +        }
 | 
|        } else if (m is MethodDeclaration) {
 | 
|          jsMethods.add(_emitMethodDeclaration(type, m));
 | 
|  
 | 
| @@ -1540,6 +1434,43 @@ class CodeGenerator extends Object
 | 
|      return jsMethods.where((m) => m != null).toList(growable: false);
 | 
|    }
 | 
|  
 | 
| +  /// Emits a Dart factory constructor to a JS static method.
 | 
| +  JS.Method _emitFactoryConstructor(ConstructorDeclaration node) {
 | 
| +    var element = node.element;
 | 
| +    var returnType = emitTypeRef(element.returnType);
 | 
| +    var name = _constructorName(element);
 | 
| +    JS.Fun fun;
 | 
| +
 | 
| +    var redirect = node.redirectedConstructor;
 | 
| +    if (redirect != null) {
 | 
| +      // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz;
 | 
| +
 | 
| +      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 =
 | 
| +          _emitFormalParameterList(node.parameters, destructure: false);
 | 
| +
 | 
| +      fun = new JS.Fun(
 | 
| +          params,
 | 
| +          js.statement(
 | 
| +              '{ return $newKeyword #(#); }', [_visit(redirect), params]),
 | 
| +          returnType: returnType);
 | 
| +    } else {
 | 
| +      // Normal factory constructor
 | 
| +      var body = <JS.Statement>[];
 | 
| +      var init = _emitArgumentInitializers(node, constructor: true);
 | 
| +      if (init != null) body.add(init);
 | 
| +      body.add(_visit(node.body));
 | 
| +
 | 
| +      var params = _emitFormalParameterList(node.parameters);
 | 
| +      fun = new JS.Fun(params, new JS.Block(body), returnType: returnType);
 | 
| +    }
 | 
| +
 | 
| +    return annotate(new JS.Method(name, fun, isStatic: true), node, element);
 | 
| +  }
 | 
| +
 | 
|    /// Given a class C that implements method M from interface I, but does not
 | 
|    /// declare M, this will generate an implementation that forwards to
 | 
|    /// noSuchMethod.
 | 
| @@ -1557,7 +1488,7 @@ class CodeGenerator extends Object
 | 
|    ///
 | 
|    ///     eatFood(...args) {
 | 
|    ///       return core.bool.as(this.noSuchMethod(
 | 
| -  ///           new dart.InvocationImpl('eatFood', args)));
 | 
| +  ///           new dart.InvocationImpl.new('eatFood', args)));
 | 
|    ///     }
 | 
|    JS.Method _implementMockMember(ExecutableElement method, InterfaceType type) {
 | 
|      var invocationProps = <JS.Property>[];
 | 
| @@ -1592,7 +1523,8 @@ class CodeGenerator extends Object
 | 
|        }
 | 
|      }
 | 
|  
 | 
| -    var fnBody = js.call('this.noSuchMethod(new #.InvocationImpl(#, #, #))', [
 | 
| +    var fnBody =
 | 
| +        js.call('this.noSuchMethod(new #.InvocationImpl.new(#, #, #))', [
 | 
|        _runtimeModule,
 | 
|        _declareMemberName(method, useDisplayName: true),
 | 
|        positionalArgs,
 | 
| @@ -1782,21 +1714,73 @@ class CodeGenerator extends Object
 | 
|      return null;
 | 
|    }
 | 
|  
 | 
| -  void _defineNamedConstructors(List<ConstructorDeclaration> ctors,
 | 
| -      List<JS.Statement> body, JS.Expression className, bool isCallable) {
 | 
| -    var code = isCallable
 | 
| -        ? 'defineNamedConstructorCallable(#, #, #);'
 | 
| -        : 'defineNamedConstructor(#, #)';
 | 
| +  /// Defines all constructors for this class as ES5 constructors.
 | 
| +  void _defineConstructors(
 | 
| +      ClassElement classElem,
 | 
| +      JS.Expression className,
 | 
| +      List<FieldDeclaration> fields,
 | 
| +      List<JS.Statement> body,
 | 
| +      List<ConstructorDeclaration> ctors) {
 | 
| +    // See if we have a "call" with a statically known function type:
 | 
| +    //
 | 
| +    // - if it's a method, then it does because all methods do,
 | 
| +    // - if it's a getter, check the return type.
 | 
| +    //
 | 
| +    // Other cases like a getter returning dynamic/Object/Function will be
 | 
| +    // handled at runtime by the dynamic call mechanism. So we only
 | 
| +    // concern ourselves with statically known function types.
 | 
| +    //
 | 
| +    // For the same reason, we can ignore "noSuchMethod".
 | 
| +    // call-implemented-by-nSM will be dispatched by dcall at runtime.
 | 
| +    bool isCallable = classElem.lookUpMethod('call', null) != null ||
 | 
| +        classElem.lookUpGetter('call', null)?.returnType is FunctionType;
 | 
| +
 | 
| +    void addConstructor(ConstructorElement element, JS.Expression jsCtor) {
 | 
| +      var ctorName = _constructorName(element);
 | 
| +      if (JS.invalidStaticFieldName(element.name)) {
 | 
| +        jsCtor =
 | 
| +            _callHelper('defineValue(#, #, #)', [className, ctorName, jsCtor]);
 | 
| +      } else {
 | 
| +        jsCtor = js.call('#.# = #', [className, ctorName, jsCtor]);
 | 
| +      }
 | 
| +      body.add(js.statement('#.prototype = #.prototype;', [jsCtor, className]));
 | 
| +    }
 | 
| +
 | 
| +    if (classElem.isMixinApplication) {
 | 
| +      var supertype = classElem.supertype;
 | 
| +      for (var ctor in classElem.constructors) {
 | 
| +        List<JS.Identifier> jsParams = _emitParametersForElement(ctor);
 | 
| +        var superCtor = supertype.lookUpConstructor(ctor.name, ctor.library);
 | 
| +        var superCall =
 | 
| +            _superConstructorCall(classElem, className, superCtor, jsParams);
 | 
| +        addConstructor(
 | 
| +            ctor,
 | 
| +            _finishConstructorFunction(
 | 
| +                jsParams,
 | 
| +                new JS.Block(superCall != null ? [superCall] : []),
 | 
| +                isCallable));
 | 
| +      }
 | 
| +      return;
 | 
| +    }
 | 
| +
 | 
| +    // Iff no constructor is specified for a class C, it implicitly has a
 | 
| +    // default constructor `C() : super() {}`, unless C is class Object.
 | 
| +    if (ctors.isEmpty) {
 | 
| +      var superCall = _superConstructorCall(classElem, className);
 | 
| +      List<JS.Statement> body = [_initializeFields(fields)];
 | 
| +      if (superCall != null) body.add(superCall);
 | 
|  
 | 
| -    for (ConstructorDeclaration member in ctors) {
 | 
| -      if (member.name != null && member.factoryKeyword == null) {
 | 
| -        var args = [className, _constructorName(member.element)];
 | 
| -        if (isCallable) {
 | 
| -          args.add(_emitCallableClassConstructor(member.element));
 | 
| -        }
 | 
| +      addConstructor(classElem.unnamedConstructor,
 | 
| +          _finishConstructorFunction([], new JS.Block(body), isCallable));
 | 
| +      return;
 | 
| +    }
 | 
|  
 | 
| -        body.add(_callHelperStatement(code, args));
 | 
| -      }
 | 
| +    for (var ctor in ctors) {
 | 
| +      var element = ctor.element;
 | 
| +      if (element.isFactory || _externalOrNative(ctor)) continue;
 | 
| +
 | 
| +      addConstructor(
 | 
| +          element, _emitConstructor(ctor, fields, isCallable, className));
 | 
|      }
 | 
|    }
 | 
|  
 | 
| @@ -1975,9 +1959,8 @@ class CodeGenerator extends Object
 | 
|      var tCtors = <JS.Property>[];
 | 
|      if (options.emitMetadata) {
 | 
|        for (ConstructorDeclaration node in ctors) {
 | 
| -        var memberName = _constructorName(node.element);
 | 
| -        var element =
 | 
| -            resolutionMap.elementDeclaredByConstructorDeclaration(node);
 | 
| +        var element = node.element;
 | 
| +        var memberName = _constructorName(element);
 | 
|          var type = _emitAnnotatedFunctionType(element.type, node.metadata,
 | 
|              parameters: node.parameters.parameters,
 | 
|              nameType: options.hoistSignatureTypes,
 | 
| @@ -2052,92 +2035,41 @@ class CodeGenerator extends Object
 | 
|      }
 | 
|    }
 | 
|  
 | 
| -  /// Generates the implicit default constructor for class C of the form
 | 
| -  /// `C() : super() {}`.
 | 
| -  JS.Method _emitImplicitConstructor(
 | 
| -      ClassDeclaration node,
 | 
| -      List<FieldDeclaration> fields,
 | 
| -      Map<FieldElement, JS.TemporaryId> virtualFields) {
 | 
| -    // If we don't have a method body, skip this.
 | 
| -    var superCall = _superConstructorCall(node.element);
 | 
| -    if (fields.isEmpty && superCall == null) return null;
 | 
| -
 | 
| -    var initFields = _initializeFields(node, fields, virtualFields);
 | 
| -    List<JS.Statement> body = [initFields];
 | 
| -    if (superCall != null) {
 | 
| -      body.add(superCall);
 | 
| -    }
 | 
| -    var name = _constructorName(resolutionMap
 | 
| -        .elementDeclaredByClassDeclaration(node)
 | 
| -        .unnamedConstructor);
 | 
| -    return annotate(
 | 
| -        new JS.Method(name, js.call('function() { #; }', [body]) as JS.Fun),
 | 
| -        node,
 | 
| -        node.element);
 | 
| -  }
 | 
| -
 | 
| -  JS.Method _emitConstructor(
 | 
| -      ConstructorDeclaration node,
 | 
| -      InterfaceType type,
 | 
| -      List<FieldDeclaration> fields,
 | 
| -      Map<FieldElement, JS.TemporaryId> virtualFields,
 | 
| -      bool isObject) {
 | 
| -    if (_externalOrNative(node)) return null;
 | 
| -
 | 
| -    var name = _constructorName(node.element);
 | 
| -    var returnType = emitTypeRef(resolutionMap
 | 
| -        .elementDeclaredByConstructorDeclaration(node)
 | 
| -        .enclosingElement
 | 
| -        .type);
 | 
| -
 | 
| -    // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz;
 | 
| -    var redirect = node.redirectedConstructor;
 | 
| -    if (redirect != null) {
 | 
| -      var newKeyword =
 | 
| -          resolutionMap.staticElementForConstructorReference(redirect).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 =
 | 
| -          _emitFormalParameterList(node.parameters, destructure: false);
 | 
| -
 | 
| -      var fun = new JS.Fun(
 | 
| -          params,
 | 
| -          js.statement(
 | 
| -              '{ return $newKeyword #(#); }', [_visit(redirect), params]),
 | 
| -          returnType: returnType);
 | 
| -      return annotate(
 | 
| -          new JS.Method(name, fun, isStatic: true), node, node.element);
 | 
| -    }
 | 
| -
 | 
| +  JS.Expression _emitConstructor(ConstructorDeclaration node,
 | 
| +      List<FieldDeclaration> fields, bool isCallable, JS.Expression className) {
 | 
|      var params = _emitFormalParameterList(node.parameters);
 | 
|  
 | 
| -    // 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(params, new JS.Block(body), returnType: returnType);
 | 
| -      return annotate(
 | 
| -          new JS.Method(name, fun, isStatic: true), node, node.element);
 | 
| -    }
 | 
| -
 | 
| -    // Code generation for Object's constructor.
 | 
|      var savedFunction = _currentFunction;
 | 
|      _currentFunction = node.body;
 | 
| -    var body = _emitConstructorBody(node, fields, virtualFields);
 | 
| +
 | 
| +    var savedSuperAllowed = _superAllowed;
 | 
| +    _superAllowed = false;
 | 
| +    var body = _emitConstructorBody(node, fields, className);
 | 
| +    _superAllowed = savedSuperAllowed;
 | 
|      _currentFunction = savedFunction;
 | 
|  
 | 
| -    // We generate constructors as initializer methods in the class;
 | 
| -    // this allows use of `super` for instance methods/properties.
 | 
| -    // It also avoids V8 restrictions on `super` in default constructors.
 | 
| -    return annotate(
 | 
| -        new JS.Method(name, new JS.Fun(params, body, returnType: returnType)),
 | 
| -        node,
 | 
| -        node.element);
 | 
| +    return _finishConstructorFunction(params, body, isCallable);
 | 
| +  }
 | 
| +
 | 
| +  JS.Expression _finishConstructorFunction(
 | 
| +      List<JS.Parameter> params, JS.Block body, isCallable) {
 | 
| +    // We consider a class callable if it inherits from anything with a `call`
 | 
| +    // method. As a result, we can know the callable JS function was created
 | 
| +    // at the first constructor that was hit.
 | 
| +    if (!isCallable) return new JS.Fun(params, body);
 | 
| +    return js.call(
 | 
| +        r'''function callableClass(#) {
 | 
| +          if (typeof this !== "function") {
 | 
| +            function self(...args) {
 | 
| +              return self.call.apply(self, args);
 | 
| +            }
 | 
| +            self.__proto__ = this.__proto__;
 | 
| +            callableClass.call(self, #);
 | 
| +            return self;
 | 
| +          }
 | 
| +          #
 | 
| +        }''',
 | 
| +        [params, params, body]);
 | 
|    }
 | 
|  
 | 
|    JS.Expression _constructorName(ConstructorElement ctor) {
 | 
| @@ -2149,10 +2081,8 @@ class CodeGenerator extends Object
 | 
|      return _emitMemberName(name, isStatic: true);
 | 
|    }
 | 
|  
 | 
| -  JS.Block _emitConstructorBody(
 | 
| -      ConstructorDeclaration node,
 | 
| -      List<FieldDeclaration> fields,
 | 
| -      Map<FieldElement, JS.TemporaryId> virtualFields) {
 | 
| +  JS.Block _emitConstructorBody(ConstructorDeclaration node,
 | 
| +      List<FieldDeclaration> fields, JS.Expression className) {
 | 
|      var body = <JS.Statement>[];
 | 
|      ClassDeclaration cls = node.parent;
 | 
|  
 | 
| @@ -2171,7 +2101,7 @@ class CodeGenerator extends Object
 | 
|          orElse: () => null);
 | 
|  
 | 
|      if (redirectCall != null) {
 | 
| -      body.add(_visit(redirectCall));
 | 
| +      body.add(_emitRedirectingConstructor(redirectCall, className));
 | 
|        return new JS.Block(body);
 | 
|      }
 | 
|  
 | 
| @@ -2179,7 +2109,7 @@ class CodeGenerator extends Object
 | 
|      // 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(cls, fields, virtualFields, node));
 | 
| +    body.add(_initializeFields(fields, node));
 | 
|  
 | 
|      var superCall = node.initializers.firstWhere(
 | 
|          (i) => i is SuperConstructorInvocation,
 | 
| @@ -2188,57 +2118,47 @@ class CodeGenerator extends Object
 | 
|      // 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);
 | 
| +    var superCallArgs =
 | 
| +        superCall != null ? _emitArgumentList(superCall.argumentList) : null;
 | 
| +    var jsSuper = _superConstructorCall(
 | 
| +        cls.element, className, superCall?.staticElement, superCallArgs);
 | 
| +    if (jsSuper != null) body.add(annotate(jsSuper, superCall));
 | 
|  
 | 
|      body.add(_visit(node.body));
 | 
|      return new JS.Block(body)..sourceInformation = node;
 | 
|    }
 | 
|  
 | 
| -  @override
 | 
| -  JS.Statement visitRedirectingConstructorInvocation(
 | 
| -      RedirectingConstructorInvocation node) {
 | 
| -    var ctor = resolutionMap.staticElementForConstructorReference(node);
 | 
| -    var cls = ctor.enclosingElement;
 | 
| +  JS.Statement _emitRedirectingConstructor(
 | 
| +      RedirectingConstructorInvocation node, JS.Expression className) {
 | 
| +    var ctor = node.staticElement;
 | 
|      // 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),
 | 
| +    return js.statement('#.#.call(this, #);', [
 | 
| +      className,
 | 
|        _constructorName(ctor),
 | 
|        _emitArgumentList(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;
 | 
| -    }
 | 
| -
 | 
| +  JS.Statement _superConstructorCall(
 | 
| +      ClassElement element, JS.Expression className,
 | 
| +      [ConstructorElement superCtor, List<JS.Expression> args]) {
 | 
| +    // 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);
 | 
| +      assert(element.type.isObject || options.unsafeForceCompile);
 | 
|        return null;
 | 
|      }
 | 
|  
 | 
| +    // We can skip the super call if it's empty. Typically this happens for
 | 
| +    // things that extend Object.
 | 
|      if (superCtor.name == '' && !_hasUnnamedSuperConstructor(element)) {
 | 
|        return null;
 | 
|      }
 | 
|  
 | 
|      var name = _constructorName(superCtor);
 | 
| -    var args = node != null ? _emitArgumentList(node.argumentList) : [];
 | 
| -    return annotate(js.statement('super.#(#);', [name, args]), node);
 | 
| +    return js.statement(
 | 
| +        '#.__proto__.#.call(this, #);', [className, name, args ?? []]);
 | 
|    }
 | 
|  
 | 
|    bool _hasUnnamedSuperConstructor(ClassElement e) {
 | 
| @@ -2264,10 +2184,7 @@ class CodeGenerator extends Object
 | 
|    ///   2. field initializing parameters,
 | 
|    ///   3. constructor field initializers,
 | 
|    ///   4. initialize fields not covered in 1-3
 | 
| -  JS.Statement _initializeFields(
 | 
| -      ClassDeclaration cls,
 | 
| -      List<FieldDeclaration> fieldDecls,
 | 
| -      Map<FieldElement, JS.TemporaryId> virtualFields,
 | 
| +  JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls,
 | 
|        [ConstructorDeclaration ctor]) {
 | 
|      // Run field initializers if they can have side-effects.
 | 
|      var fields = new Map<FieldElement, JS.Expression>();
 | 
| @@ -2320,7 +2237,8 @@ class CodeGenerator extends Object
 | 
|  
 | 
|      var body = <JS.Statement>[];
 | 
|      fields.forEach((FieldElement e, JS.Expression initialValue) {
 | 
| -      JS.Expression access = virtualFields[e] ?? _declareMemberName(e.getter);
 | 
| +      JS.Expression access =
 | 
| +          _classProperties.virtualFields[e] ?? _declareMemberName(e.getter);
 | 
|        body.add(js.statement('this.# = #;', [access, initialValue]));
 | 
|      });
 | 
|  
 | 
| @@ -2802,7 +2720,8 @@ class CodeGenerator extends Object
 | 
|    JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) {
 | 
|      var accessor = resolutionMap.staticElementForIdentifier(node);
 | 
|      if (accessor == null) {
 | 
| -      return _callHelper('throw("compile error: unresolved identifier: " + #)',
 | 
| +      return _callHelper(
 | 
| +          'throw(Error("compile error: unresolved identifier: " + #))',
 | 
|            js.escapedString(node.name ?? '<null>'));
 | 
|      }
 | 
|  
 | 
| @@ -3807,6 +3726,19 @@ class CodeGenerator extends Object
 | 
|          _propertyName(node.name.label.name), _visit(node.expression));
 | 
|    }
 | 
|  
 | 
| +  List<JS.Parameter> _emitParametersForElement(ExecutableElement member) {
 | 
| +    var jsParams = <JS.Identifier>[];
 | 
| +    for (var p in member.parameters) {
 | 
| +      if (p.parameterKind != ParameterKind.NAMED) {
 | 
| +        jsParams.add(new JS.Identifier(p.name));
 | 
| +      } else {
 | 
| +        jsParams.add(new JS.TemporaryId('namedArgs'));
 | 
| +        break;
 | 
| +      }
 | 
| +    }
 | 
| +    return jsParams;
 | 
| +  }
 | 
| +
 | 
|    List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
 | 
|        {bool destructure: true}) {
 | 
|      if (node == null) return [];
 | 
| @@ -4065,15 +3997,9 @@ class CodeGenerator extends Object
 | 
|  
 | 
|    JS.Expression _emitConstructorName(
 | 
|        ConstructorElement element, DartType type, SimpleIdentifier name) {
 | 
| -    var classElem = element.enclosingElement;
 | 
| -    var interop = _emitJSInterop(classElem);
 | 
| -    if (interop != null) return interop;
 | 
| -    var typeName = _emitConstructorAccess(type);
 | 
| -    if (name != null || element.isFactory) {
 | 
| -      var namedCtor = _constructorName(element);
 | 
| -      return new JS.PropertyAccess(typeName, namedCtor);
 | 
| -    }
 | 
| -    return typeName;
 | 
| +    return _emitJSInterop(type.element) ??
 | 
| +        new JS.PropertyAccess(
 | 
| +            _emitConstructorAccess(type), _constructorName(element));
 | 
|    }
 | 
|  
 | 
|    @override
 | 
| @@ -4093,10 +4019,12 @@ class CodeGenerator extends Object
 | 
|        bool isNative = false;
 | 
|        if (element == null) {
 | 
|          ctor = _callHelper(
 | 
| -            'throw("compile error: unresolved constructor: " + # + "." + #)', [
 | 
| -          js.escapedString(type?.name ?? '<null>'),
 | 
| -          js.escapedString(name?.name ?? '<unnamed>')
 | 
| -        ]);
 | 
| +            'throw(Error("compile error: unresolved constructor: " '
 | 
| +            '+ # + "." + #))',
 | 
| +            [
 | 
| +              js.escapedString(type?.name ?? '<null>'),
 | 
| +              js.escapedString(name?.name ?? '<unnamed>')
 | 
| +            ]);
 | 
|        } else {
 | 
|          ctor = _emitConstructorName(element, type, name);
 | 
|          isFactory = element.isFactory;
 | 
| @@ -5297,7 +5225,7 @@ class CodeGenerator extends Object
 | 
|        var name = js.string(node.components.join('.'), "'");
 | 
|        if (last.startsWith('_')) {
 | 
|          var nativeSymbol = _emitPrivateNameSymbol(currentLibrary, last);
 | 
| -        return js.call('new #(#, #)', [
 | 
| +        return js.call('new #.new(#, #)', [
 | 
|            _emitConstructorAccess(privateSymbolClass.type),
 | 
|            name,
 | 
|            nativeSymbol
 | 
| @@ -5802,6 +5730,10 @@ class CodeGenerator extends Object
 | 
|    @override
 | 
|    visitConstructorFieldInitializer(node) => _unreachable(node);
 | 
|  
 | 
| +  /// Unusued, see [_emitRedirectingConstructor].
 | 
| +  @override
 | 
| +  visitRedirectingConstructorInvocation(node) => _unreachable(node);
 | 
| +
 | 
|    /// Unusued. Handled in [visitForEachStatement].
 | 
|    @override
 | 
|    visitDeclaredIdentifier(node) => _unreachable(node);
 | 
| 
 |