| 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,
|
| + 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,
|
| - 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,
|
| + 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.
|
|
|