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 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 |