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..4aab66c4c08c5621a08003d4c404ebeec3689d2e 100644 |
--- a/lib/src/compiler/code_generator.dart |
+++ b/lib/src/compiler/code_generator.dart |
@@ -81,6 +81,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
final _privateNames = |
new HashMap<LibraryElement, HashMap<String, JS.TemporaryId>>(); |
+ final _virtualFields = new HashMap<VariableDeclaration, JS.TemporaryId>(); |
Jennifer Messerly
2016/04/25 22:47:04
Generally we don't use AST nodes as hash keys. Sho
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
+ final _virtualFieldSymbols = new HashMap<ClassElement, List<JS.Statement>>(); |
Jennifer Messerly
2016/04/25 22:47:04
do we need this map? could we simply keep a List<J
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
final _initializingFormalTemps = |
new HashMap<ParameterElement, JS.TemporaryId>(); |
@@ -531,9 +533,16 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
} |
+ JS.Expression className; |
+ if (classElem.typeParameters.isNotEmpty) { |
+ className = new JS.Identifier(classElem.name); |
Jennifer Messerly
2016/04/25 22:47:04
there was a comment below:
"// Generic classes wil
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
+ } else { |
+ className = _emitTopLevelName(classElem); |
+ } |
+ |
var allFields = new List.from(fields)..addAll(staticFields); |
var classExpr = _emitClassExpression( |
- classElem, _emitClassMethods(node, ctors, fields), |
+ classElem, _emitClassMethods(node, ctors, fields, className, classElem), |
fields: allFields); |
var body = <JS.Statement>[]; |
@@ -541,12 +550,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(classElem, body); |
_emitClassSignature(methods, classElem, ctors, extensions, className, body); |
_defineExtensionMembers(extensions, className, body); |
_emitClassMetadata(node.metadata, className, body); |
@@ -563,19 +572,20 @@ class CodeGenerator extends GeneralizingAstVisitor |
return _statement(body); |
} |
- JS.Expression _defineClass(ClassElement classElem, |
+ 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(ClassElement elem, List<JS.Statement> body) { |
+ if (_virtualFieldSymbols[elem] == null) return; |
+ body.addAll(_virtualFieldSymbols[elem]); |
} |
List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { |
@@ -752,8 +762,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, |
+ JS.Expression className, |
+ ClassElement classElem) { |
Jennifer Messerly
2016/04/25 22:47:04
This parameter isn't needed.
See the very next li
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
var element = node.element; |
var type = element.type; |
var isObject = type.isObject; |
@@ -783,8 +797,21 @@ 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) { |
+ var propertyOverrideResult = checkForPropertyOverride( |
+ field.element, superclasses, _extensionTypes); |
+ if (propertyOverrideResult.foundGetter || |
Jennifer Messerly
2016/04/25 22:47:04
consider: a shorter name here.
"overrideInfo" or
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
+ propertyOverrideResult.foundSetter) { |
+ jsMethods.addAll( |
+ _emitVirtualFieldAccessor(field, type, className, classElem)); |
Jennifer Messerly
2016/04/25 22:47:04
fyi -- you probably don't need to pass classElem o
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
+ } |
+ } |
} |
} |
@@ -803,6 +830,46 @@ 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, |
+ InterfaceType type, JS.Expression className, ClassElement classElem) { |
+ var virtualField = _emitVirtualFieldSymbol(field, className, classElem); |
+ var result = <JS.Method>[]; |
+ var name = _emitMemberName(field.element.name, type: 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) {' |
Jennifer Messerly
2016/04/25 22:47:04
style comment: triple quote style is nicer for the
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
+ ' var f = this[#];' |
+ ' if (f === undefined) {' |
Jennifer Messerly
2016/04/25 22:47:04
Hmmmm, I'm a little confused about what's going on
Harry Terkelsen
2016/04/26 18:29:27
Done.
|
+ ' this[#] = value;' |
+ ' } else {' |
+ ' super[#] = value;' |
+ ' }' |
+ '}', |
+ [virtualField, virtualField, 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 +978,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,12 +1184,6 @@ 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( |
@@ -3673,6 +3714,19 @@ class CodeGenerator extends GeneralizingAstVisitor |
}); |
} |
+ JS.TemporaryId _emitVirtualFieldSymbol(VariableDeclaration field, |
+ JS.Expression className, ClassElement classElem) { |
+ return _virtualFields.putIfAbsent(field, () { |
+ var fieldName = _emitMemberName(field.element.name, |
+ type: (field.element.enclosingElement as ClassElement).type); |
+ var id = new JS.TemporaryId(field.name.name); |
+ _virtualFieldSymbols.putIfAbsent(classElem, () => <JS.Statement>[]).add(js |
+ .statement('const # = Symbol(#.name + "." + #.toString());', |
+ [id, className, fieldName])); |
+ return id; |
+ }); |
+ } |
+ |
bool _externalOrNative(node) => |
node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |