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

Unified Diff: lib/compiler/implementation/emitter.dart

Issue 10310060: Generate getters and setters dynamically. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebase Created 8 years, 7 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 | « no previous file | lib/compiler/implementation/namer.dart » ('j') | lib/compiler/implementation/namer.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/compiler/implementation/emitter.dart
diff --git a/lib/compiler/implementation/emitter.dart b/lib/compiler/implementation/emitter.dart
index 707638d806acb2111531b2466e6eceefa36a1e6a..4c6bdc360fe944067df45e4ab41a09d139ab9dc0 100644
--- a/lib/compiler/implementation/emitter.dart
+++ b/lib/compiler/implementation/emitter.dart
@@ -53,6 +53,8 @@ class CodeEmitterTask extends CompilerTask {
=> '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}';
String get defineClassFunction() {
+ final String GETTER = "getter";
+ final String SETTER = "setter";
// First the class name, then the super class name, followed by the fields
// (in an array) and the members (inside an Object literal).
// The caller can also pass in the constructor as a function if needed.
@@ -67,17 +69,35 @@ class CodeEmitterTask extends CompilerTask {
// },
// });
return """
-function(cls, superclass, fields, prototype) {
+function(cls, superclass, fields, fieldBitSet, prototype) {
ngeoffray 2012/05/09 08:50:28 Not sure we need the two following lines. They kin
floitsch 2012/05/09 13:58:36 inlined.
+ ${namer.getDynamicFieldGetterNamerFunction(GETTER)}
+ ${namer.getDynamicFieldSetterNamerFunction(SETTER)}
var constructor;
if (typeof fields == 'function') {
constructor = fields;
+ prototype = fieldBitSet;
ngeoffray 2012/05/09 08:50:28 This is starting to be confusing. Maybe add anothe
floitsch 2012/05/09 13:58:36 removed.
} else {
+ if (typeof fieldBitSet !== 'number') {
+ prototype = fieldBitSet;
+ fieldBitSet = 0;
+ }
var str = "(function " + cls + "(";
var body = "";
for (var i = 0; i < fields.length; i++) {
kasperl 2012/05/09 07:53:15 Have you thought about encoding the need for a get
floitsch 2012/05/09 13:58:36 Done.
if (i != 0) str += ", ";
- str += fields[i];
+ var field = fields[i];
+ str += field;
body += "this." + fields[i] + " = " + fields[i] + ";\\n";
kasperl 2012/05/09 07:53:15 Use field instead of fields[i].
floitsch 2012/05/09 13:58:36 Done.
+ if (fieldBitSet & 1) {
+ var getterString = "return this." + field + ";";
+ prototype[$GETTER(field)] = new Function(getterString);
+ }
+ fieldBitSet >>>= 1;
+ if (fieldBitSet & 1) {
+ var setterString = "this." + field + " = v;";
+ prototype[$SETTER(field)] = new Function("v", setterString);
+ }
+ fieldBitSet >>>= 1;
}
str += ") {" + body + "})";
constructor = eval(str);
sra1 2012/05/08 17:59:42 Should this new new Function too?
floitsch 2012/05/09 13:58:36 I would like it to be a "new Function", but then w
@@ -326,7 +346,26 @@ function() {
}
}
+ bool instanceFieldNeedsGetter(Element member) {
+ assert(member.kind === ElementKind.FIELD);
+ return compiler.universe.hasGetter(member, compiler);
+ }
+
+ bool instanceFieldNeedsSetter(Element member) {
+ assert(member.kind === ElementKind.FIELD);
+ return (member.modifiers === null || !member.modifiers.isFinal()) &&
+ compiler.universe.hasSetter(member, compiler);
kasperl 2012/05/09 07:53:15 The indentation seems a bit off here.
floitsch 2012/05/09 13:58:36 Done.
+ }
+
+ String compiledFieldName(Element member) {
+ assert(member.kind === ElementKind.FIELD);
+ return member.isNative()
+ ? member.name.slowToString()
+ : namer.getName(member);
+ }
+
void addInstanceMember(Element member,
+ Set<Element> dynamicallyEmittedFieldGettersSetters,
void defineInstanceMember(String invocationName,
String definition)) {
// TODO(floitsch): we don't need to deal with members of
@@ -352,21 +391,19 @@ function() {
} else if (member.kind === ElementKind.FIELD) {
// TODO(ngeoffray): Have another class generate the code for the
// fields.
ngeoffray 2012/05/09 08:50:28 Remove the TODO.
floitsch 2012/05/09 13:58:36 Done.
- if ((member.modifiers === null || !member.modifiers.isFinal()) &&
- compiler.universe.hasSetter(member, compiler)) {
- String setterName = namer.setterName(member.getLibrary(), member.name);
- String name = member.isNative()
- ? member.name.slowToString()
- : namer.getName(member);
- defineInstanceMember(setterName, "function(v) { this.$name = v; }");
- }
- if (compiler.universe.hasGetter(member, compiler)) {
+ if (instanceFieldNeedsGetter(member) &&
+ !dynamicallyEmittedFieldGettersSetters.contains(member)) {
String getterName = namer.getterName(member.getLibrary(), member.name);
- String name = member.isNative()
- ? member.name.slowToString()
- : namer.getName(member);
+ String name = compiledFieldName(member);
defineInstanceMember(getterName, "function() { return this.$name; }");
}
+
+ if (instanceFieldNeedsSetter(member) &&
+ !dynamicallyEmittedFieldGettersSetters.contains(member)) {
+ String setterName = namer.setterName(member.getLibrary(), member.name);
+ String name = compiledFieldName(member);
+ defineInstanceMember(setterName, "function(v) { this.$name = v; }");
+ }
} else {
compiler.internalError('unexpected kind: "${member.kind}"',
element: member);
@@ -374,18 +411,6 @@ function() {
emitExtraAccessors(member, defineInstanceMember);
}
- List<String> generateFieldList(ClassElement classElement) {
- List<String> result = <String>[];
- void addField(ClassElement enclosingClass, Element member) {
- result.add(namer.instanceFieldName(member.getLibrary(), member.name));
- }
-
- classElement.forEachInstanceField(addField,
- includeBackendMembers: true,
- includeSuperMembers: true);
- return result;
- }
-
void generateClass(ClassElement classElement, StringBuffer buffer) {
needsDefineClass = true;
@@ -406,20 +431,76 @@ function() {
superName = namer.getName(superclass);
}
String constructorName = namer.safeName(classElement.name.slowToString());
+
+ Set<Element> dynamicallyEmittedGettersSetters = new Set<Element>();
+
buffer.add('$defineClassName("$className", "$superName", ');
+
// If the class is never instantiated we still need to set it up for
// inheritance purposes, but we can simplify its JavaScript constructor.
- if (!compiler.universe.instantiatedClasses.contains(classElement)) {
- buffer.add("[]");
- } else {
- List<String> fields = generateFieldList(classElement);
- buffer.add('[');
- for (int i = 0; i < fields.length; i++) {
- if (i != 0) buffer.add(", ");
- buffer.add('"${fields[i]}"');
+ bool isInstantiated =
+ compiler.universe.instantiatedClasses.contains(classElement);
+
+ // We encode 15 field getters and setters in an integer bit field. This way
+ // the integer will fit into a JavaScript Smi.
+ // The getters and setters for the marked fields will be generated
+ // dynamically.
+ final int MAX_DYNAMICALLY_GENERATED_FIELD_GETTERS_SETTERS = 15;
+ int fieldBits = 0;
+ int fieldCounter = 0;
+ buffer.add('[');
+
+ void addField(ClassElement enclosingClass, Element member) {
+ assert(!member.isNative());
+ LibraryElement library = member.getLibrary();
+ SourceString name = member.name;
+ String fieldName = namer.instanceFieldName(library, name);
+ // See, if we can dynamically create getters and setters.
kasperl 2012/05/09 07:53:15 Remove , after See.
floitsch 2012/05/09 13:58:36 Done.
+ // We can only generate getters and setters for [classElement] since
+ // the fields of super classes could be overwritten with getters or
+ // setters.
+ bool needsDynamicGetter = false;
+ bool needsDynamicSetter = false;
+ if (fieldCounter <= MAX_DYNAMICALLY_GENERATED_FIELD_GETTERS_SETTERS &&
+ enclosingClass === classElement) {
+ needsDynamicGetter = instanceFieldNeedsGetter(member);
+ needsDynamicSetter = instanceFieldNeedsSetter(member);
+ // Make sure that the name we would generate dynamically matches the
+ // name we assign to the getter/setter during compilation time.
+ if ((needsDynamicGetter && namer.getterName(library, name) !=
kasperl 2012/05/09 07:53:15 Maybe add a helper on namer for this check (takes
floitsch 2012/05/09 13:58:36 code removed.
+ namer.dynamicGetterName(fieldName))
+ || (needsDynamicSetter && namer.setterName(library, name) !=
+ namer.dynamicSetterName(fieldName))) {
+ needsDynamicGetter = false;
+ needsDynamicSetter = false;
+ }
+ }
+
+ if (isInstantiated || needsDynamicGetter || needsDynamicSetter) {
+ fieldCounter++;
+ if (fieldCounter != 1) buffer.add(", ");
+ buffer.add('"$fieldName"');
+ if (needsDynamicGetter || needsDynamicSetter) {
+ dynamicallyEmittedGettersSetters.add(member);
+ if (needsDynamicGetter) {
+ fieldBits |= 1 << ((fieldCounter - 1) * 2);
+ }
+ if (needsDynamicSetter) {
+ fieldBits |= 1 << ((fieldCounter - 1) * 2 + 1);
+ }
+ }
}
- buffer.add(']');
}
+
+ // 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
+ // superclasses for non-instantiated classes.
+ classElement.forEachInstanceField(addField,
+ includeBackendMembers: true,
+ includeSuperMembers: isInstantiated);
+ buffer.add(']');
+ if (fieldBits != 0) buffer.add(', $fieldBits');
buffer.add(', {\n');
void defineInstanceMember(String name, String value) {
@@ -429,7 +510,8 @@ function() {
classElement.forEachMember(includeBackendMembers: true,
f: (ClassElement enclosing, Element member) {
if (member.isInstanceMember()) {
- addInstanceMember(member, defineInstanceMember);
+ addInstanceMember(
+ member, dynamicallyEmittedGettersSetters, defineInstanceMember);
}
});
« no previous file with comments | « no previous file | lib/compiler/implementation/namer.dart » ('j') | lib/compiler/implementation/namer.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698