| 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 a23e3e3231364ffa597584c29138e42fff158429..0b45c44b195ff254313ee1d02b377dfd262cabc2 100644
|
| --- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart
|
| +++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart
|
| @@ -99,6 +99,8 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| final _initializingFormalTemps =
|
| new HashMap<ParameterElement, JS.TemporaryId>();
|
|
|
| + final _syntheticSuperclasses = new HashMap<ClassElement, JS.Identifier>();
|
| +
|
| JS.Identifier _extensionSymbolsModule;
|
| JS.Identifier _runtimeModule;
|
| final namedArgumentTemp = new JS.TemporaryId('opts');
|
| @@ -821,18 +823,28 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| _initExtensionSymbols(classElem, methods, fields, body);
|
| _emitSuperHelperSymbols(_superHelperSymbols, body);
|
|
|
| + var syntheticSuperId = _syntheticSuperclasses[classElem];
|
| + if (syntheticSuperId != null) {
|
| + body.add(js.statement('const # = #;', [
|
| + syntheticSuperId,
|
| + _emitSyntheticSuperClassExpr(classElem, syntheticSuperId)
|
| + ]));
|
| + }
|
| +
|
| // Emit the class, e.g. `core.Object = class Object { ... }`
|
| _defineClass(classElem, className, classExpr, isCallable, body);
|
|
|
| // Emit things that come after the ES6 `class ... { ... }`.
|
| var jsPeerNames = _getJSPeerNames(classElem);
|
| + var targetClassName = syntheticSuperId ?? className;
|
| JS.Statement deferredBaseClass =
|
| - _setBaseClass(classElem, className, jsPeerNames, body);
|
| + _setBaseClass(classElem, targetClassName, className, jsPeerNames, body);
|
|
|
| _emitClassTypeTests(classElem, className, body);
|
|
|
| _defineNamedConstructors(ctors, body, className, isCallableTransitive);
|
| _emitVirtualFieldSymbols(className, body);
|
| + _emitVirtualizedFieldSymbols(className, body);
|
| _emitClassSignature(
|
| methods, allFields, classElem, ctors, extensions, className, body);
|
| _defineExtensionMembers(extensions, className, body);
|
| @@ -1097,6 +1109,16 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| });
|
| }
|
|
|
| + void _emitVirtualizedFieldSymbols(
|
| + JS.Expression className, List<JS.Statement> body) {
|
| + _classProperties.virtualizedFields.forEach((field, virtualField) {
|
| + body.add(js.statement('const # = #;', [
|
| + virtualField,
|
| + _callHelper('virtualName(#)', _declareMemberName(field.getter))
|
| + ]));
|
| + });
|
| + }
|
| +
|
| void _defineClass(ClassElement classElem, JS.Expression className,
|
| JS.ClassExpression classExpr, bool isCallable, List<JS.Statement> body) {
|
| JS.Expression callableClass;
|
| @@ -1234,7 +1256,7 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| var typeArguments = type.typeArguments;
|
| for (var typeArg in typeArguments) {
|
| var typeElement = typeArg.element;
|
| - // FIXME(vsm): This does not track mutual recursive dependences.
|
| + // FIXME(vsm): This does not track mutual recursive dependencies.
|
| if (current == typeElement || _deferIfNeeded(typeArg, current)) {
|
| return true;
|
| }
|
| @@ -1243,6 +1265,43 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| return false;
|
| }
|
|
|
| + JS.Expression _emitSyntheticSuperClassExpr(
|
| + ClassElement element, JS.Identifier id) {
|
| + var heritage = _emitClassHeritage(element, ignoreSynthetic: true);
|
| + var typeParams = _emitTypeFormals(element.typeParameters);
|
| + var methods = <JS.Method>[];
|
| +
|
| + // Introduce the new forwarder with one-shot initialization support.
|
| + var body = <JS.Statement>[];
|
| +
|
| + var virtualizeArguments = <JS.Expression>[];
|
| + for (var field in _classProperties.virtualizedFields.keys) {
|
| + JS.TemporaryId fieldId = _classProperties.virtualizedFields[field];
|
| + var name = _declareMemberName(field.getter);
|
| + virtualizeArguments.add(name);
|
| + virtualizeArguments.add(fieldId);
|
| + }
|
| + body.add(_callHelperStatement(
|
| + 'virtualize(this, #);', new JS.ArrayInitializer(virtualizeArguments)));
|
| + body.add(js.statement('super.new.apply(this, arguments)'));
|
| +
|
| + var constructorName = _propertyName('new');
|
| + methods.add(new JS.Method(
|
| + constructorName, js.call('function() { #; }', [body]) as JS.Fun));
|
| +
|
| + for (var field in _classProperties.virtualizedFields.keys) {
|
| + JS.TemporaryId fieldId = _classProperties.virtualizedFields[field];
|
| + var name = _declareMemberName(field.getter);
|
| + var getter = js.call('function() { return this[#]; }', [fieldId]);
|
| + methods.add(new JS.Method(name, getter, isGetter: true));
|
| + var setter = js.call('function(value) { this[#] = value; }', [fieldId]);
|
| + methods.add(new JS.Method(name, setter, isSetter: true));
|
| + }
|
| +
|
| + return new JS.ClassExpression(id, heritage, methods,
|
| + typeParams: typeParams);
|
| + }
|
| +
|
| JS.ClassExpression _emitClassExpression(
|
| ClassElement element, List<JS.Method> methods,
|
| {List<FieldDeclaration> fields}) {
|
| @@ -1255,7 +1314,8 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| typeParams: typeParams, fields: jsFields);
|
| }
|
|
|
| - JS.Expression _emitClassHeritage(ClassElement element) {
|
| + JS.Expression _emitClassHeritage(ClassElement element,
|
| + {bool ignoreSynthetic = false}) {
|
| var type = element.type;
|
| if (type.isObject) return null;
|
|
|
| @@ -1278,6 +1338,18 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| .map((t) => _emitConstructorAccess(t, nameType: false))
|
| .toList();
|
| assert(baseclasses.isNotEmpty);
|
| +
|
| + if (!ignoreSynthetic &&
|
| + _classProperties != null &&
|
| + _classProperties.virtualizedFields.isNotEmpty) {
|
| + // TODO(kasperl): we would want to have a unique generated ID here.
|
| + // We can't use 'TemporaryId' since a class is "declared" twice:
|
| + // var ClassName = class ClassName { ... }
|
| + var id = new JS.Identifier('${element.name}_${basetypes[0].name}');
|
| + _syntheticSuperclasses[element] = id;
|
| + baseclasses = [id];
|
| + }
|
| +
|
| var heritage = (baseclasses.length == 1)
|
| ? baseclasses.first
|
| : _callHelper('mixin(#)', [baseclasses]);
|
| @@ -1593,6 +1665,10 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| var methodElement = method.element as PropertyAccessorElement;
|
| var field = methodElement.variable;
|
| if (!field.isSynthetic) return null;
|
| + if (!(field.getter?.isAbstract == false ||
|
| + field.setter?.isAbstract == false)) {
|
| + return null;
|
| + }
|
|
|
| // Generate a corresponding virtual getter / setter.
|
| var name = _declareMemberName(methodElement);
|
| @@ -1686,15 +1762,25 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| }
|
| }
|
|
|
| - JS.Statement _setBaseClass(ClassElement classElem, JS.Expression className,
|
| - List<String> jsPeerNames, List<JS.Statement> body) {
|
| + /// Sets the baseclass of [classElem], represented by [targetClassName], to
|
| + /// a "corrected" version of the base class.
|
| + ///
|
| + /// The [targetClassName] and [className] may differ if there is a
|
| + /// virtualization class injected in between the [classElem] and its
|
| + /// superclass.
|
| + JS.Statement _setBaseClass(
|
| + ClassElement classElem,
|
| + JS.Expression targetClassName,
|
| + JS.Expression className,
|
| + List<String> jsPeerNames,
|
| + List<JS.Statement> body) {
|
| var typeFormals = classElem.typeParameters;
|
| if (jsPeerNames.isNotEmpty && typeFormals.isNotEmpty) {
|
| for (var peer in jsPeerNames) {
|
| // TODO(jmesserly): we should just extend Array in the first place
|
| var newBaseClass = _callHelper('global.#', [peer]);
|
| body.add(_callHelperStatement(
|
| - 'setExtensionBaseClass(#, #);', [className, newBaseClass]));
|
| + 'setExtensionBaseClass(#, #);', [targetClassName, newBaseClass]));
|
| }
|
| } else if (_hasDeferredSupertype.contains(classElem)) {
|
| // TODO(vsm): consider just threading the deferred supertype through
|
| @@ -1709,7 +1795,7 @@ class CodeGenerator extends GeneralizingAstVisitor
|
| newBaseClass = _callHelper('mixin(#)', [mixins]);
|
| }
|
| var deferredBaseClass = _callHelperStatement(
|
| - 'setBaseClass(#, #);', [className, newBaseClass]);
|
| + 'setBaseClass(#, #);', [targetClassName, newBaseClass]);
|
| if (typeFormals.isNotEmpty) return deferredBaseClass;
|
| body.add(deferredBaseClass);
|
| }
|
| @@ -4980,15 +5066,6 @@ class CodeGenerator extends GeneralizingAstVisitor
|
|
|
| var jsTarget = _emitTarget(target, member, isStatic);
|
| bool isSuper = jsTarget is JS.Super;
|
| - if (isSuper &&
|
| - !member.isSynthetic &&
|
| - member is FieldElementImpl &&
|
| - !member.isVirtual) {
|
| - // If super.x is a sealed field, then x is an instance property since
|
| - // subclasses cannot override x.
|
| - jsTarget = new JS.This();
|
| - }
|
| -
|
| JS.Expression result;
|
| if (member != null && member is MethodElement && !isStatic) {
|
| // Tear-off methods: explicitly bind it.
|
|
|