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. |