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

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

Issue 1918033002: Remove virtualField helper, just emit getters/setters in codegen. (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 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 | « lib/runtime/dart_sdk.js ('k') | test/codegen/expect/fieldtest.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/compiler/code_generator.dart
diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart
index 2c19b39ef6f2c7ac0a9af6e10978145d953ec503..43de895f24bd03427106230beb2078f5d600b6e6 100644
--- a/lib/src/compiler/code_generator.dart
+++ b/lib/src/compiler/code_generator.dart
@@ -531,9 +531,24 @@ class CodeGenerator extends GeneralizingAstVisitor
}
}
+ JS.Expression className;
+ if (classElem.typeParameters.isNotEmpty) {
+ // Generic classes will be defined inside a function that closes over the
+ // type parameter. So we can use their local variable name directly.
+ className = new JS.Identifier(classElem.name);
+ } else {
+ className = _emitTopLevelName(classElem);
+ }
+
+ var superclasses = getSuperclasses(classElem);
+ var virtualFields = <FieldElement, JS.TemporaryId>{};
+ var virtualFieldSymbols = <JS.Statement>[];
+ _registerVirtualFields(classElem, className, superclasses, fields,
+ virtualFields, virtualFieldSymbols);
+
var allFields = new List.from(fields)..addAll(staticFields);
- var classExpr = _emitClassExpression(
- classElem, _emitClassMethods(node, ctors, fields),
+ var classExpr = _emitClassExpression(classElem,
+ _emitClassMethods(node, ctors, fields, superclasses, virtualFields),
fields: allFields);
var body = <JS.Statement>[];
@@ -541,12 +556,12 @@ class CodeGenerator extends GeneralizingAstVisitor
_initExtensionSymbols(classElem, methods, fields, body);
// Emit the class, e.g. `core.Object = class Object { ... }`
- JS.Expression className = _defineClass(classElem, classExpr, body);
+ _defineClass(classElem, className, classExpr, body);
// Emit things that come after the ES6 `class ... { ... }`.
_setBaseClass(classElem, className, body);
_defineNamedConstructors(ctors, body, className);
- _emitVirtualFields(classElem, fields, className, body);
+ _emitVirtualFieldSymbols(virtualFieldSymbols, body);
_emitClassSignature(methods, classElem, ctors, extensions, className, body);
_defineExtensionMembers(extensions, className, body);
_emitClassMetadata(node.metadata, className, body);
@@ -563,19 +578,42 @@ class CodeGenerator extends GeneralizingAstVisitor
return _statement(body);
}
- JS.Expression _defineClass(ClassElement classElem,
+ void _registerVirtualFields(
+ ClassElement classElem,
+ JS.Expression className,
+ List<ClassElement> superclasses,
+ List<FieldDeclaration> fields,
+ Map<FieldElement, JS.TemporaryId> virtualFields,
+ List<JS.Statement> virtualFieldSymbols) {
+ for (var field in fields) {
+ for (VariableDeclaration field in field.fields.variables) {
+ var overrideInfo = checkForPropertyOverride(
+ field.element, superclasses, _extensionTypes);
+ if (overrideInfo.foundGetter || overrideInfo.foundSetter) {
+ var fieldName =
+ _emitMemberName(field.element.name, type: classElem.type);
+ var virtualField = new JS.TemporaryId(field.element.name);
+ virtualFields[field.element] = virtualField;
+ virtualFieldSymbols.add(js.statement(
+ 'const # = Symbol(#.name + "." + #.toString());',
+ [virtualField, className, fieldName]));
+ }
+ }
+ }
+ }
+
+ void _defineClass(ClassElement classElem, JS.Expression className,
JS.ClassExpression classExpr, List<JS.Statement> body) {
- JS.Expression className;
if (classElem.typeParameters.isNotEmpty) {
- // Generic classes will be defined inside a function that closes over the
- // type parameter. So we can use their local variable name directly.
- className = classExpr.name;
body.add(new JS.ClassDeclaration(classExpr));
} else {
- className = _emitTopLevelName(classElem);
body.add(js.statement('# = #;', [className, classExpr]));
}
- return className;
+ }
+
+ void _emitVirtualFieldSymbols(
+ List<JS.Statement> virtualFields, List<JS.Statement> body) {
+ body.addAll(virtualFields);
}
List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) {
@@ -752,8 +790,12 @@ class CodeGenerator extends GeneralizingAstVisitor
return jsMethods;
}
- List<JS.Method> _emitClassMethods(ClassDeclaration node,
- List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
+ List<JS.Method> _emitClassMethods(
+ ClassDeclaration node,
+ List<ConstructorDeclaration> ctors,
+ List<FieldDeclaration> fields,
+ List<ClassElement> superclasses,
+ Map<FieldElement, JS.TemporaryId> virtualFields) {
var element = node.element;
var type = element.type;
var isObject = type.isObject;
@@ -762,16 +804,16 @@ class CodeGenerator extends GeneralizingAstVisitor
// default constructor `C() : super() {}`, unless C is class Object.
var jsMethods = <JS.Method>[];
if (ctors.isEmpty && !isObject) {
- jsMethods.add(_emitImplicitConstructor(node, fields));
+ jsMethods.add(_emitImplicitConstructor(node, fields, virtualFields));
}
bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null;
- var superclasses = getSuperclasses(element);
bool hasIterator = false;
for (var m in node.members) {
if (m is ConstructorDeclaration) {
- jsMethods.add(_emitConstructor(m, type, fields, isObject));
+ jsMethods
+ .add(_emitConstructor(m, type, fields, virtualFields, isObject));
} else if (m is MethodDeclaration) {
jsMethods.add(_emitMethodDeclaration(type, m));
@@ -783,8 +825,17 @@ class CodeGenerator extends GeneralizingAstVisitor
hasIterator = true;
jsMethods.add(_emitIterable(type));
}
- } else if (m is FieldDeclaration && _extensionTypes.contains(element)) {
- jsMethods.addAll(_emitNativeFieldAccessors(m));
+ } else if (m is FieldDeclaration) {
+ if (_extensionTypes.contains(element)) {
+ jsMethods.addAll(_emitNativeFieldAccessors(m));
+ continue;
+ }
+ if (m.isStatic) continue;
+ for (VariableDeclaration field in m.fields.variables) {
+ if (virtualFields.containsKey(field.element)) {
+ jsMethods.addAll(_emitVirtualFieldAccessor(field, virtualFields));
+ }
+ }
}
}
@@ -803,6 +854,38 @@ class CodeGenerator extends GeneralizingAstVisitor
return jsMethods.where((m) => m != null).toList(growable: false);
}
+ /// This is called whenever a derived class needs to introduce a new field,
+ /// shadowing a field or getter/setter pair on its parent.
+ ///
+ /// This is important because otherwise, trying to read or write the field
+ /// would end up calling the getter or setter, and one of those might not even
+ /// exist, resulting in a runtime error. Even if they did exist, that's the
+ /// wrong behavior if a new field was declared.
+ List<JS.Method> _emitVirtualFieldAccessor(VariableDeclaration field,
+ Map<FieldElement, JS.TemporaryId> virtualFields) {
+ var virtualField = virtualFields[field.element];
+ var result = <JS.Method>[];
+ var name = _emitMemberName(field.element.name,
+ type: (field.element.enclosingElement as ClassElement).type);
+ var getter = js.call('function() { return this[#]; }', [virtualField]);
+ result.add(new JS.Method(name, getter, isGetter: true));
+
+ if (field.isFinal) {
+ var setter = js.call('function(value) { super[#] = value; }', [name]);
+ result.add(new JS.Method(name, setter, isSetter: true));
+ } else {
+ var setter =
+ js.call('function(value) { this[#] = value; }', [virtualField]);
+ result.add(new JS.Method(name, setter, isSetter: true));
+ }
+
+ return result;
+ }
+
+ /// Emit a getter or setter that simply forwards to the superclass getter or
+ /// setter. This is needed because in ES6, if you only override a getter
+ /// (alternatively, a setter), then there is an implicit override of the
+ /// setter (alternatively, the getter) that does nothing.
JS.Method _emitSuperAccessorWrapper(MethodDeclaration method,
InterfaceType type, List<ClassElement> superclasses) {
var methodElement = method.element as PropertyAccessorElement;
@@ -911,26 +994,6 @@ class CodeGenerator extends GeneralizingAstVisitor
}
}
- /// Emits instance fields, if they are virtual
- /// (in other words, they override a getter/setter pair).
- void _emitVirtualFields(
- ClassElement classElement,
- List<FieldDeclaration> fields,
- JS.Expression className,
- List<JS.Statement> body) {
- List<ClassElement> superclasses = getSuperclasses(classElement);
- for (FieldDeclaration member in fields) {
- for (VariableDeclaration field in member.fields.variables) {
- var propertyOverrideResult = checkForPropertyOverride(
- field.element, superclasses, _extensionTypes);
- if (propertyOverrideResult.foundGetter ||
- propertyOverrideResult.foundSetter) {
- body.add(_overrideField(className, field.element));
- }
- }
- }
- }
-
void _defineNamedConstructors(List<ConstructorDeclaration> ctors,
List<JS.Statement> body, JS.Expression className) {
for (ConstructorDeclaration member in ctors) {
@@ -1137,23 +1200,19 @@ class CodeGenerator extends GeneralizingAstVisitor
_collectExtensions(type.superclass, types);
}
- JS.Statement _overrideField(JS.Expression className, FieldElement e) {
- var cls = e.enclosingElement;
- return js.statement('dart.virtualField(#, #)',
- [className, _emitMemberName(e.name, type: cls.type)]);
- }
-
/// Generates the implicit default constructor for class C of the form
/// `C() : super() {}`.
JS.Method _emitImplicitConstructor(
- ClassDeclaration node, List<FieldDeclaration> fields) {
+ ClassDeclaration node,
+ List<FieldDeclaration> fields,
+ Map<FieldElement, JS.TemporaryId> virtualFields) {
assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty);
// If we don't have a method body, skip this.
var superCall = _superConstructorCall(node.element);
if (fields.isEmpty && superCall == null) return null;
- dynamic body = _initializeFields(node, fields);
+ dynamic body = _initializeFields(node, fields, virtualFields);
if (superCall != null) {
body = [
[body, superCall]
@@ -1166,8 +1225,12 @@ class CodeGenerator extends GeneralizingAstVisitor
node.element);
}
- JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type,
- List<FieldDeclaration> fields, bool isObject) {
+ 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);
@@ -1238,7 +1301,7 @@ class CodeGenerator extends GeneralizingAstVisitor
} else {
var savedFunction = _currentFunction;
_currentFunction = node.body;
- body = _emitConstructorBody(node, fields);
+ body = _emitConstructorBody(node, fields, virtualFields);
_currentFunction = savedFunction;
}
@@ -1266,7 +1329,9 @@ class CodeGenerator extends GeneralizingAstVisitor
}
JS.Block _emitConstructorBody(
- ConstructorDeclaration node, List<FieldDeclaration> fields) {
+ ConstructorDeclaration node,
+ List<FieldDeclaration> fields,
+ Map<FieldElement, JS.TemporaryId> virtualFields) {
var body = <JS.Statement>[];
ClassDeclaration cls = node.parent;
@@ -1295,7 +1360,7 @@ class CodeGenerator extends GeneralizingAstVisitor
// 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, node));
+ body.add(_initializeFields(cls, fields, virtualFields, node));
var superCall = node.initializers.firstWhere(
(i) => i is SuperConstructorInvocation,
@@ -1370,7 +1435,9 @@ class CodeGenerator extends GeneralizingAstVisitor
/// 3. constructor field initializers,
/// 4. initialize fields not covered in 1-3
JS.Statement _initializeFields(
- ClassDeclaration cls, List<FieldDeclaration> fieldDecls,
+ ClassDeclaration cls,
+ List<FieldDeclaration> fieldDecls,
+ Map<FieldElement, JS.TemporaryId> virtualFields,
[ConstructorDeclaration ctor]) {
bool isConst = ctor != null && ctor.constKeyword != null;
if (isConst) _loader.startTopLevel(cls.element);
@@ -1422,8 +1489,13 @@ class CodeGenerator extends GeneralizingAstVisitor
var body = <JS.Statement>[];
fields.forEach((FieldElement e, JS.Expression initialValue) {
- var access = _emitMemberName(e.name, type: e.enclosingElement.type);
- body.add(js.statement('this.# = #;', [access, initialValue]));
+ if (virtualFields.containsKey(e)) {
+ body.add(
+ js.statement('this[#] = #;', [virtualFields[e], initialValue]));
+ } else {
+ var access = _emitMemberName(e.name, type: e.enclosingElement.type);
+ body.add(js.statement('this.# = #;', [access, initialValue]));
+ }
});
if (isConst) _loader.finishTopLevel(cls.element);
« no previous file with comments | « lib/runtime/dart_sdk.js ('k') | test/codegen/expect/fieldtest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698