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

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

Issue 2691433004: Support virtual fields in DDC without requiring the @virtual annotation.
Patch Set: Created 3 years, 10 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
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.

Powered by Google App Engine
This is Rietveld 408576698