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

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

Issue 19693011: Fix some bugs regarding native fields. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Remove debug code and add a comment 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 0104ffbcd1d36917c6f060d5e2c9481a0d52144b..c18371b2d6e9d1d24c856005590c60f396396d39 100644
--- a/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -77,6 +77,7 @@ class CodeEmitterTask extends CompilerTask {
String isolateProperties;
String classesCollector;
final Set<ClassElement> neededClasses = new Set<ClassElement>();
+ final Set<ClassElement> rtiNeededClasses = new Set<ClassElement>();
final List<ClassElement> regularClasses = <ClassElement>[];
final List<ClassElement> deferredClasses = <ClassElement>[];
final List<ClassElement> nativeClasses = <ClassElement>[];
@@ -1678,7 +1679,9 @@ class CodeEmitterTask extends CompilerTask {
ClassBuilder builder,
String superName,
{ bool classIsNative: false,
- bool emitStatics: false }) {
+ bool emitStatics: false,
+ bool onlyForRti: false }) {
+ assert(!emitStatics || !onlyForRti);
StringBuffer buffer = new StringBuffer();
if (emitStatics) {
assert(invariant(classElement, superName == null, message: superName));
@@ -1698,75 +1701,86 @@ class CodeEmitterTask extends CompilerTask {
var fieldMetadata = [];
bool hasMetadata = false;
- visitClassFields(classElement, emitStatics,
- (Element member,
- 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-
- // generate the constructor. For native classes there are no
- // constructors, so we don't need the fields unless we are generating
- // accessors at runtime.
- if (!classIsNative || needsAccessor) {
- buffer.write(separator);
- separator = ',';
- var metadata = buildMetadataFunction(member);
- if (metadata != null) {
- hasMetadata = true;
- } else {
- metadata = new jsAst.LiteralNull();
- }
- fieldMetadata.add(metadata);
- recordMangledField(member, accessorName, member.name.slowToString());
- if (!needsAccessor) {
- // Emit field for constructor generation.
- assert(!classIsNative);
- buffer.write(name);
- } else {
- // Emit (possibly renaming) field name so we can add accessors at
- // runtime.
- buffer.write(accessorName);
- if (name != accessorName) {
- buffer.write(':$name');
- // 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; }
- 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(!member.isInstanceMember() || getterCode != 0);
- }
- 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; }
- setterCode += backend.fieldHasInterceptedSetter(member) ? 2 : 0;
- setterCode += backend.isInterceptorClass(classElement) ? 0 : 1;
- assert(!member.isInstanceMember() || setterCode != 0);
+ if (!onlyForRti) {
+ visitClassFields(classElement, emitStatics,
+ (Element member,
+ 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-
+ // generate the constructor. For native classes there are no
+ // constructors, so we don't need the fields unless we are generating
+ // accessors at runtime.
+ if (!classIsNative || needsAccessor) {
+ buffer.write(separator);
+ separator = ',';
+ var metadata = buildMetadataFunction(member);
+ if (metadata != null) {
+ hasMetadata = true;
+ } else {
+ metadata = new jsAst.LiteralNull();
}
- int code = getterCode + (setterCode << 2);
- if (code == 0) {
- compiler.reportInternalError(
- member, 'Internal error: code is 0 ($classElement/$member)');
+ fieldMetadata.add(metadata);
+ recordMangledField(member, accessorName, member.name.slowToString());
+ if (!needsAccessor) {
+ // Emit field for constructor generation.
+ assert(!classIsNative);
+ buffer.write(name);
} else {
- buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]);
+ // Emit (possibly renaming) field name so we can add accessors at
+ // runtime.
+ buffer.write(accessorName);
+ if (name != accessorName) {
+ buffer.write(':$name');
+ // 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);
+ } 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);
+ } else {
+ setterCode = 1;
+ }
+ }
+ int code = getterCode + (setterCode << 2);
+ if (code == 0) {
+ compiler.reportInternalError(
+ member, 'Internal error: code is 0 ($classElement/$member)');
+ } else {
+ buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]);
+ }
}
}
- }
- });
+ });
+ }
bool fieldsAdded = buffer.length > bufferClassLength;
String compactClassData = buffer.toString();
@@ -1812,8 +1826,10 @@ class CodeEmitterTask extends CompilerTask {
* Invariant: [classElement] must be a declaration element.
*/
void generateClass(ClassElement classElement, CodeBuffer buffer) {
+ final onlyForRti = rtiNeededClasses.contains(classElement);
+
assert(invariant(classElement, classElement.isDeclaration));
- assert(invariant(classElement, !classElement.isNative()));
+ assert(invariant(classElement, !classElement.isNative() || onlyForRti));
needsDefineClass = true;
String className = namer.getName(classElement);
@@ -1836,7 +1852,7 @@ class CodeEmitterTask extends CompilerTask {
emitClassConstructor(classElement, builder);
emitSuper(superName, builder);
emitRuntimeName(runtimeName, builder);
- emitClassFields(classElement, builder, superName);
+ emitClassFields(classElement, builder, superName, onlyForRti: onlyForRti);
emitClassGettersSetters(classElement, builder);
if (!classElement.isMixinApplication) {
emitInstanceMembers(classElement, builder);
@@ -3093,46 +3109,13 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
.toSet();
neededClasses.addAll(mixinClasses);
- // 3a. Add classes that are referenced by type arguments or substitutions in
- // argument checks.
- // TODO(karlklose): merge this case with 3b when unifying argument and
- // object checks.
- RuntimeTypes rti = backend.rti;
- backend.rti.getRequiredArgumentClasses(backend).forEach((ClassElement c) {
- // Types that we represent with JS native types (like int and String) do
- // not need a class definition as we use the interceptor classes instead.
- if (!rti.isJsNative(c)) {
- addClassWithSuperclasses(c);
- }
- });
-
- // 3b. Add classes that are referenced by substitutions in object checks and
- // their superclasses.
- TypeChecks requiredChecks =
- backend.rti.computeChecks(neededClasses, checkedClasses);
- Set<ClassElement> classesUsedInSubstitutions =
- rti.getClassesUsedInSubstitutions(backend, requiredChecks);
- addClassesWithSuperclasses(classesUsedInSubstitutions);
-
- // 3c. Add classes that contain checked generic function types. These are
- // needed to store the signature encoding.
- for (FunctionType type in checkedFunctionTypes) {
- ClassElement contextClass = Types.getClassContext(type);
- if (contextClass != null) {
- neededClasses.add(contextClass);
- }
- }
-
- // 4. Finally, sort the classes.
- List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses);
-
- // If we need noSuchMethod support, we run through all needed
+ // 3. If we need noSuchMethod support, we run through all needed
// classes to figure out if we need the support on any native
// class. If so, we let the native emitter deal with it.
if (compiler.enabledNoSuchMethod) {
SourceString noSuchMethodName = Compiler.NO_SUCH_METHOD;
Selector noSuchMethodSelector = compiler.noSuchMethodSelector;
- for (ClassElement element in sortedClasses) {
+ for (ClassElement element in neededClasses) {
if (!element.isNative()) continue;
Element member = element.lookupLocalMember(noSuchMethodName);
if (member == null) continue;
@@ -3143,8 +3126,25 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
}
}
+ // 4. Find all classes needed for rti.
+ // It is important that this is the penultimate step, at this point,
+ // neededClasses must only contain classes that have been resolved and
+ // codegen'd. The rtiNeededClasses may contain additional classes, but
+ // these are thought to not have been instantiated, so we neeed to be able
+ // to identify them later and make sure we only emit "empty shells" without
+ // fields, etc.
+ computeRtiNeededClasses();
+ rtiNeededClasses.removeAll(neededClasses);
+ // rtiNeededClasses now contains only the "empty shells".
+ neededClasses.addAll(rtiNeededClasses);
+
+ // 5. Finally, sort the classes.
+ List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses);
+
for (ClassElement element in sortedClasses) {
- if (element.isNative()) {
+ if (rtiNeededClasses.contains(element)) {
+ regularClasses.add(element);
+ } else if (element.isNative()) {
// For now, native classes cannot be deferred.
nativeClasses.add(element);
} else if (isDeferred(element)) {
@@ -3155,6 +3155,58 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
}
}
+ Set<ClassElement> computeRtiNeededClasses() {
+ void addClassWithSuperclasses(ClassElement cls) {
+ if (cls.name == const SourceString('CssStyleDeclaration')) {
+ throw 'hest';
+ }
+ rtiNeededClasses.add(cls);
+ for (ClassElement superclass = cls.superclass;
+ superclass != null;
+ superclass = superclass.superclass) {
+ rtiNeededClasses.add(superclass);
+ }
+ }
+
+ void addClassesWithSuperclasses(Iterable<ClassElement> classes) {
+ for (ClassElement cls in classes) {
+ addClassWithSuperclasses(cls);
+ }
+ }
+
+ // 1. Add classes that are referenced by type arguments or substitutions in
+ // argument checks.
+ // TODO(karlklose): merge this case with 2 when unifying argument and
+ // object checks.
+ RuntimeTypes rti = backend.rti;
+ rti.getRequiredArgumentClasses(backend).forEach((ClassElement c) {
+ // Types that we represent with JS native types (like int and String) do
+ // not need a class definition as we use the interceptor classes instead.
+ if (!rti.isJsNative(c)) {
+ addClassWithSuperclasses(c);
+ }
+ });
+
+ // 2. Add classes that are referenced by substitutions in object checks and
+ // their superclasses.
+ TypeChecks requiredChecks =
+ rti.computeChecks(rtiNeededClasses, checkedClasses);
+ Set<ClassElement> classesUsedInSubstitutions =
+ rti.getClassesUsedInSubstitutions(backend, requiredChecks);
+ addClassesWithSuperclasses(classesUsedInSubstitutions);
+
+ // 3. Add classes that contain checked generic function types. These are
+ // needed to store the signature encoding.
+ for (FunctionType type in checkedFunctionTypes) {
+ ClassElement contextClass = Types.getClassContext(type);
+ if (contextClass != null) {
+ rtiNeededClasses.add(contextClass);
+ }
+ }
+
+ return rtiNeededClasses;
+ }
+
// Optimize performance critical one shot interceptors.
jsAst.Statement tryOptimizeOneShotInterceptor(Selector selector,
Set<ClassElement> classes) {

Powered by Google App Engine
This is Rietveld 408576698