Changeset 242591 in webkit
- Timestamp:
- Mar 6, 2019 9:09:53 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r242569 r242591 1 2019-03-06 Mark Lam <mark.lam@apple.com> 2 3 Fix incorrect handling of try-finally completion values. 4 https://bugs.webkit.org/show_bug.cgi?id=195131 5 <rdar://problem/46222079> 6 7 Reviewed by Saam Barati and Yusuke Suzuki. 8 9 Added many permutations of new test case to test-finally.js. test-finally.js has 10 been run on Chrome and Firefox as a sanity check, and we confirmed that all the 11 tests passes there as well. 12 13 * stress/test-finally.js: 14 1 15 2019-03-06 Saam Barati <sbarati@apple.com> 2 16 -
trunk/JSTests/stress/test-finally.js
r221137 r242591 726 726 }, "a,b,c,y,d,z,f", NothingReturned, 600); 727 727 728 test(() => { 729 append("a"); 730 try { 731 append("b"); 732 throw 100; 733 } finally { 734 append("c"); 735 try { 736 append("d"); 737 } finally { 738 append("e"); 739 } 740 append("f"); 741 } 742 append("g"); 743 return 700; 744 745 }, "a,b,c,d,e,f", NothingReturned, 100); 746 747 test(() => { 748 append("a"); 749 try { 750 append("b"); 751 return 100; 752 } finally { 753 append("c"); 754 try { 755 append("d"); 756 } finally { 757 append("e"); 758 } 759 append("f"); 760 } 761 append("g"); 762 return 700; 763 764 }, "a,b,c,d,e,f", 100, NothingThrown); 765 766 test(() => { 767 append("a"); 768 label: try { 769 append("b"); 770 for (var j = 0; j < 1; j++) { 771 append("x"); 772 break label; 773 } 774 } finally { 775 append("c"); 776 try { 777 append("d"); 778 } finally { 779 append("e"); 780 } 781 append("f"); 782 } 783 append("g"); 784 return 700; 785 786 }, "a,b,x,c,d,e,f,g", 700, NothingThrown); 787 788 test(() => { 789 append("a"); 790 try { 791 append("b"); 792 throw 100; 793 } finally { 794 append("c"); 795 for (var j = 0; j < 1; j++) { 796 append("y"); 797 try { 798 append("d"); 799 } finally { 800 append("e"); 801 throw 200; 802 } 803 append("f"); 804 } 805 append("z"); 806 } 807 append("g"); 808 return 700; 809 810 }, "a,b,c,y,d,e", NothingReturned, 200); 811 812 test(() => { 813 append("a"); 814 try { 815 append("b"); 816 throw 100; 817 } finally { 818 append("c"); 819 for (var j = 0; j < 1; j++) { 820 append("y"); 821 try { 822 append("d"); 823 } finally { 824 append("e"); 825 return 200; 826 } 827 append("f"); 828 } 829 append("z"); 830 } 831 append("g"); 832 return 700; 833 834 }, "a,b,c,y,d,e", 200, NothingThrown); 835 836 test(() => { 837 append("a"); 838 try { 839 append("b"); 840 throw 100; 841 } finally { 842 append("c"); 843 for (var j = 0; j < 1; j++) { 844 append("y"); 845 try { 846 append("d"); 847 } finally { 848 append("e"); 849 continue; 850 } 851 append("f"); 852 } 853 append("z"); 854 } 855 append("g"); 856 return 700; 857 858 }, "a,b,c,y,d,e,z", NothingReturned, 100); 859 860 test(() => { 861 append("a"); 862 try { 863 append("b"); 864 return 100; 865 } finally { 866 append("c"); 867 for (var j = 0; j < 1; j++) { 868 append("y"); 869 try { 870 append("d"); 871 } finally { 872 append("e"); 873 throw 200; 874 } 875 append("f"); 876 } 877 append("z"); 878 } 879 append("g"); 880 return 700; 881 882 }, "a,b,c,y,d,e", NothingReturned, 200); 883 884 test(() => { 885 append("a"); 886 try { 887 append("b"); 888 return 100; 889 } finally { 890 append("c"); 891 for (var j = 0; j < 1; j++) { 892 append("y"); 893 try { 894 append("d"); 895 } finally { 896 append("e"); 897 return 200; 898 } 899 append("f"); 900 } 901 append("z"); 902 } 903 append("g"); 904 return 700; 905 906 }, "a,b,c,y,d,e", 200, NothingThrown); 907 908 test(() => { 909 append("a"); 910 try { 911 append("b"); 912 return 100; 913 } finally { 914 append("c"); 915 for (var j = 0; j < 1; j++) { 916 append("y"); 917 try { 918 append("d"); 919 } finally { 920 append("e"); 921 continue; 922 } 923 append("f"); 924 } 925 append("z"); 926 } 927 append("g"); 928 return 700; 929 930 }, "a,b,c,y,d,e,z", 100, NothingThrown); 931 932 test(() => { 933 append("a"); 934 try { 935 append("b"); 936 throw 100; 937 } finally { 938 append("c"); 939 for (var j = 0; j < 1; j++) { 940 append("y"); 941 try { 942 append("d"); 943 throw 42; 944 } finally { 945 append("e"); 946 throw 200; 947 } 948 append("f"); 949 } 950 append("z"); 951 } 952 append("g"); 953 return 700; 954 955 }, "a,b,c,y,d,e", NothingReturned, 200); 956 957 test(() => { 958 append("a"); 959 try { 960 append("b"); 961 throw 100; 962 } finally { 963 append("c"); 964 for (var j = 0; j < 1; j++) { 965 append("y"); 966 try { 967 append("d"); 968 throw 42; 969 } finally { 970 append("e"); 971 return 200; 972 } 973 append("f"); 974 } 975 append("z"); 976 } 977 append("g"); 978 return 700; 979 980 }, "a,b,c,y,d,e", 200, NothingThrown); 981 982 test(() => { 983 append("a"); 984 try { 985 append("b"); 986 throw 100; 987 } finally { 988 append("c"); 989 for (var j = 0; j < 1; j++) { 990 append("y"); 991 try { 992 append("d"); 993 throw 42; 994 } finally { 995 append("e"); 996 continue; 997 } 998 append("f"); 999 } 1000 append("z"); 1001 } 1002 append("g"); 1003 return 700; 1004 1005 }, "a,b,c,y,d,e,z", NothingReturned, 100); 1006 1007 test(() => { 1008 append("a"); 1009 try { 1010 append("b"); 1011 return 100; 1012 } finally { 1013 append("c"); 1014 for (var j = 0; j < 1; j++) { 1015 append("y"); 1016 try { 1017 append("d"); 1018 throw 42; 1019 } finally { 1020 append("e"); 1021 throw 200; 1022 } 1023 append("f"); 1024 } 1025 append("z"); 1026 } 1027 append("g"); 1028 return 700; 1029 1030 }, "a,b,c,y,d,e", NothingReturned, 200); 1031 1032 test(() => { 1033 append("a"); 1034 try { 1035 append("b"); 1036 return 100; 1037 } finally { 1038 append("c"); 1039 for (var j = 0; j < 1; j++) { 1040 append("y"); 1041 try { 1042 append("d"); 1043 throw 42; 1044 } finally { 1045 append("e"); 1046 return 200; 1047 } 1048 append("f"); 1049 } 1050 append("z"); 1051 } 1052 append("g"); 1053 return 700; 1054 1055 }, "a,b,c,y,d,e", 200, NothingThrown); 1056 1057 test(() => { 1058 append("a"); 1059 try { 1060 append("b"); 1061 return 100; 1062 } finally { 1063 append("c"); 1064 for (var j = 0; j < 1; j++) { 1065 append("y"); 1066 try { 1067 append("d"); 1068 throw 42; 1069 } finally { 1070 append("e"); 1071 continue; 1072 } 1073 append("f"); 1074 } 1075 append("z"); 1076 } 1077 append("g"); 1078 return 700; 1079 1080 }, "a,b,c,y,d,e,z", 100, NothingThrown); 1081 1082 test(() => { 1083 append("a"); 1084 try { 1085 append("b"); 1086 throw 100; 1087 } finally { 1088 append("c"); 1089 for (var j = 0; j < 1; j++) { 1090 append("y"); 1091 try { 1092 append("d"); 1093 return 42; 1094 } finally { 1095 append("e"); 1096 throw 200; 1097 } 1098 append("f"); 1099 } 1100 append("z"); 1101 } 1102 append("g"); 1103 return 700; 1104 1105 }, "a,b,c,y,d,e", NothingReturned, 200); 1106 1107 test(() => { 1108 append("a"); 1109 try { 1110 append("b"); 1111 throw 100; 1112 } finally { 1113 append("c"); 1114 for (var j = 0; j < 1; j++) { 1115 append("y"); 1116 try { 1117 append("d"); 1118 return 42; 1119 } finally { 1120 append("e"); 1121 return 200; 1122 } 1123 append("f"); 1124 } 1125 append("z"); 1126 } 1127 append("g"); 1128 return 700; 1129 1130 }, "a,b,c,y,d,e", 200, NothingThrown); 1131 1132 test(() => { 1133 append("a"); 1134 try { 1135 append("b"); 1136 throw 100; 1137 } finally { 1138 append("c"); 1139 for (var j = 0; j < 1; j++) { 1140 append("y"); 1141 try { 1142 append("d"); 1143 return 42; 1144 } finally { 1145 append("e"); 1146 continue; 1147 } 1148 append("f"); 1149 } 1150 append("z"); 1151 } 1152 append("g"); 1153 return 700; 1154 1155 }, "a,b,c,y,d,e,z", NothingReturned, 100); 1156 1157 test(() => { 1158 append("a"); 1159 try { 1160 append("b"); 1161 return 100; 1162 } finally { 1163 append("c"); 1164 for (var j = 0; j < 1; j++) { 1165 append("y"); 1166 try { 1167 append("d"); 1168 return 42; 1169 } finally { 1170 append("e"); 1171 throw 200; 1172 } 1173 append("f"); 1174 } 1175 append("z"); 1176 } 1177 append("g"); 1178 return 700; 1179 1180 }, "a,b,c,y,d,e", NothingReturned, 200); 1181 1182 test(() => { 1183 append("a"); 1184 try { 1185 append("b"); 1186 return 100; 1187 } finally { 1188 append("c"); 1189 for (var j = 0; j < 1; j++) { 1190 append("y"); 1191 try { 1192 append("d"); 1193 return 42; 1194 } finally { 1195 append("e"); 1196 return 200; 1197 } 1198 append("f"); 1199 } 1200 append("z"); 1201 } 1202 append("g"); 1203 return 700; 1204 1205 }, "a,b,c,y,d,e", 200, NothingThrown); 1206 1207 test(() => { 1208 append("a"); 1209 try { 1210 append("b"); 1211 return 100; 1212 } finally { 1213 append("c"); 1214 for (var j = 0; j < 1; j++) { 1215 append("y"); 1216 try { 1217 append("d"); 1218 return 42; 1219 } finally { 1220 append("e"); 1221 continue; 1222 } 1223 append("f"); 1224 } 1225 append("z"); 1226 } 1227 append("g"); 1228 return 700; 1229 1230 }, "a,b,c,y,d,e,z", 100, NothingThrown); 1231 1232 test(() => { 1233 append("a"); 1234 try { 1235 append("b"); 1236 throw 100; 1237 } finally { 1238 append("c"); 1239 for (var j = 0; j < 1; j++) { 1240 append("y"); 1241 try { 1242 append("d"); 1243 continue; 1244 } finally { 1245 append("e"); 1246 throw 200; 1247 } 1248 append("f"); 1249 } 1250 append("z"); 1251 } 1252 append("g"); 1253 return 700; 1254 1255 }, "a,b,c,y,d,e", NothingReturned, 200); 1256 1257 test(() => { 1258 append("a"); 1259 try { 1260 append("b"); 1261 throw 100; 1262 } finally { 1263 append("c"); 1264 for (var j = 0; j < 1; j++) { 1265 append("y"); 1266 try { 1267 append("d"); 1268 continue; 1269 } finally { 1270 append("e"); 1271 return 200; 1272 } 1273 append("f"); 1274 } 1275 append("z"); 1276 } 1277 append("g"); 1278 return 700; 1279 1280 }, "a,b,c,y,d,e", 200, NothingThrown); 1281 1282 test(() => { 1283 append("a"); 1284 try { 1285 append("b"); 1286 throw 100; 1287 } finally { 1288 append("c"); 1289 for (var j = 0; j < 1; j++) { 1290 append("y"); 1291 try { 1292 append("d"); 1293 continue; 1294 } finally { 1295 append("e"); 1296 continue; 1297 } 1298 append("f"); 1299 } 1300 append("z"); 1301 } 1302 append("g"); 1303 return 700; 1304 1305 }, "a,b,c,y,d,e,z", NothingReturned, 100); 1306 1307 test(() => { 1308 append("a"); 1309 try { 1310 append("b"); 1311 return 100; 1312 } finally { 1313 append("c"); 1314 for (var j = 0; j < 1; j++) { 1315 append("y"); 1316 try { 1317 append("d"); 1318 continue; 1319 } finally { 1320 append("e"); 1321 throw 200; 1322 } 1323 append("f"); 1324 } 1325 append("z"); 1326 } 1327 append("g"); 1328 return 700; 1329 1330 }, "a,b,c,y,d,e", NothingReturned, 200); 1331 1332 test(() => { 1333 append("a"); 1334 try { 1335 append("b"); 1336 return 100; 1337 } finally { 1338 append("c"); 1339 for (var j = 0; j < 1; j++) { 1340 append("y"); 1341 try { 1342 append("d"); 1343 continue; 1344 } finally { 1345 append("e"); 1346 return 200; 1347 } 1348 append("f"); 1349 } 1350 append("z"); 1351 } 1352 append("g"); 1353 return 700; 1354 1355 }, "a,b,c,y,d,e", 200, NothingThrown); 1356 1357 test(() => { 1358 append("a"); 1359 try { 1360 append("b"); 1361 return 100; 1362 } finally { 1363 append("c"); 1364 for (var j = 0; j < 1; j++) { 1365 append("y"); 1366 try { 1367 append("d"); 1368 continue; 1369 } finally { 1370 append("e"); 1371 continue; 1372 } 1373 append("f"); 1374 } 1375 append("z"); 1376 } 1377 append("g"); 1378 return 700; 1379 1380 }, "a,b,c,y,d,e,z", 100, NothingThrown); 1381 728 1382 // No throw or return in for-of loop. 729 1383 test(() => { -
trunk/Source/JavaScriptCore/ChangeLog
r242585 r242591 1 2019-03-06 Mark Lam <mark.lam@apple.com> 2 3 Fix incorrect handling of try-finally completion values. 4 https://bugs.webkit.org/show_bug.cgi?id=195131 5 <rdar://problem/46222079> 6 7 Reviewed by Saam Barati and Yusuke Suzuki. 8 9 Consider the following: 10 11 function foo() { // line 1 12 try { 13 return 42; // line 3 14 } finally { 15 for (var j = 0; j < 1; j++) { // line 5 16 try { 17 throw ''; // line 7 18 } finally { 19 continue; // line 9 20 } 21 } 22 } // line 11 23 } 24 var result = foo(); 25 26 With the current (before fix) code base, result will be the exception object thrown 27 at line 7. The expected result should be 42, returned at line 3. 28 29 The bug is that we were previously only using one set of completion type and 30 value registers for the entire function. This is inadequate because the outer 31 try-finally needs to preserve its own completion type and value ({ Return, 42 } 32 in this case) in order to be able to complete correctly. 33 34 One might be deceived into thinking that the above example should complete with 35 the exception thrown at line 7. However, according to Section 13.15.8 of the 36 ECMAScript spec, the 'continue' in the finally at line 9 counts as an abrupt 37 completion. As a result, it overrides the throw from line 7. After the continue, 38 execution resumes at the top of the loop at line 5, followed by a normal completion 39 at line 11. 40 41 Also according to Section 13.15.8, given that the completion type of the outer 42 finally is normal, the resultant completion of the outer try-finally should be 43 the completion of the outer try block i.e. { Return, 42 }. 44 45 This patch makes the following changes: 46 47 1. Fix handling of finally completion to use a unique set of completion 48 type and value registers for each FinallyContext. 49 50 2. Move the setting of Throw completion type to the out of line exception handler. 51 This makes the mainline code slightly less branchy. 52 53 3. Introduce emitOutOfLineCatchHandler(), emitOutOfLineFinallyHandler(), and 54 emitOutOfLineExceptionHandler() to make it clearer that these are not emitting 55 bytecode inline. Also, these make it clearer when we're emitting a handler 56 for a catch vs a finally. 57 58 4. Allocate the FinallyContext on the stack instead of as a member of the 59 heap allocated ControlFlowScope. This simplifies its life-cycle management 60 and reduces the amount of needed copying. 61 62 5. Update emitFinallyCompletion() to propagate the completion type and value to 63 the outer FinallyContext when needed. 64 65 6. Fix emitJumpIf() to use the right order of operands. Previously, we were 66 only using it to do op_stricteq and op_nstricteq comparisons. So, the order 67 wasn't important. We now use it to also do op_beloweq comparisons. Hence, 68 the order needs to be corrected. 69 70 7. Remove the unused CompletionType::Break and Continue. These are encoded with 71 the jumpIDs of the jump targets instead. 72 73 Relevant specifications: 74 Section 13.15.8: https://www.ecma-international.org/ecma-262/9.0/index.html#sec-try-statement-runtime-semantics-evaluation 75 Section 6.3.2.4: https://www.ecma-international.org/ecma-262/9.0/index.html#sec-updateempty 76 77 * bytecompiler/BytecodeGenerator.cpp: 78 (JSC::FinallyContext::FinallyContext): 79 (JSC::BytecodeGenerator::generate): 80 (JSC::BytecodeGenerator::BytecodeGenerator): 81 (JSC::BytecodeGenerator::pushFinallyControlFlowScope): 82 (JSC::BytecodeGenerator::popFinallyControlFlowScope): 83 (JSC::BytecodeGenerator::emitOutOfLineCatchHandler): 84 (JSC::BytecodeGenerator::emitOutOfLineFinallyHandler): 85 (JSC::BytecodeGenerator::emitOutOfLineExceptionHandler): 86 (JSC::BytecodeGenerator::emitEnumeration): 87 (JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded): 88 (JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded): 89 (JSC::BytecodeGenerator::emitFinallyCompletion): 90 (JSC::BytecodeGenerator::emitJumpIf): 91 (JSC::BytecodeGenerator::emitCatch): Deleted. 92 (JSC::BytecodeGenerator::allocateCompletionRecordRegisters): Deleted. 93 (JSC::BytecodeGenerator::releaseCompletionRecordRegisters): Deleted. 94 * bytecompiler/BytecodeGenerator.h: 95 (JSC::FinallyContext::completionTypeRegister const): 96 (JSC::FinallyContext::completionValueRegister const): 97 (JSC::ControlFlowScope::ControlFlowScope): 98 (JSC::BytecodeGenerator::emitLoad): 99 (JSC::BytecodeGenerator::CompletionRecordScope::CompletionRecordScope): Deleted. 100 (JSC::BytecodeGenerator::CompletionRecordScope::~CompletionRecordScope): Deleted. 101 (JSC::BytecodeGenerator::completionTypeRegister const): Deleted. 102 (JSC::BytecodeGenerator::completionValueRegister const): Deleted. 103 (JSC::BytecodeGenerator::emitSetCompletionType): Deleted. 104 (JSC::BytecodeGenerator::emitSetCompletionValue): Deleted. 105 * bytecompiler/NodesCodegen.cpp: 106 (JSC::TryNode::emitBytecode): 107 1 108 2019-03-06 Saam Barati <sbarati@apple.com> 2 109 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r241571 r242591 184 184 } 185 185 186 FinallyContext::FinallyContext(BytecodeGenerator& generator, Label& finallyLabel) 187 : m_outerContext(generator.m_currentFinallyContext) 188 , m_finallyLabel(&finallyLabel) 189 { 190 ASSERT(m_jumps.isEmpty()); 191 m_completionRecord.typeRegister = generator.newTemporary(); 192 m_completionRecord.valueRegister = generator.newTemporary(); 193 generator.emitLoad(completionTypeRegister(), CompletionType::Normal); 194 generator.moveEmptyValue(completionValueRegister()); 195 } 196 186 197 ParserError BytecodeGenerator::generate() 187 198 { … … 246 257 } 247 258 248 for (auto& tuple : m_ catchesToEmit) {259 for (auto& tuple : m_exceptionHandlersToEmit) { 249 260 Ref<Label> realCatchTarget = newLabel(); 261 TryData* tryData = std::get<0>(tuple); 262 250 263 OpCatch::emit(this, std::get<1>(tuple), std::get<2>(tuple)); 251 264 realCatchTarget->setLocation(*this, m_lastInstruction.offset()); 265 if (std::get<3>(tuple).isValid()) { 266 RegisterID completionTypeRegister { std::get<3>(tuple) }; 267 CompletionType completionType = 268 tryData->handlerType == HandlerType::Finally || tryData->handlerType == HandlerType::SynthesizedFinally 269 ? CompletionType::Throw 270 : CompletionType::Normal; 271 emitLoad(&completionTypeRegister, completionType); 272 } 252 273 m_codeBlock->addJumpTarget(m_lastInstruction.offset()); 253 274 254 275 255 TryData* tryData = std::get<0>(tuple);256 276 emitJump(tryData->target.get()); 257 277 tryData->target = WTFMove(realCatchTarget); … … 789 809 790 810 RefPtr<RegisterID> thrownValue = newTemporary(); 791 RegisterID* unused = newTemporary(); 792 emitCatch(unused, thrownValue.get(), tryFormalParametersData); 811 emitOutOfLineCatchHandler(thrownValue.get(), nullptr, tryFormalParametersData); 793 812 794 813 // return promiseCapability.@reject(thrownValue) … … 3514 3533 } 3515 3534 3516 FinallyContext* BytecodeGenerator::pushFinallyControlFlowScope(Label& finallyLabel)3517 { 3518 ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), FinallyContext(m_currentFinallyContext, finallyLabel));3535 void BytecodeGenerator::pushFinallyControlFlowScope(FinallyContext& finallyContext) 3536 { 3537 ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), &finallyContext); 3519 3538 m_controlFlowScopeStack.append(WTFMove(scope)); 3520 3539 3521 3540 m_finallyDepth++; 3522 m_currentFinallyContext = &m_controlFlowScopeStack.last().finallyContext; 3523 return m_currentFinallyContext; 3524 } 3525 3526 FinallyContext BytecodeGenerator::popFinallyControlFlowScope() 3541 m_currentFinallyContext = &finallyContext; 3542 } 3543 3544 void BytecodeGenerator::popFinallyControlFlowScope() 3527 3545 { 3528 3546 ASSERT(m_controlFlowScopeStack.size()); … … 3532 3550 m_currentFinallyContext = m_currentFinallyContext->outerContext(); 3533 3551 m_finallyDepth--; 3534 return m_controlFlowScopeStack.takeLast().finallyContext;3552 m_controlFlowScopeStack.removeLast(); 3535 3553 } 3536 3554 … … 3643 3661 } 3644 3662 3645 void BytecodeGenerator::emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, TryData* data) 3646 { 3647 m_catchesToEmit.append(CatchEntry { data, exceptionRegister, thrownValueRegister }); 3663 void BytecodeGenerator::emitOutOfLineCatchHandler(RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData* data) 3664 { 3665 RegisterID* unused = newTemporary(); 3666 emitOutOfLineExceptionHandler(unused, thrownValueRegister, completionTypeRegister, data); 3667 } 3668 3669 void BytecodeGenerator::emitOutOfLineFinallyHandler(RegisterID* exceptionRegister, RegisterID* completionTypeRegister, TryData* data) 3670 { 3671 RegisterID* unused = newTemporary(); 3672 ASSERT(completionTypeRegister); 3673 emitOutOfLineExceptionHandler(exceptionRegister, unused, completionTypeRegister, data); 3674 } 3675 3676 void BytecodeGenerator::emitOutOfLineExceptionHandler(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData* data) 3677 { 3678 VirtualRegister completionTypeVirtualRegister = completionTypeRegister ? completionTypeRegister : VirtualRegister(); 3679 m_exceptionHandlersToEmit.append(CatchEntry { data, exceptionRegister, thrownValueRegister, completionTypeVirtualRegister }); 3648 3680 } 3649 3681 … … 3960 3992 ASSERT(!isForAwait || (isForAwait && isAsyncFunctionParseMode(parseMode()))); 3961 3993 3962 CompletionRecordScope completionRecordScope(*this);3963 3964 3994 RefPtr<RegisterID> subject = newTemporary(); 3965 3995 emitNode(subject.get(), subjectNode); … … 3975 4005 3976 4006 // RefPtr<Register> iterator's lifetime must be longer than IteratorCloseContext. 3977 FinallyContext* finallyContext = pushFinallyControlFlowScope(finallyLabel.get()); 4007 FinallyContext finallyContext(*this, finallyLabel.get()); 4008 pushFinallyControlFlowScope(finallyContext); 3978 4009 3979 4010 { … … 4001 4032 Ref<Label> finallyBodyLabel = newLabel(); 4002 4033 RefPtr<RegisterID> finallyExceptionRegister = newTemporary(); 4003 RegisterID* unused = newTemporary(); 4004 4005 emitCatch(completionValueRegister(), unused, tryData); 4006 emitSetCompletionType(CompletionType::Throw); 4007 move(finallyExceptionRegister.get(), completionValueRegister()); 4034 4035 emitOutOfLineFinallyHandler(finallyContext.completionValueRegister(), finallyContext.completionTypeRegister(), tryData); 4036 move(finallyExceptionRegister.get(), finallyContext.completionValueRegister()); 4008 4037 emitJump(finallyBodyLabel.get()); 4009 4038 … … 4011 4040 moveEmptyValue(finallyExceptionRegister.get()); 4012 4041 4042 // Finally fall through case. 4013 4043 emitLabel(finallyBodyLabel.get()); 4014 4044 restoreScopeRegister(); … … 4034 4064 4035 4065 emitLabel(finallyDone.get()); 4036 emitFinallyCompletion( *finallyContext, completionTypeRegister(), endCatchLabel.get());4066 emitFinallyCompletion(finallyContext, endCatchLabel.get()); 4037 4067 4038 4068 popTry(returnCallTryData, finallyDone.get()); … … 4045 4075 { 4046 4076 emitLabel(catchLabel.get()); 4077 4047 4078 RefPtr<RegisterID> exceptionRegister = newTemporary(); 4048 RegisterID* unused = newTemporary(); 4049 emitCatch(exceptionRegister.get(), unused, returnCallTryData); 4079 emitOutOfLineFinallyHandler(exceptionRegister.get(), finallyContext.completionTypeRegister(), returnCallTryData); 4050 4080 // Since this is a synthesized catch block and we're guaranteed to never need 4051 4081 // to resolve any symbols from the scope, we can skip restoring the scope … … 4686 4716 ControlFlowScope* scope = &m_controlFlowScopeStack[scopeIndex--]; 4687 4717 if (scope->isFinallyScope()) { 4688 FinallyContext* finallyContext = &scope->finallyContext;4718 FinallyContext* finallyContext = scope->finallyContext; 4689 4719 if (!innermostFinallyContext) 4690 4720 innermostFinallyContext = finallyContext; … … 4700 4730 outermostFinallyContext->registerJump(jumpID, lexicalScopeIndex, jumpTarget); 4701 4731 4702 emit SetCompletionType(jumpID);4732 emitLoad(innermostFinallyContext->completionTypeRegister(), jumpID); 4703 4733 emitJump(*innermostFinallyContext->finallyLabel()); 4704 4734 return true; // We'll be jumping to a finally block. … … 4716 4746 ControlFlowScope* scope = &m_controlFlowScopeStack[scopeIndex]; 4717 4747 if (scope->isFinallyScope()) { 4718 FinallyContext* finallyContext = &scope->finallyContext;4748 FinallyContext* finallyContext = scope->finallyContext; 4719 4749 if (!innermostFinallyContext) 4720 4750 innermostFinallyContext = finallyContext; … … 4725 4755 return false; // No finallys to thread through. 4726 4756 4727 emit SetCompletionType(CompletionType::Return);4728 emitSetCompletionValue(returnRegister);4757 emitLoad(innermostFinallyContext->completionTypeRegister(), CompletionType::Return); 4758 move(innermostFinallyContext->completionValueRegister(), returnRegister); 4729 4759 emitJump(*innermostFinallyContext->finallyLabel()); 4730 4760 return true; // We'll be jumping to a finally block. 4731 4761 } 4732 4762 4733 void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, RegisterID* completionTypeRegister,Label& normalCompletionLabel)4763 void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, Label& normalCompletionLabel) 4734 4764 { 4735 4765 if (context.numberOfBreaksOrContinues() || context.handlesReturns()) { 4736 emitJumpIf<OpStricteq>(co mpletionTypeRegister, CompletionType::Normal, normalCompletionLabel);4766 emitJumpIf<OpStricteq>(context.completionTypeRegister(), CompletionType::Normal, normalCompletionLabel); 4737 4767 4738 4768 FinallyContext* outerContext = context.outerContext(); … … 4741 4771 ASSERT(outerContext || numberOfJumps == context.numberOfBreaksOrContinues()); 4742 4772 4773 // Handle Break or Continue completions that jumps into this FinallyContext. 4743 4774 for (size_t i = 0; i < numberOfJumps; i++) { 4744 4775 Ref<Label> nextLabel = newLabel(); 4745 4776 auto& jump = context.jumps(i); 4746 emitJumpIf<OpNstricteq>(completionTypeRegister, jump.jumpID, nextLabel.get()); 4747 4777 emitJumpIf<OpNstricteq>(context.completionTypeRegister(), jump.jumpID, nextLabel.get()); 4778 4779 // After a Break or Continue, we resume execution and may eventually complete with 4780 // Normal completion (unless abruptly completed again). So, pre-emptively set the 4781 // completion type to Normal. We can also set the completion value to undefined, 4782 // but it will never be used for normal completion anyway. So, we'll skip setting it. 4748 4783 restoreScopeRegister(jump.targetLexicalScopeIndex); 4749 emit SetCompletionType(CompletionType::Normal);4784 emitLoad(context.completionTypeRegister(), CompletionType::Normal); 4750 4785 emitJump(jump.targetLabel.get()); 4751 4786 … … 4753 4788 } 4754 4789 4790 // Handle completions that take us out of this FinallyContext. 4755 4791 if (outerContext) { 4756 // We are not the outermost finally. 4792 if (context.handlesReturns()) { 4793 Ref<Label> isNotReturnLabel = newLabel(); 4794 emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Return, isNotReturnLabel.get()); 4795 4796 // For Return completion, we need to pass the completion type and value to 4797 // the outer finally so that it can return when it's done (unless interrupted 4798 // by another abrupt completion). 4799 move(outerContext->completionTypeRegister(), context.completionTypeRegister()); 4800 move(outerContext->completionValueRegister(), context.completionValueRegister()); 4801 emitJump(*outerContext->finallyLabel()); 4802 4803 emitLabel(isNotReturnLabel.get()); 4804 } 4805 4757 4806 bool hasBreaksOrContinuesNotCoveredByJumps = context.numberOfBreaksOrContinues() > numberOfJumps; 4758 if (hasBreaksOrContinuesNotCoveredByJumps || context.handlesReturns()) 4759 emitJumpIf<OpNstricteq>(completionTypeRegister, CompletionType::Throw, *outerContext->finallyLabel()); 4807 if (hasBreaksOrContinuesNotCoveredByJumps) { 4808 Ref<Label> isThrowOrNormalLabel = newLabel(); 4809 emitJumpIf<OpBeloweq>(context.completionTypeRegister(), CompletionType::Throw, isThrowOrNormalLabel.get()); 4810 static_assert(CompletionType::Throw < CompletionType::Return && CompletionType::Throw < CompletionType::Return, "jumpIDs are above CompletionType::Return"); 4811 4812 // Not Throw means we have a Break or Continue that should be handled by the outer context. 4813 // These are for Break or Continue completions that have not reached their jump targets 4814 // yet. The outer context needs to run its finally, and resume the jump outwards (unless 4815 // interrupted by another abrupt completion). So, we need to pass the completion type to 4816 // the outer finally. Again, we can skip the completion value because it's not used for 4817 // Break nor Continue. 4818 move(outerContext->completionTypeRegister(), context.completionTypeRegister()); 4819 emitJump(*outerContext->finallyLabel()); 4820 4821 emitLabel(isThrowOrNormalLabel.get()); 4822 } 4760 4823 4761 4824 } else { … … 4763 4826 if (context.handlesReturns()) { 4764 4827 Ref<Label> notReturnLabel = newLabel(); 4765 emitJumpIf<OpNstricteq>(co mpletionTypeRegister, CompletionType::Return, notReturnLabel.get());4828 emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Return, notReturnLabel.get()); 4766 4829 4767 4830 emitWillLeaveCallFrameDebugHook(); 4768 emitReturn(co mpletionValueRegister(), ReturnFrom::Finally);4769 4831 emitReturn(context.completionValueRegister(), ReturnFrom::Finally); 4832 4770 4833 emitLabel(notReturnLabel.get()); 4771 4834 } 4772 4835 } 4773 4836 } 4774 emitJumpIf<OpNstricteq>(completionTypeRegister, CompletionType::Throw, normalCompletionLabel); 4775 emitThrow(completionValueRegister()); 4776 } 4777 4778 bool BytecodeGenerator::allocateCompletionRecordRegisters() 4779 { 4780 if (m_completionTypeRegister) 4781 return false; 4782 4783 ASSERT(!m_completionValueRegister); 4784 m_completionTypeRegister = newTemporary(); 4785 m_completionValueRegister = newTemporary(); 4786 4787 emitSetCompletionType(CompletionType::Normal); 4788 moveEmptyValue(m_completionValueRegister.get()); 4789 return true; 4790 } 4791 4792 void BytecodeGenerator::releaseCompletionRecordRegisters() 4793 { 4794 ASSERT(m_completionTypeRegister && m_completionValueRegister); 4795 m_completionTypeRegister = nullptr; 4796 m_completionValueRegister = nullptr; 4837 4838 // Handle Throw or Normal completions. 4839 emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Throw, normalCompletionLabel); 4840 4841 // For Throw, we just re-throw the previously caught exception captured in the completion value. 4842 // The exception handler will set the completion type to Throw, and re-capture the completion 4843 // value if needed (i.e. if the exception handler is for a finally). Hence, no need to set the 4844 // completion type and value here. 4845 emitThrow(context.completionValueRegister()); 4797 4846 } 4798 4847 … … 4804 4853 OperandTypes operandTypes = OperandTypes(ResultType::numberTypeIsInt32(), ResultType::unknownType()); 4805 4854 4806 auto equivalenceResult = emitBinaryOp<CompareOp>(tempRegister.get(), valueConstant, completionTypeRegister, operandTypes);4855 auto equivalenceResult = emitBinaryOp<CompareOp>(tempRegister.get(), completionTypeRegister, valueConstant, operandTypes); 4807 4856 emitJumpIfTrue(equivalenceResult, jumpTarget); 4808 4857 } -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
r241571 r242591 1 1 /* 2 * Copyright (C) 2008-201 8Apple Inc. All rights reserved.2 * Copyright (C) 2008-2019 Apple Inc. All rights reserved. 3 3 * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> 4 4 * Copyright (C) 2012 Igalia, S.L. … … 99 99 enum class CompletionType : int { 100 100 Normal, 101 Break, 102 Continue, 101 Throw, 103 102 Return, 104 Throw,105 106 103 NumberOfTypes 107 104 }; … … 126 123 }; 127 124 128 struct FinallyContext { 125 class FinallyContext { 126 public: 129 127 FinallyContext() { } 130 FinallyContext(FinallyContext* outerContext, Label& finallyLabel) 131 : m_outerContext(outerContext) 132 , m_finallyLabel(&finallyLabel) 133 { 134 ASSERT(m_jumps.isEmpty()); 135 } 128 FinallyContext(BytecodeGenerator&, Label& finallyLabel); 136 129 137 130 FinallyContext* outerContext() const { return m_outerContext; } 138 131 Label* finallyLabel() const { return m_finallyLabel; } 132 133 RegisterID* completionTypeRegister() const { return m_completionRecord.typeRegister.get(); } 134 RegisterID* completionValueRegister() const { return m_completionRecord.valueRegister.get(); } 139 135 140 136 uint32_t numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues.unsafeGet(); } … … 158 154 bool m_handlesReturns { false }; 159 155 Vector<FinallyJump> m_jumps; 156 struct { 157 RefPtr<RegisterID> typeRegister; 158 RefPtr<RegisterID> valueRegister; 159 } m_completionRecord; 160 160 }; 161 161 … … 166 166 Finally 167 167 }; 168 ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext && finallyContext = FinallyContext())168 ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext* finallyContext = nullptr) 169 169 : type(type) 170 170 , lexicalScopeIndex(lexicalScopeIndex) 171 , finallyContext( std::forward<FinallyContext>(finallyContext))171 , finallyContext(finallyContext) 172 172 { } 173 173 … … 177 177 Type type; 178 178 int lexicalScopeIndex; 179 FinallyContext finallyContext;179 FinallyContext* finallyContext; 180 180 }; 181 181 … … 365 365 366 366 friend class BoundLabel; 367 friend class FinallyContext; 367 368 friend class Label; 368 369 friend class IndexedForInContext; … … 876 877 // End a try block. 'end' must have been emitted. 877 878 void popTry(TryData*, Label& end); 878 void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, TryData*); 879 880 void emitOutOfLineCatchHandler(RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData*); 881 void emitOutOfLineFinallyHandler(RegisterID* exceptionRegister, RegisterID* completionTypeRegister, TryData*); 879 882 880 883 private: … … 891 894 return size - 1; 892 895 } 896 897 void emitOutOfLineExceptionHandler(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData*); 893 898 894 899 public: … … 930 935 void emitWillLeaveCallFrameDebugHook(); 931 936 932 class CompletionRecordScope { 933 public: 934 CompletionRecordScope(BytecodeGenerator& generator, bool needCompletionRecordRegisters = true) 935 : m_generator(generator) 936 { 937 if (needCompletionRecordRegisters && m_generator.allocateCompletionRecordRegisters()) 938 m_needToReleaseOnDestruction = true; 939 } 940 ~CompletionRecordScope() 941 { 942 if (m_needToReleaseOnDestruction) 943 m_generator.releaseCompletionRecordRegisters(); 944 } 945 946 private: 947 BytecodeGenerator& m_generator; 948 bool m_needToReleaseOnDestruction { false }; 949 }; 950 951 RegisterID* completionTypeRegister() const 952 { 953 ASSERT(m_completionTypeRegister); 954 return m_completionTypeRegister.get(); 955 } 956 RegisterID* completionValueRegister() const 957 { 958 ASSERT(m_completionValueRegister); 959 return m_completionValueRegister.get(); 960 } 961 962 void emitSetCompletionType(CompletionType type) 963 { 964 emitLoad(completionTypeRegister(), JSValue(static_cast<int>(type))); 965 } 966 void emitSetCompletionValue(RegisterID* reg) 967 { 968 move(completionValueRegister(), reg); 937 void emitLoad(RegisterID* completionTypeRegister, CompletionType type) 938 { 939 emitLoad(completionTypeRegister, JSValue(static_cast<int>(type))); 969 940 } 970 941 … … 974 945 bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label& jumpTarget); 975 946 bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister); 976 void emitFinallyCompletion(FinallyContext&, RegisterID* completionTypeRegister, Label& normalCompletionLabel); 977 978 private: 979 bool allocateCompletionRecordRegisters(); 980 void releaseCompletionRecordRegisters(); 947 void emitFinallyCompletion(FinallyContext&, Label& normalCompletionLabel); 981 948 982 949 public: 983 FinallyContext* pushFinallyControlFlowScope(Label& finallyLabel);984 FinallyContextpopFinallyControlFlowScope();950 void pushFinallyControlFlowScope(FinallyContext&); 951 void popFinallyControlFlowScope(); 985 952 986 953 void pushIndexedForInScope(RegisterID* local, RegisterID* index); … … 1246 1213 RegisterID* m_promiseCapabilityRegister { nullptr }; 1247 1214 1248 RefPtr<RegisterID> m_completionTypeRegister;1249 RefPtr<RegisterID> m_completionValueRegister;1250 1251 1215 FinallyContext* m_currentFinallyContext { nullptr }; 1252 1216 … … 1315 1279 CompactVariableMap::Handle m_cachedVariablesUnderTDZ; 1316 1280 1317 using CatchEntry = std::tuple<TryData*, VirtualRegister, VirtualRegister>; 1318 Vector<CatchEntry> m_catchesToEmit; 1319 }; 1320 1281 using CatchEntry = std::tuple<TryData*, VirtualRegister, VirtualRegister, VirtualRegister>; 1282 Vector<CatchEntry> m_exceptionHandlersToEmit; 1283 }; 1321 1284 1322 1285 } // namespace JSC -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r241104 r242591 2 2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) 3 3 * Copyright (C) 2001 Peter Kelly (pmk@post.com) 4 * Copyright (C) 2003-201 8Apple Inc. All rights reserved.4 * Copyright (C) 2003-2019 Apple Inc. All rights reserved. 5 5 * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) 6 6 * Copyright (C) 2007 Maks Orlovich … … 3583 3583 3584 3584 ASSERT(m_catchBlock || m_finallyBlock); 3585 BytecodeGenerator::CompletionRecordScope completionRecordScope(generator, m_finallyBlock);3586 3585 3587 3586 RefPtr<Label> catchLabel; 3588 3587 RefPtr<Label> catchEndLabel; 3589 RefPtr<Label> finallyViaThrowLabel;3590 3588 RefPtr<Label> finallyLabel; 3591 3589 RefPtr<Label> finallyEndLabel; 3592 3593 Ref<Label> tryStartLabel = generator.newLabel(); 3594 generator.emitLabel(tryStartLabel.get()); 3590 Optional<FinallyContext> finallyContext; 3595 3591 3596 3592 if (m_finallyBlock) { 3597 finallyViaThrowLabel = generator.newLabel();3598 3593 finallyLabel = generator.newLabel(); 3599 3594 finallyEndLabel = generator.newLabel(); 3600 3595 3601 generator.pushFinallyControlFlowScope(*finallyLabel); 3596 finallyContext.emplace(generator, *finallyLabel); 3597 generator.pushFinallyControlFlowScope(finallyContext.value()); 3602 3598 } 3603 3599 if (m_catchBlock) { … … 3606 3602 } 3607 3603 3608 Label& tryHandlerLabel = m_catchBlock ? *catchLabel : *finallyViaThrowLabel; 3604 Ref<Label> tryLabel = generator.newEmittedLabel(); 3605 Label& tryHandlerLabel = m_catchBlock ? *catchLabel : *finallyLabel; 3609 3606 HandlerType tryHandlerType = m_catchBlock ? HandlerType::Catch : HandlerType::Finally; 3610 TryData* tryData = generator.pushTry(try StartLabel.get(), tryHandlerLabel, tryHandlerType);3607 TryData* tryData = generator.pushTry(tryLabel.get(), tryHandlerLabel, tryHandlerType); 3611 3608 TryData* finallyTryData = nullptr; 3612 3609 if (!m_catchBlock && m_finallyBlock) … … 3620 3617 generator.emitJump(*catchEndLabel); 3621 3618 3622 Ref<Label> endTryLabel = generator.newEmittedLabel();3623 generator.popTry(tryData, endTryLabel.get());3619 Ref<Label> tryEndLabel = generator.newEmittedLabel(); 3620 generator.popTry(tryData, tryEndLabel.get()); 3624 3621 3625 3622 if (m_catchBlock) { … … 3627 3624 generator.emitLabel(*catchLabel); 3628 3625 RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); 3629 RegisterID* unused = generator.newTemporary();3630 generator.emit Catch(unused, thrownValueRegister.get(), tryData);3626 RegisterID* completionTypeRegister = m_finallyBlock ? finallyContext->completionTypeRegister() : nullptr; 3627 generator.emitOutOfLineCatchHandler(thrownValueRegister.get(), completionTypeRegister, tryData); 3631 3628 generator.restoreScopeRegister(); 3632 3629 … … 3634 3631 // If the catch block throws an exception and we have a finally block, then the finally 3635 3632 // block should "catch" that exception. 3636 finallyTryData = generator.pushTry(*catchLabel, *finally ViaThrowLabel, HandlerType::Finally);3633 finallyTryData = generator.pushTry(*catchLabel, *finallyLabel, HandlerType::Finally); 3637 3634 } 3638 3635 … … 3653 3650 3654 3651 if (m_finallyBlock) { 3655 generator.emit SetCompletionType(CompletionType::Normal);3652 generator.emitLoad(finallyContext->completionTypeRegister(), CompletionType::Normal); 3656 3653 generator.emitJump(*finallyLabel); 3657 generator.popTry(finallyTryData, *finally ViaThrowLabel);3654 generator.popTry(finallyTryData, *finallyLabel); 3658 3655 } 3659 3656 … … 3663 3660 3664 3661 if (m_finallyBlock) { 3665 FinallyContext finallyContext = generator.popFinallyControlFlowScope(); 3666 3667 // Entry to the finally block for CompletionType::Throw. 3668 generator.emitLabel(*finallyViaThrowLabel); 3669 RegisterID* unused = generator.newTemporary(); 3670 generator.emitCatch(generator.completionValueRegister(), unused, finallyTryData); 3671 generator.emitSetCompletionType(CompletionType::Throw); 3662 generator.popFinallyControlFlowScope(); 3663 3664 // Entry to the finally block for CompletionType::Throw to be generated later. 3665 generator.emitOutOfLineFinallyHandler(finallyContext->completionValueRegister(), finallyContext->completionTypeRegister(), finallyTryData); 3672 3666 3673 3667 // Entry to the finally block for CompletionTypes other than Throw. … … 3675 3669 generator.restoreScopeRegister(); 3676 3670 3677 RefPtr<RegisterID> savedCompletionTypeRegister = generator.newTemporary();3678 generator.move(savedCompletionTypeRegister.get(), generator.completionTypeRegister());3679 3680 3671 int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1; 3681 3672 generator.emitProfileControlFlow(finallyStartOffset); 3682 3673 generator.emitNodeInTailPosition(m_finallyBlock); 3683 3674 3684 generator.emitFinallyCompletion(finallyContext , savedCompletionTypeRegister.get(), *finallyEndLabel);3675 generator.emitFinallyCompletion(finallyContext.value(), *finallyEndLabel); 3685 3676 generator.emitLabel(*finallyEndLabel); 3686 3677 generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
Note: See TracChangeset
for help on using the changeset viewer.