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 part of js_backend; | 5 part of js_backend; |
6 | 6 |
7 /** | 7 /** |
8 * A function element that represents a closure call. The signature is copied | 8 * A function element that represents a closure call. The signature is copied |
9 * from the given element. | 9 * from the given element. |
10 */ | 10 */ |
(...skipping 26 matching lines...) Expand all Loading... | |
37 bool needsLazyInitializer = false; | 37 bool needsLazyInitializer = false; |
38 final Namer namer; | 38 final Namer namer; |
39 ConstantEmitter constantEmitter; | 39 ConstantEmitter constantEmitter; |
40 NativeEmitter nativeEmitter; | 40 NativeEmitter nativeEmitter; |
41 CodeBuffer boundClosureBuffer; | 41 CodeBuffer boundClosureBuffer; |
42 CodeBuffer mainBuffer; | 42 CodeBuffer mainBuffer; |
43 /** Shorter access to [isolatePropertiesName]. Both here in the code, as | 43 /** Shorter access to [isolatePropertiesName]. Both here in the code, as |
44 well as in the generated code. */ | 44 well as in the generated code. */ |
45 String isolateProperties; | 45 String isolateProperties; |
46 String classesCollector; | 46 String classesCollector; |
47 | |
48 /** | |
49 * A cache of closures that are used to closurize instance methods. | |
50 * A closure is dynamically bound to the instance used when closurization. | |
51 */ | |
47 final Map<int, String> boundClosureCache; | 52 final Map<int, String> boundClosureCache; |
53 | |
54 /** | |
55 * A cache of closures that are used to closurize instance methods | |
56 * of interceptors. Such closure is dynmically bound to the | |
57 * interceptor instance, and the actuall receiver of the method. | |
ahe
2012/11/13 12:42:41
actuall -> actual.
ngeoffray
2012/11/13 12:51:50
Done.
| |
58 */ | |
59 final Map<int, String> interceptorClosureCache; | |
48 Set<ClassElement> checkedClasses; | 60 Set<ClassElement> checkedClasses; |
49 | 61 |
50 final bool generateSourceMap; | 62 final bool generateSourceMap; |
51 | 63 |
52 CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) | 64 CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) |
53 : boundClosureBuffer = new CodeBuffer(), | 65 : boundClosureBuffer = new CodeBuffer(), |
54 mainBuffer = new CodeBuffer(), | 66 mainBuffer = new CodeBuffer(), |
55 this.namer = namer, | 67 this.namer = namer, |
56 boundClosureCache = new Map<int, String>(), | 68 boundClosureCache = new Map<int, String>(), |
69 interceptorClosureCache = new Map<int, String>(), | |
57 constantEmitter = new ConstantEmitter(compiler, namer), | 70 constantEmitter = new ConstantEmitter(compiler, namer), |
58 super(compiler) { | 71 super(compiler) { |
59 nativeEmitter = new NativeEmitter(this); | 72 nativeEmitter = new NativeEmitter(this); |
60 } | 73 } |
61 | 74 |
62 void computeRequiredTypeChecks() { | 75 void computeRequiredTypeChecks() { |
63 assert(checkedClasses == null); | 76 assert(checkedClasses == null); |
64 checkedClasses = new Set<ClassElement>(); | 77 checkedClasses = new Set<ClassElement>(); |
65 compiler.codegenWorld.isChecks.forEach((DartType t) { | 78 compiler.codegenWorld.isChecks.forEach((DartType t) { |
66 if (t is InterfaceType) checkedClasses.add(t.element); | 79 if (t is InterfaceType) checkedClasses.add(t.element); |
(...skipping 543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
610 /** | 623 /** |
611 * Documentation wanted -- johnniwinther | 624 * Documentation wanted -- johnniwinther |
612 * | 625 * |
613 * Invariant: [classElement] must be a declaration element. | 626 * Invariant: [classElement] must be a declaration element. |
614 */ | 627 */ |
615 void emitInstanceMembers(ClassElement classElement, | 628 void emitInstanceMembers(ClassElement classElement, |
616 CodeBuffer buffer, | 629 CodeBuffer buffer, |
617 bool needsLeadingComma) { | 630 bool needsLeadingComma) { |
618 assert(invariant(classElement, classElement.isDeclaration)); | 631 assert(invariant(classElement, classElement.isDeclaration)); |
619 bool needsComma = needsLeadingComma; | 632 bool needsComma = needsLeadingComma; |
620 void defineInstanceMember(String name, CodeBuffer memberBuffer) { | 633 void defineInstanceMember(String name, StringBuffer memberBuffer) { |
621 if (needsComma) buffer.add(','); | 634 if (needsComma) buffer.add(','); |
622 needsComma = true; | 635 needsComma = true; |
623 buffer.add('\n'); | 636 buffer.add('\n'); |
624 buffer.add(' $name: '); | 637 buffer.add(' $name: '); |
625 buffer.add(memberBuffer); | 638 buffer.add(memberBuffer); |
626 } | 639 } |
627 | 640 |
641 JavaScriptBackend backend = compiler.backend; | |
642 if (classElement == backend.objectInterceptorClass) { | |
643 emitInterceptorMethods(defineInstanceMember); | |
644 // The ObjectInterceptor does not have any instance methods. | |
645 return; | |
646 } | |
647 | |
628 classElement.implementation.forEachMember( | 648 classElement.implementation.forEachMember( |
629 (ClassElement enclosing, Element member) { | 649 (ClassElement enclosing, Element member) { |
630 assert(invariant(classElement, member.isDeclaration)); | 650 assert(invariant(classElement, member.isDeclaration)); |
631 if (member.isInstanceMember()) { | 651 if (member.isInstanceMember()) { |
632 addInstanceMember(member, defineInstanceMember); | 652 addInstanceMember(member, defineInstanceMember); |
633 } | 653 } |
634 }, | 654 }, |
635 includeBackendMembers: true); | 655 includeBackendMembers: true); |
636 | 656 |
637 generateIsTestsOn(classElement, (ClassElement other) { | 657 generateIsTestsOn(classElement, (ClassElement other) { |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
848 emitClassFields(classElement, buffer); | 868 emitClassFields(classElement, buffer); |
849 // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'. | 869 // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'. |
850 // That does currently not work because the native classes have a different | 870 // That does currently not work because the native classes have a different |
851 // syntax. | 871 // syntax. |
852 buffer.add(',\n "super": "$superName"'); | 872 buffer.add(',\n "super": "$superName"'); |
853 emitClassGettersSetters(classElement, buffer); | 873 emitClassGettersSetters(classElement, buffer); |
854 emitInstanceMembers(classElement, buffer, true); | 874 emitInstanceMembers(classElement, buffer, true); |
855 buffer.add('\n};\n\n'); | 875 buffer.add('\n};\n\n'); |
856 } | 876 } |
857 | 877 |
878 void emitInterceptorMethods( | |
879 void defineInstanceMember(String name, StringBuffer memberBuffer)) { | |
880 JavaScriptBackend backend = compiler.backend; | |
881 // Emit forwarders for the ObjectInterceptor class. We need to | |
882 // emit all possible sends on intercepted methods. | |
883 for (Selector selector in backend.usedInterceptors) { | |
884 String name; | |
885 String comma = ''; | |
886 String parameters = ''; | |
887 if (selector.isGetter()) { | |
888 name = backend.namer.getterName(selector.library, selector.name); | |
889 } else if (selector.isSetter()) { | |
890 name = backend.namer.setterName(selector.library, selector.name); | |
891 } else { | |
892 assert(selector.isCall()); | |
893 name = backend.namer.instanceMethodInvocationName( | |
894 selector.library, selector.name, selector); | |
895 if (selector.argumentCount > 0) { | |
896 comma = ', '; | |
897 int i = 0; | |
898 for (; i < selector.argumentCount - 1; i++) { | |
899 parameters = '${parameters}a$i, '; | |
900 } | |
901 parameters = '${parameters}a$i'; | |
902 } | |
903 } | |
904 StringBuffer body = new StringBuffer( | |
905 "function(receiver$comma$parameters) {" | |
906 " return receiver.$name($parameters); }"); | |
907 defineInstanceMember(name, body); | |
908 } | |
909 } | |
910 | |
858 /** | 911 /** |
859 * Generate "is tests" for [cls]: itself, and the "is tests" for the | 912 * Generate "is tests" for [cls]: itself, and the "is tests" for the |
860 * classes it implements. We don't need to add the "is tests" of the | 913 * classes it implements. We don't need to add the "is tests" of the |
861 * super class because they will be inherited at runtime. | 914 * super class because they will be inherited at runtime. |
862 */ | 915 */ |
863 void generateIsTestsOn(ClassElement cls, | 916 void generateIsTestsOn(ClassElement cls, |
864 void emitIsTest(ClassElement element)) { | 917 void emitIsTest(ClassElement element)) { |
865 if (checkedClasses.contains(cls)) { | 918 if (checkedClasses.contains(cls)) { |
866 emitIsTest(cls); | 919 emitIsTest(cls); |
867 } | 920 } |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1009 buffer.add('$fieldAccess.$name = $value;\n'); | 1062 buffer.add('$fieldAccess.$name = $value;\n'); |
1010 }); | 1063 }); |
1011 // If a static function is used as a closure we need to add its name | 1064 // If a static function is used as a closure we need to add its name |
1012 // in case it is used in spawnFunction. | 1065 // in case it is used in spawnFunction. |
1013 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; | 1066 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; |
1014 buffer.add('$fieldAccess.$fieldName = "$staticName";\n'); | 1067 buffer.add('$fieldAccess.$fieldName = "$staticName";\n'); |
1015 } | 1068 } |
1016 } | 1069 } |
1017 | 1070 |
1018 void emitBoundClosureClassHeader(String mangledName, | 1071 void emitBoundClosureClassHeader(String mangledName, |
1019 String superName, | 1072 String superName, |
1020 CodeBuffer buffer) { | 1073 String extraArgument, |
1074 CodeBuffer buffer) { | |
1075 extraArgument = extraArgument.isEmpty ? '' : ", '$extraArgument'"; | |
1021 buffer.add(""" | 1076 buffer.add(""" |
1022 $classesCollector.$mangledName = {'': | 1077 $classesCollector.$mangledName = {'': |
1023 ['self', 'target'], | 1078 ['self'$extraArgument, 'target'], |
1024 'super': '$superName', | 1079 'super': '$superName', |
1025 """); | 1080 """); |
1026 } | 1081 } |
1027 | 1082 |
1028 /** | 1083 /** |
1029 * Documentation wanted -- johnniwinther | 1084 * Documentation wanted -- johnniwinther |
1030 * | 1085 * |
1031 * Invariant: [member] must be a declaration element. | 1086 * Invariant: [member] must be a declaration element. |
1032 */ | 1087 */ |
1033 void emitDynamicFunctionGetter(FunctionElement member, | 1088 void emitDynamicFunctionGetter(FunctionElement member, |
(...skipping 16 matching lines...) Expand all Loading... | |
1050 | 1105 |
1051 // TODO(floitsch): share the closure classes with other classes | 1106 // TODO(floitsch): share the closure classes with other classes |
1052 // if they share methods with the same signature. Currently we do this only | 1107 // if they share methods with the same signature. Currently we do this only |
1053 // if there are no optional parameters. Closures with optional parameters | 1108 // if there are no optional parameters. Closures with optional parameters |
1054 // are more difficult to canonicalize because they would need to have the | 1109 // are more difficult to canonicalize because they would need to have the |
1055 // same default values. | 1110 // same default values. |
1056 | 1111 |
1057 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; | 1112 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
1058 int parameterCount = member.parameterCount(compiler); | 1113 int parameterCount = member.parameterCount(compiler); |
1059 | 1114 |
1115 Map<int, String> cache; | |
1116 String extraArg; | |
1117 String extraArgWithThis; | |
1118 String extraArgWithoutComma; | |
1119 // Methods on foreign classes take an extra parameter, which is | |
1120 // the actual receiver of the call. | |
1121 JavaScriptBackend backend = compiler.backend; | |
1122 if (backend.isInterceptorClass(member.getEnclosingClass())) { | |
1123 cache = interceptorClosureCache; | |
1124 extraArg = 'receiver, '; | |
1125 extraArgWithThis = 'this.receiver, '; | |
1126 extraArgWithoutComma = 'receiver'; | |
1127 } else { | |
1128 cache = boundClosureCache; | |
1129 extraArg = ''; | |
1130 extraArgWithoutComma = ''; | |
1131 extraArgWithThis = ''; | |
1132 } | |
1133 | |
1060 String closureClass = | 1134 String closureClass = |
1061 hasOptionalParameters ? null : boundClosureCache[parameterCount]; | 1135 hasOptionalParameters ? null : cache[parameterCount]; |
1062 if (closureClass == null) { | 1136 if (closureClass == null) { |
1063 // Either the class was not cached yet, or there are optional parameters. | 1137 // Either the class was not cached yet, or there are optional parameters. |
1064 // Create a new closure class. | 1138 // Create a new closure class. |
1065 SourceString name = const SourceString("BoundClosure"); | 1139 SourceString name = const SourceString("BoundClosure"); |
1066 ClassElement closureClassElement = new ClosureClassElement( | 1140 ClassElement closureClassElement = new ClosureClassElement( |
1067 name, compiler, member, member.getCompilationUnit()); | 1141 name, compiler, member, member.getCompilationUnit()); |
1068 String mangledName = namer.getName(closureClassElement); | 1142 String mangledName = namer.getName(closureClassElement); |
1069 String superName = namer.getName(closureClassElement.superclass); | 1143 String superName = namer.getName(closureClassElement.superclass); |
1070 needsClosureClass = true; | 1144 needsClosureClass = true; |
1071 | 1145 |
1072 // Define the constructor with a name so that Object.toString can | 1146 // Define the constructor with a name so that Object.toString can |
1073 // find the class name of the closure class. | 1147 // find the class name of the closure class. |
1074 emitBoundClosureClassHeader(mangledName, superName, boundClosureBuffer); | 1148 emitBoundClosureClassHeader( |
1149 mangledName, superName, extraArgWithoutComma, boundClosureBuffer); | |
1075 // Now add the methods on the closure class. The instance method does not | 1150 // Now add the methods on the closure class. The instance method does not |
1076 // have the correct name. Since [addParameterStubs] use the name to create | 1151 // have the correct name. Since [addParameterStubs] use the name to create |
1077 // its stubs we simply create a fake element with the correct name. | 1152 // its stubs we simply create a fake element with the correct name. |
1078 // Note: the callElement will not have any enclosingElement. | 1153 // Note: the callElement will not have any enclosingElement. |
1079 FunctionElement callElement = | 1154 FunctionElement callElement = |
1080 new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); | 1155 new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); |
1081 | 1156 |
1082 String invocationName = namer.instanceMethodName(callElement); | 1157 String invocationName = namer.instanceMethodName(callElement); |
1083 List<String> arguments = new List<String>(parameterCount); | 1158 List<String> arguments = new List<String>(parameterCount); |
1084 for (int i = 0; i < parameterCount; i++) { | 1159 for (int i = 0; i < parameterCount; i++) { |
1085 arguments[i] = "p$i"; | 1160 arguments[i] = "p$i"; |
1086 } | 1161 } |
1087 String joinedArgs = Strings.join(arguments, ", "); | 1162 String joinedArgs = Strings.join(arguments, ", "); |
1088 boundClosureBuffer.add( | 1163 boundClosureBuffer.add( |
1089 "$invocationName: function($joinedArgs) {"); | 1164 "$invocationName: function($joinedArgs) {"); |
1090 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); | 1165 boundClosureBuffer.add( |
1166 " return this.self[this.target]($extraArgWithThis$joinedArgs);"); | |
1091 boundClosureBuffer.add(" }"); | 1167 boundClosureBuffer.add(" }"); |
1092 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { | 1168 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { |
1093 boundClosureBuffer.add(',\n $stubName: $memberValue'); | 1169 boundClosureBuffer.add(',\n $stubName: $memberValue'); |
1094 }); | 1170 }); |
1095 boundClosureBuffer.add("\n};\n"); | 1171 boundClosureBuffer.add("\n};\n"); |
1096 | 1172 |
1097 closureClass = namer.isolateAccess(closureClassElement); | 1173 closureClass = namer.isolateAccess(closureClassElement); |
1098 | 1174 |
1099 // Cache it. | 1175 // Cache it. |
1100 if (!hasOptionalParameters) { | 1176 if (!hasOptionalParameters) { |
1101 boundClosureCache[parameterCount] = closureClass; | 1177 cache[parameterCount] = closureClass; |
1102 } | 1178 } |
1103 } | 1179 } |
1104 | 1180 |
1105 // And finally the getter. | 1181 // And finally the getter. |
1106 String getterName = namer.getterName(member.getLibrary(), member.name); | 1182 String getterName = namer.getterName(member.getLibrary(), member.name); |
1107 String targetName = namer.instanceMethodName(member); | 1183 String targetName = namer.instanceMethodName(member); |
1108 CodeBuffer getterBuffer = new CodeBuffer(); | 1184 CodeBuffer getterBuffer = new CodeBuffer(); |
1109 getterBuffer.add( | 1185 getterBuffer.add("function($extraArgWithoutComma) " |
1110 "function() { return new $closureClass(this, '$targetName'); }"); | 1186 "{ return new $closureClass(this, $extraArg'$targetName'); }"); |
1111 defineInstanceMember(getterName, getterBuffer); | 1187 defineInstanceMember(getterName, getterBuffer); |
1112 } | 1188 } |
1113 | 1189 |
1114 /** | 1190 /** |
1115 * Documentation wanted -- johnniwinther | 1191 * Documentation wanted -- johnniwinther |
1116 * | 1192 * |
1117 * Invariant: [member] must be a declaration element. | 1193 * Invariant: [member] must be a declaration element. |
1118 */ | 1194 */ |
1119 void emitCallStubForGetter(Element member, | 1195 void emitCallStubForGetter(Element member, |
1120 Set<Selector> selectors, | 1196 Set<Selector> selectors, |
(...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1584 const String HOOKS_API_USAGE = """ | 1660 const String HOOKS_API_USAGE = """ |
1585 // Generated by dart2js, the Dart to JavaScript compiler. | 1661 // Generated by dart2js, the Dart to JavaScript compiler. |
1586 // The code supports the following hooks: | 1662 // The code supports the following hooks: |
1587 // dartPrint(message) - if this function is defined it is called | 1663 // dartPrint(message) - if this function is defined it is called |
1588 // instead of the Dart [print] method. | 1664 // instead of the Dart [print] method. |
1589 // dartMainRunner(main) - if this function is defined, the Dart [main] | 1665 // dartMainRunner(main) - if this function is defined, the Dart [main] |
1590 // method will not be invoked directly. | 1666 // method will not be invoked directly. |
1591 // Instead, a closure that will invoke [main] is | 1667 // Instead, a closure that will invoke [main] is |
1592 // passed to [dartMainRunner]. | 1668 // passed to [dartMainRunner]. |
1593 """; | 1669 """; |
OLD | NEW |