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

Unified Diff: dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 19676002: Implement top-level getters. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Merged with r25274 Created 7 years, 5 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: dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
diff --git a/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index 15cab2a27bfb85b107e213caa3dbc613f6362822..f9716a5a703c3a8b03469e756962fcb28c295ddf 100644
--- a/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -30,6 +30,31 @@ class ClosureInvocationElement extends FunctionElementX {
typedef void DefineStubFunction(String invocationName, jsAst.Expression value);
/**
+ * [member] is a field (instance, static, or top level).
+ *
+ * [name] is the field name that the [Namer] has picked for this field's
+ * storage, that is, the JavaScript property name.
+ *
+ * [accessorName] is the name of the accessor. For instance fields this is
+ * mostly the same as [name] except when [member] is shadowing a field in its
+ * superclass. For other fields, they are rarely the same.
+ *
+ * [needsGetter] and [needsSetter] represent if a getter or a setter
+ * respectively is needed. There are many factors in this, for example, if the
+ * accessor can be inlined.
+ *
+ * [needsCheckedSetter] indicates that a checked getter is needed, and in this
+ * case, [needsSetter] is always false. [needsCheckedSetter] is only true when
+ * type assertions are enabled (checked mode).
+ */
+typedef void AcceptField(VariableElement member,
+ String name,
+ String accessorName,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter);
+
+/**
* A data structure for collecting fragments of a class definition.
*/
class ClassBuilder {
@@ -1192,26 +1217,26 @@ class CodeEmitterTask extends CompilerTask {
return selectors;
}
- bool instanceFieldNeedsGetter(Element member) {
- assert(member.isField());
- if (fieldAccessNeverThrows(member)) return false;
- return backend.retainGetter(member)
- || compiler.codegenWorld.hasInvokedGetter(member, compiler);
+ bool fieldNeedsGetter(VariableElement field) {
+ assert(field.isField());
+ if (fieldAccessNeverThrows(field)) return false;
+ return backend.retainGetter(field)
+ || compiler.codegenWorld.hasInvokedGetter(field, compiler);
}
- bool instanceFieldNeedsSetter(Element member) {
- assert(member.isField());
- if (fieldAccessNeverThrows(member)) return false;
- return (!member.modifiers.isFinalOrConst())
- && (backend.retainSetter(member)
- || compiler.codegenWorld.hasInvokedSetter(member, compiler));
+ bool fieldNeedsSetter(VariableElement field) {
+ assert(field.isField());
+ if (fieldAccessNeverThrows(field)) return false;
+ return (!field.modifiers.isFinalOrConst())
+ && (backend.retainSetter(field)
+ || compiler.codegenWorld.hasInvokedSetter(field, compiler));
}
// We never access a field in a closure (a captured variable) without knowing
// that it is there. Therefore we don't need to use a getter (that will throw
// if the getter method is missing), but can always access the field directly.
- static bool fieldAccessNeverThrows(Element element) {
- return element is ClosureFieldElement;
+ static bool fieldAccessNeverThrows(VariableElement field) {
+ return field is ClosureFieldElement;
}
String compiledFieldName(Element member) {
@@ -1485,37 +1510,52 @@ class CodeEmitterTask extends CompilerTask {
}
/**
- * Documentation wanted -- johnniwinther
+ * Calls [addField] for each of the fields of [element].
*
- * Invariant: [classElement] must be a declaration element.
+ * [element] must be a [ClassElement] or a [LibraryElement].
+ *
+ * If [element] is a [ClassElement], the static fields of the class are
+ * visited if [visitStatics] is true and the instance fields are visited if
+ * [visitStatics] is false.
+ *
+ * If [element] is a [LibraryElement], [visitStatics] must be true.
+ *
+ * When visiting the instance fields of a class, the fields of its superclass
+ * are also visited if the class is instantiated.
+ *
+ * Invariant: [element] must be a declaration element.
*/
- void visitClassFields(ClassElement classElement,
- bool visitStatics,
- void addField(Element member,
- String name,
- String accessorName,
- bool needsGetter,
- bool needsSetter,
- bool needsCheckedSetter)) {
- assert(invariant(classElement, classElement.isDeclaration));
+ void visitFields(Element element, bool visitStatics, AcceptField f) {
+ assert(invariant(element, element.isDeclaration));
+
+ bool isClass = false;
+ bool isLibrary = false;
+ if (element.isClass()) {
+ isClass = true;
+ } else if (element.isLibrary()) {
+ isLibrary = true;
+ assert(invariant(element, visitStatics));
+ } else {
+ throw new SpannableAssertionFailure(
+ element, 'Expected a ClassElement or a LibraryElement.');
+ }
+
// If the class is never instantiated we still need to set it up for
// inheritance purposes, but we can simplify its JavaScript constructor.
bool isInstantiated =
- compiler.codegenWorld.instantiatedClasses.contains(classElement);
+ compiler.codegenWorld.instantiatedClasses.contains(element);
- void visitField(ClassElement enclosingClass, Element member) {
- assert(invariant(classElement, member.isDeclaration));
- LibraryElement library = member.getLibrary();
- SourceString name = member.name;
- bool isPrivate = name.isPrivate();
+ void visitField(Element holder, VariableElement field) {
+ assert(invariant(element, field.isDeclaration));
+ SourceString name = field.name;
// Keep track of whether or not we're dealing with a field mixin
// into a native class.
bool isMixinNativeField =
- classElement.isNative() && enclosingClass.isMixinApplication;
+ isClass && element.isNative() && holder.isMixinApplication;
// See if we can dynamically create getters and setters.
- // We can only generate getters and setters for [classElement] since
+ // We can only generate getters and setters for [element] since
// the fields of super classes could be overwritten with getters or
// setters.
bool needsGetter = false;
@@ -1523,43 +1563,46 @@ class CodeEmitterTask extends CompilerTask {
// We need to name shadowed fields differently, so they don't clash with
// the non-shadowed field.
bool isShadowed = false;
- if (isMixinNativeField || identical(enclosingClass, classElement)) {
- needsGetter = instanceFieldNeedsGetter(member);
- needsSetter = instanceFieldNeedsSetter(member);
+ if (isLibrary || isMixinNativeField || holder == element) {
+ needsGetter = fieldNeedsGetter(field);
+ needsSetter = fieldNeedsSetter(field);
} else {
- isShadowed = classElement.isShadowedByField(member);
+ ClassElement cls = element;
+ isShadowed = cls.isShadowedByField(field);
}
- if ((isInstantiated && !enclosingClass.isNative())
+ if ((isInstantiated && !holder.isNative())
|| needsGetter
|| needsSetter) {
String accessorName = isShadowed
- ? namer.shadowedFieldName(member)
- : namer.getName(member);
- String fieldName = member.hasFixedBackendName()
- ? member.fixedBackendName()
- : (isMixinNativeField ? member.name.slowToString() : accessorName);
+ ? namer.shadowedFieldName(field)
+ : namer.getName(field);
+ String fieldName = field.hasFixedBackendName()
+ ? field.fixedBackendName()
+ : (isMixinNativeField ? name.slowToString() : accessorName);
bool needsCheckedSetter = false;
- if (needsSetter) {
- if (compiler.enableTypeAssertions
- && canGenerateCheckedSetter(member)) {
- needsCheckedSetter = true;
- needsSetter = false;
- }
+ if (compiler.enableTypeAssertions
+ && needsSetter
+ && canGenerateCheckedSetter(field)) {
+ needsCheckedSetter = true;
+ needsSetter = false;
}
// Getters and setters with suffixes will be generated dynamically.
- addField(member,
- fieldName,
- accessorName,
- needsGetter,
- needsSetter,
- needsCheckedSetter);
+ f(field, fieldName, accessorName, needsGetter, needsSetter,
+ needsCheckedSetter);
}
}
- if (visitStatics) {
- classElement.implementation.forEachStaticField(visitField);
+ if (isLibrary) {
+ LibraryElement library = element;
+ library.implementation.forEachLocalMember((Element member) {
+ if (member.isField()) visitField(library, member);
+ });
+ } else if (visitStatics) {
+ ClassElement cls = element;
+ cls.implementation.forEachStaticField(visitField);
} else {
+ ClassElement cls = element;
// TODO(kasperl): We should make sure to only emit one version of
// overridden fields. Right now, we rely on the ordering so the
// fields pulled in from mixins are replaced with the fields from
@@ -1567,11 +1610,10 @@ class CodeEmitterTask extends CompilerTask {
// If a class is not instantiated then we add the field just so we can
// generate the field getter/setter dynamically. Since this is only
- // allowed on fields that are in [classElement] we don't need to visit
+ // allowed on fields that are in [element] we don't need to visit
// superclasses for non-instantiated classes.
- classElement.implementation.forEachInstanceField(
- visitField,
- includeSuperAndInjectedMembers: isInstantiated);
+ cls.implementation.forEachInstanceField(
+ visitField, includeSuperAndInjectedMembers: isInstantiated);
}
}
@@ -1599,12 +1641,12 @@ class CodeEmitterTask extends CompilerTask {
js.fun(args, js('$receiver.$fieldName = v')));
}
- bool canGenerateCheckedSetter(Element member) {
+ bool canGenerateCheckedSetter(VariableElement field) {
// We never generate accessors for top-level/static fields.
- if (!member.isInstanceMember()) return false;
- DartType type = member.computeType(compiler).unalias(compiler);
+ if (!field.isInstanceMember()) return false;
+ DartType type = field.computeType(compiler).unalias(compiler);
if (type.element.isTypeVariable() ||
- (type is FunctionType && type.containsTypeVariables) ||
+ (type is FunctionType && type.containsTypeVariables) ||
type.element == compiler.dynamicClass ||
type.element == compiler.objectClass) {
// TODO(ngeoffray): Support type checks on type parameters.
@@ -1675,20 +1717,31 @@ class CodeEmitterTask extends CompilerTask {
}
/// Returns `true` if fields added.
- bool emitClassFields(ClassElement classElement,
- ClassBuilder builder,
- String superName,
- { bool classIsNative: false,
- bool emitStatics: false,
- bool onlyForRti: false }) {
+ bool emitFields(Element element,
+ ClassBuilder builder,
+ String superName,
+ { bool classIsNative: false,
+ bool emitStatics: false,
+ bool onlyForRti: false }) {
assert(!emitStatics || !onlyForRti);
+ bool isClass = false;
+ bool isLibrary = false;
+ if (element.isClass()) {
+ isClass = true;
+ } else if (element.isLibrary()) {
+ isLibrary = false;
+ assert(invariant(element, emitStatics));
+ } else {
+ throw new SpannableAssertionFailure(
+ element, 'Must be a ClassElement or a LibraryElement');
+ }
StringBuffer buffer = new StringBuffer();
if (emitStatics) {
- assert(invariant(classElement, superName == null, message: superName));
+ assert(invariant(element, superName == null, message: superName));
} else {
- assert(invariant(classElement, superName != null));
+ assert(invariant(element, superName != null));
String nativeName =
- namer.getPrimitiveInterceptorRuntimeName(classElement);
+ namer.getPrimitiveInterceptorRuntimeName(element);
if (nativeName != null) {
buffer.write('$nativeName/');
}
@@ -1702,13 +1755,13 @@ class CodeEmitterTask extends CompilerTask {
bool hasMetadata = false;
if (!onlyForRti) {
- visitClassFields(classElement, emitStatics,
- (Element member,
- String name,
- String accessorName,
- bool needsGetter,
- bool needsSetter,
- bool needsCheckedSetter) {
+ visitFields(element, emitStatics,
+ (VariableElement field,
+ String name,
+ String accessorName,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter) {
// Ignore needsCheckedSetter - that is handled below.
bool needsAccessor = (needsGetter || needsSetter);
// We need to output the fields for non-native classes so we can auto-
@@ -1718,14 +1771,14 @@ class CodeEmitterTask extends CompilerTask {
if (!classIsNative || needsAccessor) {
buffer.write(separator);
separator = ',';
- var metadata = buildMetadataFunction(member);
+ var metadata = buildMetadataFunction(field);
if (metadata != null) {
hasMetadata = true;
} else {
metadata = new jsAst.LiteralNull();
}
fieldMetadata.add(metadata);
- recordMangledField(member, accessorName, member.name.slowToString());
+ recordMangledField(field, accessorName, field.name.slowToString());
if (!needsAccessor) {
// Emit field for constructor generation.
assert(!classIsNative);
@@ -1739,33 +1792,32 @@ class CodeEmitterTask extends CompilerTask {
// Only the native classes can have renaming accessors.
assert(classIsNative);
}
-
+
int getterCode = 0;
if (needsGetter) {
- // 01: function() { return this.field; }
- // 10: function(receiver) { return receiver.field; }
- // 11: function(receiver) { return this.field; }
- if (member.isInstanceMember()) {
- getterCode += backend.fieldHasInterceptedGetter(member) ? 2 : 0;
- getterCode += backend.isInterceptorClass(classElement) ? 0 : 1;
- // TODO(sra): 'isInterceptorClass' might not be the correct
- // test for methods forced to use the interceptor convention
- // because the method's class was elsewhere mixed-in to an
- // interceptor.
- assert(getterCode != 0);
+ if (field.isInstanceMember()) {
+ // 01: function() { return this.field; }
+ // 10: function(receiver) { return receiver.field; }
+ // 11: function(receiver) { return this.field; }
+ getterCode += backend.fieldHasInterceptedGetter(field) ? 2 : 0;
+ getterCode += backend.isInterceptorClass(element) ? 0 : 1;
+ // TODO(sra): 'isInterceptorClass' might not be the correct test
+ // for methods forced to use the interceptor convention because
+ // the method's class was elsewhere mixed-in to an interceptor.
+ assert(!field.isInstanceMember() || getterCode != 0);
} else {
getterCode = 1;
}
}
int setterCode = 0;
if (needsSetter) {
- // 01: function(value) { this.field = value; }
- // 10: function(receiver, value) { receiver.field = value; }
- // 11: function(receiver, value) { this.field = value; }
- if (member.isInstanceMember()) {
- setterCode += backend.fieldHasInterceptedSetter(member) ? 2 : 0;
- setterCode += backend.isInterceptorClass(classElement) ? 0 : 1;
- assert(setterCode != 0);
+ if (field.isInstanceMember()) {
+ // 01: function(value) { this.field = value; }
+ // 10: function(receiver, value) { receiver.field = value; }
+ // 11: function(receiver, value) { this.field = value; }
+ setterCode += backend.fieldHasInterceptedSetter(field) ? 2 : 0;
+ setterCode += backend.isInterceptorClass(element) ? 0 : 1;
+ assert(!field.isInstanceMember() || setterCode != 0);
} else {
setterCode = 1;
}
@@ -1773,7 +1825,7 @@ class CodeEmitterTask extends CompilerTask {
int code = getterCode + (setterCode << 2);
if (code == 0) {
compiler.reportInternalError(
- member, 'Internal error: code is 0 ($classElement/$member)');
+ field, 'Internal error: code is 0 ($element/$field)');
} else {
buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]);
}
@@ -1796,13 +1848,13 @@ class CodeEmitterTask extends CompilerTask {
void emitClassGettersSetters(ClassElement classElement,
ClassBuilder builder) {
- visitClassFields(classElement, false,
- (Element member,
- String name,
- String accessorName,
- bool needsGetter,
- bool needsSetter,
- bool needsCheckedSetter) {
+ visitFields(classElement, false,
+ (VariableElement member,
+ String name,
+ String accessorName,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter) {
compiler.withCurrentElement(member, () {
if (needsCheckedSetter) {
assert(!needsSetter);
@@ -1852,7 +1904,7 @@ class CodeEmitterTask extends CompilerTask {
emitClassConstructor(classElement, builder);
emitSuper(superName, builder);
emitRuntimeName(runtimeName, builder);
- emitClassFields(classElement, builder, superName, onlyForRti: onlyForRti);
+ emitFields(classElement, builder, superName, onlyForRti: onlyForRti);
emitClassGettersSetters(classElement, builder);
if (!classElement.isMixinApplication) {
emitInstanceMembers(classElement, builder);
@@ -1882,8 +1934,7 @@ class CodeEmitterTask extends CompilerTask {
statics.write('{$n');
bool hasStatics = false;
ClassBuilder staticsBuilder = new ClassBuilder();
- if (emitClassFields(
- classElement, staticsBuilder, null, emitStatics: true)) {
+ if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) {
hasStatics = true;
statics.write('"":$_');
statics.write(
@@ -3611,6 +3662,27 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
if (compiler.enableMinification) {
mainBuffer.write(';');
}
+
+ for (Element element in elementBuffers.keys) {
+ // TODO(ahe): Should iterate over all libraries. Otherwise, we will
+ // not see libraries that only have fields.
+ if (element.isLibrary()) {
+ LibraryElement library = element;
+ ClassBuilder builder = new ClassBuilder();
+ if (emitFields(library, builder, null, emitStatics: true)) {
+ List<CodeBuffer> buffers = elementBuffers[library];
+ var buffer = buffers[0];
+ if (buffer == null) {
+ buffers[0] = buffer = new CodeBuffer();
+ }
+ for (jsAst.Property property in builder.properties) {
+ if (!buffer.isEmpty) buffer.write(',$n');
+ buffer.addBuffer(jsAst.prettyPrint(property, compiler));
+ }
+ }
+ }
+ }
+
if (!mangledFieldNames.isEmpty) {
var keys = mangledFieldNames.keys.toList();
keys.sort();
@@ -3907,6 +3979,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
var uri = data[1];
var metadata = data[2];
var descriptor = data[3];
+ var fields = descriptor && descriptor[""];
var classes = [];
var functions = [];
function processStatics(descriptor) {
@@ -3947,7 +4020,7 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
}
}
processStatics(descriptor);
- libraries.push([name, uri, classes, functions, metadata]);
+ libraries.push([name, uri, classes, functions, metadata, fields]);
}
})''';
}

Powered by Google App Engine
This is Rietveld 408576698