Chromium Code Reviews| Index: lib/compiler/implementation/js_backend/emitter.dart |
| diff --git a/lib/compiler/implementation/js_backend/emitter.dart b/lib/compiler/implementation/js_backend/emitter.dart |
| index af68123c4b5a77d3b770b5fa4bc0ee1a0cf6b6d7..a3d32c512e7addf0d13304ade868e5ca9d6f2c51 100644 |
| --- a/lib/compiler/implementation/js_backend/emitter.dart |
| +++ b/lib/compiler/implementation/js_backend/emitter.dart |
| @@ -47,13 +47,11 @@ class CodeEmitterTask extends CompilerTask { |
| final bool generateSourceMap; |
| - CodeEmitterTask(Compiler compiler, Namer namer, |
| - [bool generateSourceMap = false]) |
| + CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) |
| : boundClosureBuffer = new CodeBuffer(), |
| mainBuffer = new CodeBuffer(), |
| this.namer = namer, |
| boundClosureCache = new Map<int, String>(), |
| - generateSourceMap = generateSourceMap, |
| constantEmitter = new ConstantEmitter(compiler, namer), |
| super(compiler) { |
| nativeEmitter = new NativeEmitter(this); |
| @@ -165,6 +163,8 @@ function(cls, fields, prototype) { |
| // On Firefox and Webkit browsers we can manipulate the __proto__ |
| // directly. Opera claims to have __proto__ support, but it is buggy. |
| // So we have to do more checks. |
| + // Opera bug was filed as DSK-370158, and fixed as CORE-47615 |
| + // (http://my.opera.com/desktopteam/blog/2012/07/20/more-12-01-fixes). |
| // If the browser does not support __proto__ we need to instantiate an |
| // object with the correct (internal) prototype set up correctly, and then |
| // copy the members. |
| @@ -620,23 +620,55 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| emitExtraAccessors(member, defineInstanceMember); |
| } |
| - String generateCheckedSetter(Element member, String fieldName) { |
| - DartType type = member.computeType(compiler); |
| - if (type.element.isTypeVariable() |
| - || type.element == compiler.dynamicClass |
| - || type.element == compiler.objectClass) { |
| - // TODO(ngeoffray): Support type checks on type parameters. |
| - return null; |
| - } else { |
| - SourceString helper = compiler.backend.getCheckedModeHelper(type); |
| - FunctionElement helperElement = compiler.findHelper(helper); |
| - String helperName = namer.isolateAccess(helperElement); |
| - String additionalArgument = ''; |
| - if (helperElement.computeSignature(compiler).parameterCount != 1) { |
| - additionalArgument = ", '${namer.operatorIs(type.element)}'"; |
| + /** |
| + * Documentation wanted -- johnniwinther |
| + * |
| + * Invariant: [classElement] must be a declaration element. |
| + */ |
| + void emitInstanceMembers(ClassElement classElement, |
|
floitsch
2012/10/17 21:15:01
emitInstanceMembers was just moved.
|
| + CodeBuffer buffer, |
| + bool needsLeadingComma) { |
| + assert(invariant(classElement, classElement.isDeclaration)); |
| + bool needsComma = needsLeadingComma; |
| + void defineInstanceMember(String name, CodeBuffer memberBuffer) { |
| + if (needsComma) buffer.add(','); |
| + needsComma = true; |
| + buffer.add('\n'); |
| + buffer.add(' $name: '); |
| + buffer.add(memberBuffer); |
| + } |
| + |
| + classElement.implementation.forEachMember( |
| + (ClassElement enclosing, Element member) { |
| + assert(invariant(classElement, member.isDeclaration)); |
| + if (member.isInstanceMember()) { |
| + addInstanceMember(member, defineInstanceMember); |
| + } |
| + }, |
| + includeBackendMembers: true); |
| + |
| + generateIsTestsOn(classElement, (ClassElement other) { |
| + String code; |
| + if (other.isObject(compiler)) return; |
| + if (nativeEmitter.requiresNativeIsCheck(other)) { |
| + code = 'function() { return true; }'; |
| + } else { |
| + code = 'true'; |
| + } |
| + CodeBuffer typeTestBuffer = new CodeBuffer(); |
| + typeTestBuffer.add(code); |
| + defineInstanceMember(namer.operatorIs(other), typeTestBuffer); |
| + }); |
| + |
| + if (identical(classElement, compiler.objectClass) |
| + && compiler.enabledNoSuchMethod) { |
| + // Emit the noSuchMethod handlers on the Object prototype now, |
| + // so that the code in the dynamicFunction helper can find |
| + // them. Note that this helper is invoked before analyzing the |
| + // full JS script. |
| + if (!nativeEmitter.handleNoSuchMethod) { |
| + emitNoSuchMethodHandlers(defineInstanceMember); |
| } |
| - return " set\$$fieldName: function(v) { " |
| - "this.$fieldName = $helperName(v$additionalArgument); }"; |
| } |
| } |
| @@ -645,16 +677,19 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| * |
| * Invariant: [classElement] must be a declaration element. |
| */ |
| - List<String> emitClassFields(ClassElement classElement, CodeBuffer buffer) { |
| + void visitClassFields(ClassElement classElement, |
| + void addField(Element member, |
| + String name, |
| + bool needsGetter, |
| + bool needsSetter, |
| + bool needsCheckedSetter)) { |
| assert(invariant(classElement, classElement.isDeclaration)); |
| // 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); |
| - List<String> checkedSetters = <String>[]; |
| - bool isFirstField = true; |
| - void addField(ClassElement enclosingClass, Element member) { |
| + void visitField(ClassElement enclosingClass, Element member) { |
| assert(!member.isNative()); |
| assert(invariant(classElement, member.isDeclaration)); |
| @@ -665,48 +700,36 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| // 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; |
| + bool needsGetter = false; |
| + bool needsSetter = false; |
| // We need to name shadowed fields differently, so they don't clash with |
| // the non-shadowed field. |
| bool isShadowed = false; |
| if (identical(enclosingClass, classElement)) { |
| - needsDynamicGetter = instanceFieldNeedsGetter(member); |
| - needsDynamicSetter = instanceFieldNeedsSetter(member); |
| + needsGetter = instanceFieldNeedsGetter(member); |
| + needsSetter = instanceFieldNeedsSetter(member); |
| } else { |
| isShadowed = classElement.isShadowedByField(member); |
| } |
| if ((isInstantiated && !enclosingClass.isNative()) |
| - || needsDynamicGetter |
| - || needsDynamicSetter) { |
| - if (isFirstField) { |
| - isFirstField = false; |
| - } else { |
| - buffer.add(", "); |
| - } |
| + || needsGetter |
| + || needsSetter) { |
| String fieldName = isShadowed |
| ? namer.shadowedFieldName(member) |
| : namer.getName(member); |
| - if (needsDynamicSetter && compiler.enableTypeAssertions) { |
| - String setter = generateCheckedSetter(member, fieldName); |
| - if (setter != null) { |
| - needsDynamicSetter = false; |
| - checkedSetters.add(setter); |
| - } |
| + bool needsCheckedSetter = false; |
| + if (needsSetter && compiler.enableTypeAssertions |
| + && canGenerateCheckedSetter(member)) { |
| + needsCheckedSetter = true; |
| + needsSetter = false; |
| } |
| // Getters and setters with suffixes will be generated dynamically. |
| - buffer.add('"$fieldName'); |
| - if (needsDynamicGetter || needsDynamicSetter) { |
| - if (needsDynamicGetter && needsDynamicSetter) { |
| - buffer.add(GETTER_SETTER_SUFFIX); |
| - } else if (needsDynamicGetter) { |
| - buffer.add(GETTER_SUFFIX); |
| - } else { |
| - buffer.add(SETTER_SUFFIX); |
| - } |
| - } |
| - buffer.add('"'); |
| + addField(member, |
| + fieldName, |
| + needsGetter, |
| + needsSetter, |
| + needsCheckedSetter); |
| } |
| } |
| @@ -715,62 +738,97 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| // allowed on fields that are in [classElement] we don't need to visit |
| // superclasses for non-instantiated classes. |
| classElement.implementation.forEachInstanceField( |
| - addField, |
| + visitField, |
| includeBackendMembers: true, |
| includeSuperMembers: isInstantiated && !classElement.isNative()); |
| - return checkedSetters; |
| } |
| - /** |
| - * Documentation wanted -- johnniwinther |
| - * |
| - * Invariant: [classElement] must be a declaration element. |
| - */ |
| - void emitInstanceMembers(ClassElement classElement, |
|
floitsch
2012/10/17 21:15:01
has been moved to above. No changes in this functi
|
| - CodeBuffer buffer, |
| - bool needsLeadingComma) { |
| - assert(invariant(classElement, classElement.isDeclaration)); |
| - bool needsComma = needsLeadingComma; |
| - void defineInstanceMember(String name, CodeBuffer memberBuffer) { |
| - if (needsComma) buffer.add(','); |
| - needsComma = true; |
| - buffer.add('\n'); |
| - buffer.add(' $name: '); |
| - buffer.add(memberBuffer); |
| + void generateGetter(Element member, String fieldName, CodeBuffer buffer) { |
| + String getterName = namer.getterName(member.getLibrary(), member.name); |
| + buffer.add("$getterName: function() { return this.$fieldName; }"); |
| + } |
| + |
| + void generateSetter(Element member, String fieldName, CodeBuffer buffer) { |
| + String setterName = namer.setterName(member.getLibrary(), member.name); |
| + buffer.add("$setterName: function(v) { this.$fieldName = v; }"); |
| + } |
| + |
| + bool canGenerateCheckedSetter(Element member) { |
| + DartType type = member.computeType(compiler); |
| + if (type.element.isTypeVariable() |
| + || type.element == compiler.dynamicClass |
| + || type.element == compiler.objectClass) { |
| + // TODO(ngeoffray): Support type checks on type parameters. |
| + return false; |
| } |
| + return true; |
| + } |
| - classElement.implementation.forEachMember( |
| - (ClassElement enclosing, Element member) { |
| - assert(invariant(classElement, member.isDeclaration)); |
| - if (member.isInstanceMember()) { |
| - addInstanceMember(member, defineInstanceMember); |
| - } |
| - }, |
| - includeBackendMembers: true); |
| + void generateCheckedSetter(Element member, |
|
floitsch
2012/10/17 21:15:01
generateCheckedSetter lost the boolean check which
|
| + String fieldName, |
| + CodeBuffer buffer) { |
| + assert(canGenerateCheckedSetter(member)); |
| + DartType type = member.computeType(compiler); |
| + SourceString helper = compiler.backend.getCheckedModeHelper(type); |
| + FunctionElement helperElement = compiler.findHelper(helper); |
| + String helperName = namer.isolateAccess(helperElement); |
| + String additionalArgument = ''; |
| + if (helperElement.computeSignature(compiler).parameterCount != 1) { |
| + additionalArgument = ", '${namer.operatorIs(type.element)}'"; |
| + } |
| + String setterName = namer.setterName(member.getLibrary(), member.name); |
| + buffer.add("$setterName: function(v) { " |
| + "this.$fieldName = $helperName(v$additionalArgument); }"); |
| + } |
| - generateIsTestsOn(classElement, (ClassElement other) { |
| - String code; |
| - if (other.isObject(compiler)) return; |
| - if (nativeEmitter.requiresNativeIsCheck(other)) { |
| - code = 'function() { return true; }'; |
| + void emitClassConstructor(ClassElement classElement, CodeBuffer buffer) { |
| + /* Do nothing. */ |
| + } |
| + |
| + void emitClassFields(ClassElement classElement, CodeBuffer buffer) { |
| + buffer.add('"": ['); |
| + bool isFirstField = true; |
| + visitClassFields(classElement, (Element member, |
| + String name, |
| + bool needsGetter, |
| + bool needsSetter, |
| + bool needsCheckedSetter) { |
| + if (isFirstField) { |
| + isFirstField = false; |
| } else { |
| - code = 'true'; |
| + buffer.add(", "); |
| } |
| - CodeBuffer typeTestBuffer = new CodeBuffer(); |
| - typeTestBuffer.add(code); |
| - defineInstanceMember(namer.operatorIs(other), typeTestBuffer); |
| + buffer.add('"$name'); |
| + if (needsGetter && needsSetter) { |
| + buffer.add(GETTER_SETTER_SUFFIX); |
| + } else if (needsGetter) { |
| + buffer.add(GETTER_SUFFIX); |
| + } else if (needsSetter) { |
| + buffer.add(SETTER_SUFFIX); |
| + } |
| + buffer.add('"'); |
| }); |
| + buffer.add(']'); |
| + } |
| - if (identical(classElement, compiler.objectClass) |
| - && compiler.enabledNoSuchMethod) { |
| - // Emit the noSuchMethod handlers on the Object prototype now, |
| - // so that the code in the dynamicFunction helper can find |
| - // them. Note that this helper is invoked before analyzing the |
| - // full JS script. |
| - if (!nativeEmitter.handleNoSuchMethod) { |
| - emitNoSuchMethodHandlers(defineInstanceMember); |
| + /** Each getter/setter must be prefixed with a ",\n ". */ |
| + void emitClassGettersSetters(ClassElement classElement, CodeBuffer buffer, |
| + {bool omitLeadingComma: false}) { |
| + visitClassFields(classElement, (Element member, |
| + String name, |
| + bool needsGetter, |
| + bool needsSetter, |
| + bool needsCheckedSetter) { |
| + if (needsCheckedSetter) { |
| + assert(!needsSetter); |
| + if (!omitLeadingComma) { |
| + buffer.add(",\n "); |
| + } else { |
| + omitLeadingComma = false; |
| + } |
| + generateCheckedSetter(member, name, buffer); |
| } |
| - } |
| + }); |
| } |
| /** |
| @@ -797,20 +855,15 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| if (superclass != null) { |
| superName = namer.getName(superclass); |
| } |
| - String constructorName = namer.safeName(classElement.name.slowToString()); |
| - buffer.add('$classesCollector.$className = {"":\n'); |
| - buffer.add(' ['); |
| - List<String> checkedSetters = emitClassFields(classElement, buffer); |
| - buffer.add('],\n'); |
| + buffer.add('$classesCollector.$className = {'); |
| + emitClassConstructor(classElement, buffer); |
| + emitClassFields(classElement, buffer); |
| // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'. |
| // That does currently not work because the native classes have a different |
| // syntax. |
| - buffer.add(' "super": "$superName"'); |
| - if (!checkedSetters.isEmpty()) { |
| - buffer.add(',\n'); |
| - buffer.add('${Strings.join(checkedSetters, ",\n")}'); |
| - } |
| + buffer.add(',\n "super": "$superName"'); |
| + emitClassGettersSetters(classElement, buffer); |
| emitInstanceMembers(classElement, buffer, true); |
| buffer.add('\n};\n\n'); |
| } |
| @@ -975,6 +1028,16 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| } |
| } |
| + void emitBoundClosureClassHeader(String mangledName, |
| + String superName, |
| + CodeBuffer buffer) { |
| + buffer.add(""" |
| +$classesCollector.$mangledName = {'': |
| +['self', 'target'], |
| +'super': '$superName', |
| +"""); |
| + } |
| + |
| /** |
| * Documentation wanted -- johnniwinther |
| * |
| @@ -1021,11 +1084,7 @@ function(prototype, staticName, fieldName, getterName, lazyValue) { |
| // Define the constructor with a name so that Object.toString can |
| // find the class name of the closure class. |
| - boundClosureBuffer.add(""" |
| -$classesCollector.$mangledName = {'': |
| - ['self', 'target'], |
| - 'super': '$superName', |
| -"""); |
| + emitBoundClosureClassHeader(mangledName, superName, boundClosureBuffer); |
| // Now add the methods on the closure class. The instance method does not |
| // have the correct name. Since [addParameterStubs] use the name to create |
| // its stubs we simply create a fake element with the correct name. |