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

Unified Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

Issue 2934623003: fix #29753, use ES5 constructors for ddc (Closed)
Patch Set: rebase Created 3 years, 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/test/browser/language_tests.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/test/browser/language_tests.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698