Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * A function element that represents a closure call. The signature is copied | 6 * A function element that represents a closure call. The signature is copied |
| 7 * from the given element. | 7 * from the given element. |
| 8 */ | 8 */ |
| 9 class ClosureInvocationElement extends FunctionElement { | 9 class ClosureInvocationElement extends FunctionElement { |
| 10 ClosureInvocationElement(SourceString name, | 10 ClosureInvocationElement(SourceString name, |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 39 CodeBuffer boundClosureBuffer; | 39 CodeBuffer boundClosureBuffer; |
| 40 CodeBuffer mainBuffer; | 40 CodeBuffer mainBuffer; |
| 41 /** Shorter access to [isolatePropertiesName]. Both here in the code, as | 41 /** Shorter access to [isolatePropertiesName]. Both here in the code, as |
| 42 well as in the generated code. */ | 42 well as in the generated code. */ |
| 43 String isolateProperties; | 43 String isolateProperties; |
| 44 String classesCollector; | 44 String classesCollector; |
| 45 final Map<int, String> boundClosureCache; | 45 final Map<int, String> boundClosureCache; |
| 46 Set<ClassElement> checkedClasses; | 46 Set<ClassElement> checkedClasses; |
| 47 | 47 |
| 48 final bool generateSourceMap; | 48 final bool generateSourceMap; |
| 49 final bool useContentSecurityPolicy; | |
| 49 | 50 |
| 50 CodeEmitterTask(Compiler compiler, Namer namer, | 51 CodeEmitterTask(Compiler compiler, Namer namer, |
| 51 [bool generateSourceMap = false]) | 52 this.generateSourceMap, |
| 53 this.useContentSecurityPolicy) | |
| 52 : boundClosureBuffer = new CodeBuffer(), | 54 : boundClosureBuffer = new CodeBuffer(), |
| 53 mainBuffer = new CodeBuffer(), | 55 mainBuffer = new CodeBuffer(), |
| 54 this.namer = namer, | 56 this.namer = namer, |
| 55 boundClosureCache = new Map<int, String>(), | 57 boundClosureCache = new Map<int, String>(), |
| 56 generateSourceMap = generateSourceMap, | |
| 57 constantEmitter = new ConstantEmitter(compiler, namer), | 58 constantEmitter = new ConstantEmitter(compiler, namer), |
| 58 super(compiler) { | 59 super(compiler) { |
| 59 nativeEmitter = new NativeEmitter(this); | 60 nativeEmitter = new NativeEmitter(this); |
| 60 } | 61 } |
| 61 | 62 |
| 62 void computeRequiredTypeChecks() { | 63 void computeRequiredTypeChecks() { |
| 63 assert(checkedClasses == null); | 64 assert(checkedClasses == null); |
| 64 checkedClasses = new Set<ClassElement>(); | 65 checkedClasses = new Set<ClassElement>(); |
| 65 compiler.codegenWorld.isChecks.forEach((DartType t) { | 66 compiler.codegenWorld.isChecks.forEach((DartType t) { |
| 66 if (t is InterfaceType) checkedClasses.add(t.element); | 67 if (t is InterfaceType) checkedClasses.add(t.element); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 91 String get supportsProtoName | 92 String get supportsProtoName |
| 92 => 'supportsProto'; | 93 => 'supportsProto'; |
| 93 String get lazyInitializerName | 94 String get lazyInitializerName |
| 94 => '${namer.ISOLATE}.\$lazy'; | 95 => '${namer.ISOLATE}.\$lazy'; |
| 95 | 96 |
| 96 final String GETTER_SUFFIX = "?"; | 97 final String GETTER_SUFFIX = "?"; |
| 97 final String SETTER_SUFFIX = "!"; | 98 final String SETTER_SUFFIX = "!"; |
| 98 final String GETTER_SETTER_SUFFIX = "="; | 99 final String GETTER_SETTER_SUFFIX = "="; |
| 99 | 100 |
| 100 String get generateGetterSetterFunction { | 101 String get generateGetterSetterFunction { |
| 102 if (useContentSecurityPolicy) { | |
| 103 compiler.internalError("Dynamic Getters/Setters are unused in CSP"); | |
|
kasperl
2012/10/17 09:37:11
Getters/Setters -> getters/setters
floitsch
2012/10/17 21:15:01
Refactored.
| |
| 104 } | |
| 105 | |
| 101 return """ | 106 return """ |
| 102 function(field, prototype) { | 107 function(field, prototype) { |
| 103 var len = field.length; | 108 var len = field.length; |
| 104 var lastChar = field[len - 1]; | 109 var lastChar = field[len - 1]; |
| 105 var needsGetter = lastChar == '$GETTER_SUFFIX' || lastChar == '$GETTER_SETTE R_SUFFIX'; | 110 var needsGetter = lastChar == '$GETTER_SUFFIX' || lastChar == '$GETTER_SETTE R_SUFFIX'; |
| 106 var needsSetter = lastChar == '$SETTER_SUFFIX' || lastChar == '$GETTER_SETTE R_SUFFIX'; | 111 var needsSetter = lastChar == '$SETTER_SUFFIX' || lastChar == '$GETTER_SETTE R_SUFFIX'; |
| 107 if (needsGetter || needsSetter) field = field.substring(0, len - 1); | 112 if (needsGetter || needsSetter) field = field.substring(0, len - 1); |
| 108 if (needsGetter) { | 113 if (needsGetter) { |
| 109 var getterString = "return this." + field + ";"; | 114 var getterString = "return this." + field + ";"; |
| 110 """ | 115 """ |
| 111 /* The supportsProtoCheck below depends on the getter/setter convention. | 116 /* The supportsProtoCheck below depends on the getter/setter convention. |
| 112 When changing here, update the protoCheck too. */ | 117 When changing here, update the protoCheck too. */ |
| 113 """ | 118 """ |
| 114 prototype["get\$" + field] = new Function(getterString); | 119 prototype["get\$" + field] = new Function(getterString); |
| 115 } | 120 } |
| 116 if (needsSetter) { | 121 if (needsSetter) { |
| 117 var setterString = "this." + field + " = v;"; | 122 var setterString = "this." + field + " = v;"; |
| 118 prototype["set\$" + field] = new Function("v", setterString); | 123 prototype["set\$" + field] = new Function("v", setterString); |
| 119 } | 124 } |
| 120 return field; | 125 return field; |
| 121 }"""; | 126 }"""; |
| 122 } | 127 } |
| 123 | 128 |
| 124 String get defineClassFunction { | 129 String get defineClassFunction { |
| 130 if (useContentSecurityPolicy) { | |
|
kasperl
2012/10/17 09:37:11
It feels a little bit like subclassing would help
floitsch
2012/10/17 21:15:01
Done.
| |
| 131 return """ | |
| 132 function(cls, constructor, prototype) { | |
| 133 constructor.prototype = prototype; | |
| 134 return constructor; | |
| 135 }"""; | |
| 136 } | |
| 125 // First the class name, then the super class name, followed by the fields | 137 // First the class name, then the super class name, followed by the fields |
| 126 // (in an array) and the members (inside an Object literal). | 138 // (in an array) and the members (inside an Object literal). |
| 127 // The caller can also pass in the constructor as a function if needed. | 139 // The caller can also pass in the constructor as a function if needed. |
| 128 // | 140 // |
| 129 // Example: | 141 // Example: |
| 130 // defineClass("A", "B", ["x", "y"], { | 142 // defineClass("A", "B", ["x", "y"], { |
| 131 // foo$1: function(y) { | 143 // foo$1: function(y) { |
| 132 // print(this.x + y); | 144 // print(this.x + y); |
| 133 // }, | 145 // }, |
| 134 // bar$2: function(t, v) { | 146 // bar$2: function(t, v) { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 155 str += "return " + cls + ";"; | 167 str += "return " + cls + ";"; |
| 156 constructor = new Function(str)(); | 168 constructor = new Function(str)(); |
| 157 } | 169 } |
| 158 constructor.prototype = prototype; | 170 constructor.prototype = prototype; |
| 159 return constructor; | 171 return constructor; |
| 160 }"""; | 172 }"""; |
| 161 } | 173 } |
| 162 | 174 |
| 163 /** Needs defineClass to be defined. */ | 175 /** Needs defineClass to be defined. */ |
| 164 String get protoSupportCheck { | 176 String get protoSupportCheck { |
| 177 if (useContentSecurityPolicy) { | |
| 178 // We don't modify the prototypes in CSP mode. Therefore we can have an | |
| 179 // easier prototype-check. | |
| 180 return 'var $supportsProtoName = !!{}.__proto__;'; | |
| 181 } | |
| 165 // On Firefox and Webkit browsers we can manipulate the __proto__ | 182 // On Firefox and Webkit browsers we can manipulate the __proto__ |
| 166 // directly. Opera claims to have __proto__ support, but it is buggy. | 183 // directly. Opera claims to have __proto__ support, but it is buggy. |
| 167 // So we have to do more checks. | 184 // So we have to do more checks. |
| 185 // Opera bug was filed as DSK-370158, and fixed as CORE-47615 | |
| 186 // (http://my.opera.com/desktopteam/blog/2012/07/20/more-12-01-fixes). | |
| 168 // If the browser does not support __proto__ we need to instantiate an | 187 // If the browser does not support __proto__ we need to instantiate an |
| 169 // object with the correct (internal) prototype set up correctly, and then | 188 // object with the correct (internal) prototype set up correctly, and then |
| 170 // copy the members. | 189 // copy the members. |
| 171 | 190 |
| 172 return ''' | 191 return ''' |
| 173 var $supportsProtoName = false; | 192 var $supportsProtoName = false; |
| 174 var tmp = $defineClassName('c', ['f?'], {}).prototype; | 193 var tmp = $defineClassName('c', ['f?'], {}).prototype; |
| 175 if (tmp.__proto__) { | 194 if (tmp.__proto__) { |
| 176 tmp.__proto__ = {}; | 195 tmp.__proto__ = {}; |
| 177 if (typeof tmp.get\$f !== "undefined") $supportsProtoName = true; | 196 if (typeof tmp.get\$f !== "undefined") $supportsProtoName = true; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 235 newPrototype[member] = prototype[member]; | 254 newPrototype[member] = prototype[member]; |
| 236 } | 255 } |
| 237 } | 256 } |
| 238 } | 257 } |
| 239 } | 258 } |
| 240 for (var cls in pendingClasses) finishClass(cls); | 259 for (var cls in pendingClasses) finishClass(cls); |
| 241 }'''; | 260 }'''; |
| 242 } | 261 } |
| 243 | 262 |
| 244 String get finishIsolateConstructorFunction { | 263 String get finishIsolateConstructorFunction { |
| 264 if (useContentSecurityPolicy) { | |
| 265 // We replace the old Isolate function with a new one that initializes | |
| 266 // all its field with the initial (and often final) value of all globals. | |
| 267 // | |
| 268 // We also copy over old values like the prototype, and the | |
| 269 // isolateProperties themselves. | |
| 270 return """ | |
| 271 function(oldIsolate) { | |
| 272 var isolateProperties = oldIsolate.${namer.ISOLATE_PROPERTIES}; | |
|
kasperl
2012/10/17 09:37:11
Isn't there a way to construct the isolate object
floitsch
2012/10/17 21:15:01
There is, but since it's not necessary.
| |
| 273 function Isolate() { | |
| 274 for (var staticName in isolateProperties) { | |
| 275 if (Object.prototype.hasOwnProperty.call(isolateProperties, staticName)) { | |
| 276 this[staticName] = isolateProperties[staticName]; | |
| 277 } | |
| 278 } | |
| 279 // Use the newly created object as prototype. In Chrome this creates a | |
|
floitsch
2012/10/16 18:15:36
Thanks to Erik Corry for this tip. Without these l
| |
| 280 // hidden class for the object and makes sure it is fast to access. | |
| 281 function t(){} | |
|
kasperl
2012/10/17 09:37:11
Maybe give t a better name? Space after ).
floitsch
2012/10/17 21:15:01
Done.
| |
| 282 t.prototype = this; | |
| 283 new t(); | |
| 284 } | |
| 285 Isolate.prototype = oldIsolate.prototype; | |
| 286 Isolate.prototype.constructor = Isolate; | |
| 287 Isolate.${namer.ISOLATE_PROPERTIES} = isolateProperties; | |
| 288 return Isolate; | |
| 289 }"""; | |
| 290 } | |
| 291 | |
| 245 String isolate = namer.ISOLATE; | 292 String isolate = namer.ISOLATE; |
| 246 // We replace the old Isolate function with a new one that initializes | 293 // We replace the old Isolate function with a new one that initializes |
| 247 // all its field with the initial (and often final) value of all globals. | 294 // all its field with the initial (and often final) value of all globals. |
| 248 // This has two advantages: | 295 // This has two advantages: |
| 249 // 1. the properties are in the object itself (thus avoiding to go through | 296 // 1. the properties are in the object itself (thus avoiding to go through |
| 250 // the prototype when looking up globals. | 297 // the prototype when looking up globals. |
| 251 // 2. a new isolate goes through a (usually well optimized) constructor | 298 // 2. a new isolate goes through a (usually well optimized) constructor |
| 252 // function of the form: "function() { this.x = ...; this.y = ...; }". | 299 // function of the form: "function() { this.x = ...; this.y = ...; }". |
| 253 // | 300 // |
| 254 // Example: If [isolateProperties] is an object containing: x = 3 and | 301 // Example: If [isolateProperties] is an object containing: x = 3 and |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 613 if (!parameters.optionalParameters.isEmpty()) { | 660 if (!parameters.optionalParameters.isEmpty()) { |
| 614 addParameterStubs(member, defineInstanceMember); | 661 addParameterStubs(member, defineInstanceMember); |
| 615 } | 662 } |
| 616 } else if (!member.isField()) { | 663 } else if (!member.isField()) { |
| 617 compiler.internalError('unexpected kind: "${member.kind}"', | 664 compiler.internalError('unexpected kind: "${member.kind}"', |
| 618 element: member); | 665 element: member); |
| 619 } | 666 } |
| 620 emitExtraAccessors(member, defineInstanceMember); | 667 emitExtraAccessors(member, defineInstanceMember); |
| 621 } | 668 } |
| 622 | 669 |
| 670 String generateGetter(Element member, String fieldName) { | |
| 671 String getterName = namer.getterName(member.getLibrary(), member.name); | |
| 672 return "$getterName: function() { return this.$fieldName; }"; | |
| 673 } | |
| 674 | |
| 675 String generateSetter(Element member, String fieldName) { | |
| 676 String setterName = namer.setterName(member.getLibrary(), member.name); | |
| 677 return "$setterName: function(v) { this.$fieldName = v; }"; | |
| 678 } | |
| 679 | |
| 623 String generateCheckedSetter(Element member, String fieldName) { | 680 String generateCheckedSetter(Element member, String fieldName) { |
| 624 DartType type = member.computeType(compiler); | 681 DartType type = member.computeType(compiler); |
| 625 if (type.element.isTypeVariable() | 682 if (type.element.isTypeVariable() |
| 626 || type.element == compiler.dynamicClass | 683 || type.element == compiler.dynamicClass |
| 627 || type.element == compiler.objectClass) { | 684 || type.element == compiler.objectClass) { |
| 628 // TODO(ngeoffray): Support type checks on type parameters. | 685 // TODO(ngeoffray): Support type checks on type parameters. |
| 629 return null; | 686 return null; |
| 630 } else { | 687 } else { |
| 631 SourceString helper = compiler.backend.getCheckedModeHelper(type); | 688 SourceString helper = compiler.backend.getCheckedModeHelper(type); |
| 632 FunctionElement helperElement = compiler.findHelper(helper); | 689 FunctionElement helperElement = compiler.findHelper(helper); |
| 633 String helperName = namer.isolateAccess(helperElement); | 690 String helperName = namer.isolateAccess(helperElement); |
| 634 String additionalArgument = ''; | 691 String additionalArgument = ''; |
| 635 if (helperElement.computeSignature(compiler).parameterCount != 1) { | 692 if (helperElement.computeSignature(compiler).parameterCount != 1) { |
| 636 additionalArgument = ", '${namer.operatorIs(type.element)}'"; | 693 additionalArgument = ", '${namer.operatorIs(type.element)}'"; |
| 637 } | 694 } |
| 638 return " set\$$fieldName: function(v) { " | 695 String setterName = namer.setterName(member.getLibrary(), member.name); |
| 696 return " $setterName: function(v) { " | |
| 639 "this.$fieldName = $helperName(v$additionalArgument); }"; | 697 "this.$fieldName = $helperName(v$additionalArgument); }"; |
| 640 } | 698 } |
| 641 } | 699 } |
| 642 | 700 |
| 643 /** | 701 /** |
| 644 * Documentation wanted -- johnniwinther | 702 * Documentation wanted -- johnniwinther |
| 645 * | 703 * |
| 646 * Invariant: [classElement] must be a declaration element. | 704 * Invariant: [classElement] must be a declaration element. |
| 647 */ | 705 */ |
| 648 List<String> emitClassFields(ClassElement classElement, CodeBuffer buffer) { | 706 void visitClassFields(ClassElement classElement, |
| 707 void addField(Element member, | |
| 708 String name, | |
| 709 bool needsGetter, | |
| 710 bool needsSetter, | |
| 711 bool needsCheckedSetter)) { | |
| 649 assert(invariant(classElement, classElement.isDeclaration)); | 712 assert(invariant(classElement, classElement.isDeclaration)); |
| 650 // If the class is never instantiated we still need to set it up for | 713 // If the class is never instantiated we still need to set it up for |
| 651 // inheritance purposes, but we can simplify its JavaScript constructor. | 714 // inheritance purposes, but we can simplify its JavaScript constructor. |
| 652 bool isInstantiated = | 715 bool isInstantiated = |
| 653 compiler.codegenWorld.instantiatedClasses.contains(classElement); | 716 compiler.codegenWorld.instantiatedClasses.contains(classElement); |
| 654 List<String> checkedSetters = <String>[]; | |
| 655 | 717 |
| 656 bool isFirstField = true; | 718 void visitField(ClassElement enclosingClass, Element member) { |
| 657 void addField(ClassElement enclosingClass, Element member) { | |
| 658 assert(!member.isNative()); | 719 assert(!member.isNative()); |
| 659 assert(invariant(classElement, member.isDeclaration)); | 720 assert(invariant(classElement, member.isDeclaration)); |
| 660 | 721 |
| 661 LibraryElement library = member.getLibrary(); | 722 LibraryElement library = member.getLibrary(); |
| 662 SourceString name = member.name; | 723 SourceString name = member.name; |
| 663 bool isPrivate = name.isPrivate(); | 724 bool isPrivate = name.isPrivate(); |
| 664 // See if we can dynamically create getters and setters. | 725 // See if we can dynamically create getters and setters. |
| 665 // We can only generate getters and setters for [classElement] since | 726 // We can only generate getters and setters for [classElement] since |
| 666 // the fields of super classes could be overwritten with getters or | 727 // the fields of super classes could be overwritten with getters or |
| 667 // setters. | 728 // setters. |
| 668 bool needsDynamicGetter = false; | 729 bool needsGetter = false; |
| 669 bool needsDynamicSetter = false; | 730 bool needsSetter = false; |
| 670 // We need to name shadowed fields differently, so they don't clash with | 731 // We need to name shadowed fields differently, so they don't clash with |
| 671 // the non-shadowed field. | 732 // the non-shadowed field. |
| 672 bool isShadowed = false; | 733 bool isShadowed = false; |
| 673 if (enclosingClass === classElement) { | 734 if (enclosingClass === classElement) { |
| 674 needsDynamicGetter = instanceFieldNeedsGetter(member); | 735 needsGetter = instanceFieldNeedsGetter(member); |
| 675 needsDynamicSetter = instanceFieldNeedsSetter(member); | 736 needsSetter = instanceFieldNeedsSetter(member); |
| 676 } else { | 737 } else { |
| 677 isShadowed = classElement.isShadowedByField(member); | 738 isShadowed = classElement.isShadowedByField(member); |
| 678 } | 739 } |
| 679 | 740 |
| 680 if ((isInstantiated && !enclosingClass.isNative()) | 741 if ((isInstantiated && !enclosingClass.isNative()) |
| 681 || needsDynamicGetter | 742 || needsGetter |
| 682 || needsDynamicSetter) { | 743 || needsSetter) { |
| 683 if (isFirstField) { | |
| 684 isFirstField = false; | |
| 685 } else { | |
| 686 buffer.add(", "); | |
| 687 } | |
| 688 String fieldName = isShadowed | 744 String fieldName = isShadowed |
| 689 ? namer.shadowedFieldName(member) | 745 ? namer.shadowedFieldName(member) |
| 690 : namer.getName(member); | 746 : namer.getName(member); |
| 691 if (needsDynamicSetter && compiler.enableTypeAssertions) { | 747 bool needsCheckedSetter = false; |
| 748 if (needsSetter && compiler.enableTypeAssertions) { | |
| 692 String setter = generateCheckedSetter(member, fieldName); | 749 String setter = generateCheckedSetter(member, fieldName); |
| 693 if (setter != null) { | 750 if (setter != null) { |
| 694 needsDynamicSetter = false; | 751 needsSetter = false; |
| 695 checkedSetters.add(setter); | 752 needsCheckedSetter = true; |
| 696 } | 753 } |
| 697 } | 754 } |
| 698 // Getters and setters with suffixes will be generated dynamically. | 755 // Getters and setters with suffixes will be generated dynamically. |
| 699 buffer.add('"$fieldName'); | 756 addField(member, |
| 700 if (needsDynamicGetter || needsDynamicSetter) { | 757 fieldName, |
| 701 if (needsDynamicGetter && needsDynamicSetter) { | 758 needsGetter, |
| 702 buffer.add(GETTER_SETTER_SUFFIX); | 759 needsSetter, |
| 703 } else if (needsDynamicGetter) { | 760 needsCheckedSetter); |
| 704 buffer.add(GETTER_SUFFIX); | |
| 705 } else { | |
| 706 buffer.add(SETTER_SUFFIX); | |
| 707 } | |
| 708 } | |
| 709 buffer.add('"'); | |
| 710 } | 761 } |
| 711 } | 762 } |
| 712 | 763 |
| 713 // If a class is not instantiated then we add the field just so we can | 764 // If a class is not instantiated then we add the field just so we can |
| 714 // generate the field getter/setter dynamically. Since this is only | 765 // generate the field getter/setter dynamically. Since this is only |
| 715 // allowed on fields that are in [classElement] we don't need to visit | 766 // allowed on fields that are in [classElement] we don't need to visit |
| 716 // superclasses for non-instantiated classes. | 767 // superclasses for non-instantiated classes. |
| 717 classElement.implementation.forEachInstanceField( | 768 classElement.implementation.forEachInstanceField( |
| 718 addField, | 769 visitField, |
| 719 includeBackendMembers: true, | 770 includeBackendMembers: true, |
| 720 includeSuperMembers: isInstantiated && !classElement.isNative()); | 771 includeSuperMembers: isInstantiated && !classElement.isNative()); |
| 772 } | |
| 773 | |
| 774 List<String> emitClassFields(ClassElement classElement, CodeBuffer buffer) { | |
| 775 buffer.add(' ['); | |
| 776 bool isFirstField = true; | |
| 777 List<String> checkedSetters = <String>[]; | |
| 778 visitClassFields(classElement, (Element member, | |
| 779 String name, | |
| 780 bool needsGetter, | |
| 781 bool needsSetter, | |
| 782 bool needsCheckedSetter) { | |
| 783 if (isFirstField) { | |
| 784 isFirstField = false; | |
| 785 } else { | |
| 786 buffer.add(", "); | |
| 787 } | |
| 788 if (needsCheckedSetter) { | |
| 789 assert(!needsSetter); | |
| 790 String setter = generateCheckedSetter(member, name); | |
| 791 if (setter !== null) { | |
| 792 needsSetter = null; | |
| 793 } else { | |
| 794 checkedSetters.add(setter); | |
| 795 } | |
| 796 } | |
| 797 buffer.add('"$name'); | |
| 798 if (needsGetter && needsSetter) { | |
| 799 buffer.add(GETTER_SETTER_SUFFIX); | |
| 800 } else if (needsGetter) { | |
| 801 buffer.add(GETTER_SUFFIX); | |
| 802 } else if (needsSetter) { | |
| 803 buffer.add(SETTER_SUFFIX); | |
| 804 } | |
| 805 buffer.add('"'); | |
| 806 }); | |
| 807 buffer.add(']'); | |
| 721 return checkedSetters; | 808 return checkedSetters; |
| 722 } | 809 } |
| 723 | 810 |
| 724 /** | 811 /** |
| 812 * This is the equivalent of [emitClassFields] when running under the | |
| 813 * Content-Security Policy. | |
| 814 */ | |
| 815 List<String> emitClassConstructorGettersSetters(ClassElement classElement, | |
| 816 CodeBuffer buffer) { | |
| 817 // Say we have a class A with fields b, c and d, where c needs a getter and | |
| 818 // d needs both a getter and a setter. Then we produce: | |
| 819 // - a constructor (directly into the given [buffer]): | |
| 820 // function A(b, c, d) { this.b = b, this.c = c, this.d = d; } | |
| 821 // - getters and setters (stored in the [explicitGettersSetters] list): | |
| 822 // get$c : function() { return this.c; } | |
| 823 // get$d : function() { return this.d; } | |
| 824 // set$d : function(x) { this.d = x; } | |
| 825 List<String> explicitGettersSetters = <String>[]; | |
| 826 List<String> fields = <String>[]; | |
| 827 visitClassFields(classElement, (Element member, | |
| 828 String name, | |
| 829 bool needsGetter, | |
| 830 bool needsSetter, | |
| 831 bool needsCheckedSetter) { | |
| 832 fields.add(name); | |
| 833 if (needsCheckedSetter) { | |
| 834 assert(!needsSetter); | |
| 835 String setter = generateCheckedSetter(member, name); | |
| 836 if (setter !== null) { | |
| 837 needsSetter = null; | |
| 838 } else { | |
| 839 explicitGettersSetters.add(setter); | |
| 840 } | |
| 841 } | |
| 842 if (needsGetter) { | |
| 843 explicitGettersSetters.add(generateGetter(member, name)); | |
| 844 } | |
| 845 if (needsSetter) { | |
| 846 explicitGettersSetters.add(generateSetter(member, name)); | |
| 847 } | |
| 848 }); | |
| 849 | |
| 850 String constructorName = namer.safeName(classElement.name.slowToString()); | |
| 851 // Generate the constructor. | |
| 852 buffer.add("function $constructorName("); | |
| 853 buffer.add(Strings.join(fields, ", ")); | |
| 854 buffer.add(") {"); | |
| 855 for (String field in fields) { | |
| 856 buffer.add(" this.$field = $field;"); | |
| 857 } | |
| 858 buffer.add(' }'); | |
| 859 return explicitGettersSetters; | |
| 860 } | |
| 861 | |
| 862 /** | |
| 725 * Documentation wanted -- johnniwinther | 863 * Documentation wanted -- johnniwinther |
| 726 * | 864 * |
| 727 * Invariant: [classElement] must be a declaration element. | 865 * Invariant: [classElement] must be a declaration element. |
| 728 */ | 866 */ |
| 729 void emitInstanceMembers(ClassElement classElement, | 867 void emitInstanceMembers(ClassElement classElement, |
| 730 CodeBuffer buffer, | 868 CodeBuffer buffer, |
| 731 bool needsLeadingComma) { | 869 bool needsLeadingComma) { |
| 732 assert(invariant(classElement, classElement.isDeclaration)); | 870 assert(invariant(classElement, classElement.isDeclaration)); |
| 733 bool needsComma = needsLeadingComma; | 871 bool needsComma = needsLeadingComma; |
| 734 void defineInstanceMember(String name, CodeBuffer memberBuffer) { | 872 void defineInstanceMember(String name, CodeBuffer memberBuffer) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 789 buffer = mainBuffer; | 927 buffer = mainBuffer; |
| 790 } | 928 } |
| 791 | 929 |
| 792 needsDefineClass = true; | 930 needsDefineClass = true; |
| 793 String className = namer.getName(classElement); | 931 String className = namer.getName(classElement); |
| 794 ClassElement superclass = classElement.superclass; | 932 ClassElement superclass = classElement.superclass; |
| 795 String superName = ""; | 933 String superName = ""; |
| 796 if (superclass !== null) { | 934 if (superclass !== null) { |
| 797 superName = namer.getName(superclass); | 935 superName = namer.getName(superclass); |
| 798 } | 936 } |
| 799 String constructorName = namer.safeName(classElement.name.slowToString()); | |
| 800 | 937 |
| 801 buffer.add('$classesCollector.$className = {"":\n'); | 938 buffer.add('$classesCollector.$className = {"":\n'); |
| 802 buffer.add(' ['); | 939 List<String> explicitGettersSetters; |
| 803 List<String> checkedSetters = emitClassFields(classElement, buffer); | 940 if (useContentSecurityPolicy) { |
| 804 buffer.add('],\n'); | 941 explicitGettersSetters = |
| 942 emitClassConstructorGettersSetters(classElement, buffer); | |
| 943 } else { | |
| 944 explicitGettersSetters = emitClassFields(classElement, buffer); | |
| 945 } | |
| 805 // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'. | 946 // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'. |
| 806 // That does currently not work because the native classes have a different | 947 // That does currently not work because the native classes have a different |
| 807 // syntax. | 948 // syntax. |
| 808 buffer.add(' "super": "$superName"'); | 949 buffer.add(',\n "super": "$superName"'); |
| 809 if (!checkedSetters.isEmpty()) { | 950 if (!explicitGettersSetters.isEmpty()) { |
| 810 buffer.add(',\n'); | 951 buffer.add(',\n'); |
| 811 buffer.add('${Strings.join(checkedSetters, ",\n")}'); | 952 buffer.add('${Strings.join(explicitGettersSetters, ",\n")}'); |
| 812 } | 953 } |
| 813 emitInstanceMembers(classElement, buffer, true); | 954 emitInstanceMembers(classElement, buffer, true); |
| 814 buffer.add('\n};\n\n'); | 955 buffer.add('\n};\n\n'); |
| 815 } | 956 } |
| 816 | 957 |
| 817 /** | 958 /** |
| 818 * Generate "is tests" for [cls]: itself, and the "is tests" for the | 959 * Generate "is tests" for [cls]: itself, and the "is tests" for the |
| 819 * classes it implements. We don't need to add the "is tests" of the | 960 * classes it implements. We don't need to add the "is tests" of the |
| 820 * super class because they will be inherited at runtime. | 961 * super class because they will be inherited at runtime. |
| 821 */ | 962 */ |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1013 // Create a new closure class. | 1154 // Create a new closure class. |
| 1014 SourceString name = const SourceString("BoundClosure"); | 1155 SourceString name = const SourceString("BoundClosure"); |
| 1015 ClassElement closureClassElement = new ClosureClassElement( | 1156 ClassElement closureClassElement = new ClosureClassElement( |
| 1016 name, compiler, member, member.getCompilationUnit()); | 1157 name, compiler, member, member.getCompilationUnit()); |
| 1017 String mangledName = namer.getName(closureClassElement); | 1158 String mangledName = namer.getName(closureClassElement); |
| 1018 String superName = namer.getName(closureClassElement.superclass); | 1159 String superName = namer.getName(closureClassElement.superclass); |
| 1019 needsClosureClass = true; | 1160 needsClosureClass = true; |
| 1020 | 1161 |
| 1021 // Define the constructor with a name so that Object.toString can | 1162 // Define the constructor with a name so that Object.toString can |
| 1022 // find the class name of the closure class. | 1163 // find the class name of the closure class. |
| 1023 boundClosureBuffer.add(""" | 1164 if (useContentSecurityPolicy) { |
| 1165 boundClosureBuffer.add(""" | |
| 1166 $classesCollector.$mangledName = {'': | |
| 1167 function $mangledName(self, target) { this.self = self; this.target = target; }, | |
| 1168 'super': '$superName', | |
| 1169 """); | |
| 1170 } else { | |
| 1171 boundClosureBuffer.add(""" | |
| 1024 $classesCollector.$mangledName = {'': | 1172 $classesCollector.$mangledName = {'': |
| 1025 ['self', 'target'], | 1173 ['self', 'target'], |
| 1026 'super': '$superName', | 1174 'super': '$superName', |
| 1027 """); | 1175 """); |
| 1176 } | |
| 1028 // Now add the methods on the closure class. The instance method does not | 1177 // Now add the methods on the closure class. The instance method does not |
| 1029 // have the correct name. Since [addParameterStubs] use the name to create | 1178 // have the correct name. Since [addParameterStubs] use the name to create |
| 1030 // its stubs we simply create a fake element with the correct name. | 1179 // its stubs we simply create a fake element with the correct name. |
| 1031 // Note: the callElement will not have any enclosingElement. | 1180 // Note: the callElement will not have any enclosingElement. |
| 1032 FunctionElement callElement = | 1181 FunctionElement callElement = |
| 1033 new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); | 1182 new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); |
| 1034 | 1183 |
| 1035 String invocationName = namer.instanceMethodName(callElement); | 1184 String invocationName = namer.instanceMethodName(callElement); |
| 1036 List<String> arguments = new List<String>(parameterCount); | 1185 List<String> arguments = new List<String>(parameterCount); |
| 1037 for (int i = 0; i < parameterCount; i++) { | 1186 for (int i = 0; i < parameterCount; i++) { |
| (...skipping 475 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1513 const String HOOKS_API_USAGE = """ | 1662 const String HOOKS_API_USAGE = """ |
| 1514 // Generated by dart2js, the Dart to JavaScript compiler. | 1663 // Generated by dart2js, the Dart to JavaScript compiler. |
| 1515 // The code supports the following hooks: | 1664 // The code supports the following hooks: |
| 1516 // dartPrint(message) - if this function is defined it is called | 1665 // dartPrint(message) - if this function is defined it is called |
| 1517 // instead of the Dart [print] method. | 1666 // instead of the Dart [print] method. |
| 1518 // dartMainRunner(main) - if this function is defined, the Dart [main] | 1667 // dartMainRunner(main) - if this function is defined, the Dart [main] |
| 1519 // method will not be invoked directly. | 1668 // method will not be invoked directly. |
| 1520 // Instead, a closure that will invoke [main] is | 1669 // Instead, a closure that will invoke [main] is |
| 1521 // passed to [dartMainRunner]. | 1670 // passed to [dartMainRunner]. |
| 1522 """; | 1671 """; |
| OLD | NEW |