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