Changeset 210116 in webkit
- Timestamp:
- Dec 22, 2016, 2:48:32 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 8 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r210115 r210116 1 2016-12-22 Mark Lam <mark.lam@apple.com> 2 3 De-duplicate finally blocks. 4 https://bugs.webkit.org/show_bug.cgi?id=160168 5 6 Reviewed by Saam Barati. 7 8 Re-landing r209952 with a few new tests added in test-finally.js. 9 10 * stress/deeply-nested-finallys.js: Copied from JSTests/stress/deeply-nested-finallys.js. 11 - Tests many levels of finally nesting. This causes the old code to hang (and 12 crashes eventually) while trying to generate bytecode for the exponentially 13 duplicated finally blocks. The new code completes this test almost instantly. 14 15 * stress/test-finally.js: Copied from JSTests/stress/test-finally.js. 16 - Tests control flow through various permutations of finally blocks. 17 1 18 2016-12-22 Saam Barati <sbarati@apple.com> 2 19 -
trunk/JSTests/stress/test-finally.js
r210009 r210116 1017 1017 1018 1018 }, "c,n,0,n,1,r", NothingReturned, 100); 1019 1020 // Handling return in finally block F1 with try-finally in F1's body. 1021 test(() => { 1022 try { 1023 append("t1a"); 1024 return "r1"; 1025 append("t1b"); 1026 } catch(e) { 1027 append("c1"); 1028 } finally { 1029 append("f1a"); 1030 1031 try { 1032 append("t2"); 1033 } catch (e) { 1034 append("c2"); 1035 } finally { 1036 append("f2"); 1037 } 1038 append("f1b"); 1039 } 1040 1041 }, "t1a,f1a,t2,f2,f1b", "r1", NothingThrown); 1042 1043 // Handling return in finally block F1 with try-finally in F1's body, with F1 in a for-of loop. 1044 test(() => { 1045 class TestIterator { 1046 constructor() { 1047 append("ci"); 1048 this.i = 0; 1049 } 1050 next() { 1051 append("ni"); 1052 let done = (this.i == 3); 1053 return { done, value: this.i++ }; 1054 } 1055 return() { 1056 append("ri"); 1057 return { } 1058 } 1059 } 1060 1061 var arr = []; 1062 arr[Symbol.iterator] = function() { 1063 return new TestIterator(); 1064 } 1065 1066 for (var element of arr) { 1067 append(element); 1068 try { 1069 append("t1a"); 1070 return "r1"; 1071 append("t1b"); 1072 } catch(e) { 1073 append("c1"); 1074 } finally { 1075 append("f1a"); 1076 1077 try { 1078 append("t2"); 1079 } catch (e) { 1080 append("c2"); 1081 } finally { 1082 append("f2"); 1083 } 1084 append("f1b"); 1085 } 1086 } 1087 append("x"); 1088 1089 }, "ci,ni,0,t1a,f1a,t2,f2,f1b,ri", "r1", NothingThrown); 1090 1091 // Handling break in finally block F1 with try-finally in F1's body, with F1 in a for-of loop. 1092 test(() => { 1093 class TestIterator { 1094 constructor() { 1095 append("ci"); 1096 this.i = 0; 1097 } 1098 next() { 1099 append("ni"); 1100 let done = (this.i == 3); 1101 return { done, value: this.i++ }; 1102 } 1103 return() { 1104 append("ri"); 1105 return { } 1106 } 1107 } 1108 1109 var arr = []; 1110 arr[Symbol.iterator] = function() { 1111 return new TestIterator(); 1112 } 1113 1114 for (var element of arr) { 1115 append(element); 1116 try { 1117 append("t1a"); 1118 break; 1119 append("t1b"); 1120 } catch(e) { 1121 append("c1"); 1122 } finally { 1123 append("f1a"); 1124 1125 try { 1126 append("t2"); 1127 } catch (e) { 1128 append("c2"); 1129 } finally { 1130 append("f2"); 1131 } 1132 append("f1b"); 1133 } 1134 } 1135 append("x"); 1136 1137 }, "ci,ni,0,t1a,f1a,t2,f2,f1b,ri,x", undefined, NothingThrown); 1138 1139 // Handling return in a for-of loop in finally block F1 with try-finally in F1's body. 1140 test(() => { 1141 class TestIterator { 1142 constructor() { 1143 append("ci"); 1144 this.i = 0; 1145 } 1146 next() { 1147 append("ni"); 1148 let done = (this.i == 3); 1149 return { done, value: this.i++ }; 1150 } 1151 return() { 1152 append("ri"); 1153 return { } 1154 } 1155 } 1156 1157 var arr = []; 1158 arr[Symbol.iterator] = function() { 1159 return new TestIterator(); 1160 } 1161 1162 try { 1163 append("t1a"); 1164 for (var element of arr) { 1165 append(element); 1166 return "r1"; 1167 } 1168 append("t1b"); 1169 } catch(e) { 1170 append("c1"); 1171 } finally { 1172 append("f1a"); 1173 1174 try { 1175 append("t2"); 1176 } catch (e) { 1177 append("c2"); 1178 } finally { 1179 append("f2"); 1180 } 1181 append("f1b"); 1182 } 1183 append("x"); 1184 1185 }, "t1a,ci,ni,0,ri,f1a,t2,f2,f1b", "r1", NothingThrown); 1186 1187 // Handling break in a for-of loop in finally block F1 with try-finally in F1's body. 1188 test(() => { 1189 class TestIterator { 1190 constructor() { 1191 append("ci"); 1192 this.i = 0; 1193 } 1194 next() { 1195 append("ni"); 1196 let done = (this.i == 3); 1197 return { done, value: this.i++ }; 1198 } 1199 return() { 1200 append("ri"); 1201 return { } 1202 } 1203 } 1204 1205 var arr = []; 1206 arr[Symbol.iterator] = function() { 1207 return new TestIterator(); 1208 } 1209 1210 try { 1211 append("t1a"); 1212 for (var element of arr) { 1213 append(element); 1214 break; 1215 } 1216 append("t1b"); 1217 } catch(e) { 1218 append("c1"); 1219 } finally { 1220 append("f1a"); 1221 1222 try { 1223 append("t2"); 1224 } catch (e) { 1225 append("c2"); 1226 } finally { 1227 append("f2"); 1228 } 1229 append("f1b"); 1230 } 1231 append("x"); 1232 1233 }, "t1a,ci,ni,0,ri,t1b,f1a,t2,f2,f1b,x", undefined, NothingThrown); 1234 1235 // Handling return in finally block F1 with a for-of loop in F1's body. 1236 test(() => { 1237 class TestIterator { 1238 constructor() { 1239 append("ci"); 1240 this.i = 0; 1241 } 1242 next() { 1243 append("ni"); 1244 let done = (this.i == 2); 1245 return { done, value: this.i++ }; 1246 } 1247 return() { 1248 append("ri"); 1249 return { } 1250 } 1251 } 1252 1253 var arr = []; 1254 arr[Symbol.iterator] = function() { 1255 return new TestIterator(); 1256 } 1257 1258 try { 1259 append("t1a"); 1260 return "r1"; 1261 append("t1b"); 1262 } catch(e) { 1263 append("c1"); 1264 } finally { 1265 append("f1a"); 1266 1267 for (var element of arr) { 1268 append(element); 1269 } 1270 append("f1b"); 1271 } 1272 1273 append("x"); 1274 1275 }, "t1a,f1a,ci,ni,0,ni,1,ni,f1b", "r1", NothingThrown); 1276 1277 // Handling return in finally block F1 with a for-of loop nesting a try-finally in F1's body. 1278 test(() => { 1279 class TestIterator { 1280 constructor() { 1281 append("ci"); 1282 this.i = 0; 1283 } 1284 next() { 1285 append("ni"); 1286 let done = (this.i == 2); 1287 return { done, value: this.i++ }; 1288 } 1289 return() { 1290 append("ri"); 1291 return { } 1292 } 1293 } 1294 1295 var arr = []; 1296 arr[Symbol.iterator] = function() { 1297 return new TestIterator(); 1298 } 1299 1300 try { 1301 append("t1a"); 1302 return "r1"; 1303 append("t1b"); 1304 } catch(e) { 1305 append("c1"); 1306 } finally { 1307 append("f1a"); 1308 1309 for (var element of arr) { 1310 append(element); 1311 try { 1312 append("t2"); 1313 } catch (e) { 1314 append("c2"); 1315 } finally { 1316 append("f2"); 1317 } 1318 } 1319 append("f1b"); 1320 } 1321 1322 append("x"); 1323 1324 }, "t1a,f1a,ci,ni,0,t2,f2,ni,1,t2,f2,ni,f1b", "r1", NothingThrown); 1325 1326 // Handling return in finally block F1 with a for-of loop in F1's body + break in for-of loop. 1327 test(() => { 1328 class TestIterator { 1329 constructor() { 1330 append("ci"); 1331 this.i = 0; 1332 } 1333 next() { 1334 append("ni"); 1335 let done = (this.i == 2); 1336 return { done, value: this.i++ }; 1337 } 1338 return() { 1339 append("ri"); 1340 return { } 1341 } 1342 } 1343 1344 var arr = []; 1345 arr[Symbol.iterator] = function() { 1346 return new TestIterator(); 1347 } 1348 1349 try { 1350 append("t1a"); 1351 return "r1"; 1352 append("t1b"); 1353 } catch(e) { 1354 append("c1"); 1355 } finally { 1356 append("f1a"); 1357 1358 for (var element of arr) { 1359 append(element); 1360 break; 1361 } 1362 append("f1b"); 1363 } 1364 1365 append("x"); 1366 1367 }, "t1a,f1a,ci,ni,0,ri,f1b", "r1", NothingThrown); 1368 1369 // Handling return in finally block F1 with a for-of loop nesting a try-finally in F1's body + break in for-of loop. 1370 test(() => { 1371 class TestIterator { 1372 constructor() { 1373 append("ci"); 1374 this.i = 0; 1375 } 1376 next() { 1377 append("ni"); 1378 let done = (this.i == 2); 1379 return { done, value: this.i++ }; 1380 } 1381 return() { 1382 append("ri"); 1383 return { } 1384 } 1385 } 1386 1387 var arr = []; 1388 arr[Symbol.iterator] = function() { 1389 return new TestIterator(); 1390 } 1391 1392 try { 1393 append("t1a"); 1394 return "r1"; 1395 append("t1b"); 1396 } catch(e) { 1397 append("c1"); 1398 } finally { 1399 append("f1a"); 1400 1401 for (var element of arr) { 1402 append(element); 1403 try { 1404 append("t2"); 1405 } catch (e) { 1406 append("c2"); 1407 } finally { 1408 append("f2"); 1409 } 1410 break; 1411 } 1412 append("f1b"); 1413 } 1414 1415 append("x"); 1416 1417 }, "t1a,f1a,ci,ni,0,t2,f2,ri,f1b", "r1", NothingThrown); 1418 1419 // Handling return in finally block F1 with try-finally in F1's body. 1420 test(() => { 1421 try { 1422 append("t1a"); 1423 return "r1"; 1424 append("t1b"); 1425 } catch(e) { 1426 append("c1"); 1427 } finally { 1428 append("f1a"); 1429 1430 try { 1431 append("t2"); 1432 throw "t2"; 1433 } catch (e) { 1434 append("c2"); 1435 // t2 caught here, and completion type set back to normal. 1436 } finally { 1437 append("f2"); 1438 } 1439 append("f1b"); 1440 } 1441 1442 }, "t1a,f1a,t2,c2,f2,f1b", "r1", NothingThrown); 1443 1444 if (this.window) 1445 print("PASSED"); -
trunk/Source/JavaScriptCore/ChangeLog
r210115 r210116 1 2016-12-22 Mark Lam <mark.lam@apple.com> 2 3 De-duplicate finally blocks. 4 https://bugs.webkit.org/show_bug.cgi?id=160168 5 6 Reviewed by Saam Barati. 7 8 JS execution can arrive at a finally block when there are abrupt completions from 9 its try or catch block. The abrupt completion types include Break, 10 Continue, Return, and Throw. The non-abrupt completion type is called Normal 11 (i.e. the case of a try block falling through to the finally block). 12 13 Previously, we enable each of these paths for abrupt completion (except for Throw) 14 to run the finally block code by duplicating the finally block code at each of 15 the sites that trigger those completions. This patch fixes the implementation so 16 that each of these abrupt completions will set a completionTypeRegister (plus a 17 completionValueRegister for CompletionType::Return) and then jump to the 18 relevant finally blocks, and continue to thread through subsequent outer finally 19 blocks until execution reaches the outermost finally block that the completion 20 type dictates. We no longer duplicate the finally block code. 21 22 The implementation details: 23 1. We allocate a pair of registers (completionTypeRegister and completionValueRegister) 24 just before entering the outermost try-catch-finally scope. 25 26 On allocating the registers, we initialize the completionTypeRegister to 27 CompletionType::Normal, and set the completionValueRegister to the empty 28 JSValue. 29 30 2. The completionTypeRegister will hold a CompletionType value. This is how we 31 encode the CompletionType value to be set: 32 33 a. For Normal, Return, and Throw completion types: 34 - The completionTypeRegister is set to CompletionType::Normal, 35 CompletionType::Return, and CompletionType::Throw respectively. 36 37 b. For Break and Continue completion types: 38 - The completionTypeRegister is set to a unique jumpID where the jumpID is 39 computed as: 40 41 jumpID = CompletionType::NumberOfTypes + bytecodeOffset 42 43 The bytecodeOffset used here is the bytecodeOffset of the break or continue 44 statement that triggered this completion. 45 46 3. Each finally block will have 2 entries: 47 a. the catch entry. 48 b. the normal entry. 49 50 The catch entry is recorded in the codeBlock's exception handler table, 51 and can only be jumped to by the VM's exception handling mechanism. 52 53 The normal entry is recorded in a FinallyContext (at bytecode generation time 54 only) and is jumped to when we want enter the finally block due any of the 55 other CompletionTypes. 56 57 4. How each completion type works? 58 59 CompletionType::Normal 60 ====================== 61 We normally encounter this when falling through from a try or catch block to 62 the finally block. 63 64 For the try block case, since completionTypeRegister is set to Normal by default, 65 there's nothing more that needs to be done. 66 67 For the catch block case, since we entered the catch block with an exception, 68 completionTypeRegister may be set to Throw. We'll need to set it to Normal 69 before jumping to the finally block's normal entry. 70 71 CompletionType::Break 72 ===================== 73 When we emit bytecode for the BreakNode, we check if we have any FinallyContexts 74 that we need to service before jumping to the breakTarget. If we don't, then 75 emit op_jump to the breakTarget as usual. Otherwise: 76 77 a. we'll register a jumpID and the breakTarget with the FinallyContext for the 78 outermost finally block that we're supposed to run through. 79 b. we'll also increment the numberOfBreaksOrContinues count in each FinallyContext 80 from the innermost to the one for that outermost finally block. 81 c. emit bytecode to set the completionTypeRegister to the jumpID. 82 d. emit bytecode to jump to the normal entry of the innermost finally block. 83 84 Each finally block will take care of cascading to the next outer finally block 85 as needed (see (5) below). 86 87 CompletionType::Continue 88 ======================== 89 Since continues and breaks work the same way (i.e. with a jump), we handle this 90 exactly the same way as CompletionType::Break, except that we use the 91 continueTarget instead of the breakTarget. 92 93 CompletionType::Return 94 ====================== 95 When we emit bytecode for the ReturnNode, we check if we have any FinallyContexts 96 at all on the m_controlFlowScopeStack. If we don't, then emit op_ret as usual. 97 Otherwise: 98 99 a. emit bytecode to set the completionTypeRegister to CompletionType::Return. 100 b. emit bytecode to move the return value into the completionValueRegister. 101 c. emit bytecode to jump to the normal entry of the innermost finally block. 102 103 Each finally block will take care of cascading to the next outer finally block 104 as needed (see (5) below). 105 106 CompletionType::Throw 107 ====================== 108 At the catch entry a finally block, we: 109 1. emit an op_catch that stores the caught Exception object in the 110 completionValueRegister. 111 2. emit bytecode to set the completionTypeRegister to CompletionType::Throw. 112 3. Fall through or jump to the finally block's normal entry. 113 114 5. What happens in each finally block? 115 ================================== 116 For details on the finally block's catch entry, see "CompletionType::Throw" in 117 (4) above. 118 119 The finally block's normal entry will: 120 1. restore the scope of the finally block. 121 2. save the completionTypeRegister in a savedCompletionTypeRegister. 122 3. proceed to execute the body of the finally block. 123 124 At the end of the finally block, we will emit bytecode check the 125 savedCompletionTypeRegister for each completion type see emitFinallyCompletion()) 126 in the following order: 127 128 a. Check for CompletionType::Normal 129 ================================ 130 If savedCompletionTypeRegister is CompletionType::Normal, jump to the 131 designated normalCompletion label. We only need this check this finally 132 block also needs to check for Break, Continue, or Return. If not, the 133 completion type check for CompletionType::Throw below will make this check 134 redundant. 135 136 b. Check for CompletionType::Break and Continue 137 ============================================ 138 If the FinallyContext for this block has registered FinallyJumps, we'll 139 check the jumpIDs against the savedCompletionTypeRegister. If the jumpID 140 matches, jump to the corresponding jumpTarget. 141 142 If no jumpIDs match but the FinallyContext's numberOfBreaksOrContinues is 143 greater than the number of registered FinallyJumps, then this means that 144 we have a Break or Continue that needs to be handled by an outer finally 145 block. In that case, jump to the next outer finally block's normal entry. 146 147 c. Check for CompletionType::Return 148 ================================ 149 If this finally block is not the outermost and the savedCompletionTypeRegister 150 is set to CompletionType::Return, then jump to the next outer finally 151 block's normal entry. 152 153 Otherwise, if this finally block is the outermost and the savedCompletionTypeRegister 154 is set to CompletionType::Return, then execute op_ret and return the value 155 in the completionValueRegister. 156 157 d. CompletionType::Throw 158 ===================== 159 If savedCompletionTypeRegister is CompletionType::Throw, then just re-throw the 160 Exception object in the completionValueRegister. 161 162 Detail 1: that we check the savedCompletionTypeRegister (and not the 163 completionTypeRegister). This is because the finally block may itself contain 164 a try-finally, and this inner try-finally may have trashed the completionTypeRegister. 165 Here's an example: 166 167 try { 168 return "r1"; // Sets completionTypeRegister to CompletionType::Return; 169 } finally { 170 // completionTypeRegister is CompletionType::Return here. 171 172 try { 173 ... // do stuff. 174 } finally { 175 ... // do more stuff. 176 } 177 178 // completionTypeRegister may be anything here depending on what 179 // was executed in the inner try-finally block above. 180 181 // Hence, finally completion here must be based on a saved copy of the 182 // completionTypeRegister when we entered this finally block. 183 } 184 185 Detail 2: the finally completion for CompletionType::Throw must always explicitly 186 check if the savedCompletionTypeRegister is CompletionType::Throw before throwing. 187 We cannot imply that it is so from the Throw case being last. Here's why: 188 189 // completionTypeRegister is CompletionType::Normal here. 190 try { 191 return "r1"; // Sets completionTypeRegister to CompletionType::Return; 192 } finally { 193 // completionTypeRegister is CompletionType::Return here. 194 195 try { 196 ... // do stuff. No abrupt completions. 197 } finally { 198 // completionTypeRegister is CompletionType::Return here (from the outer try-finally). 199 // savedCompletionTypeRegister is set to completionTypeRegister (i.e. CompletionType::Return) here. 200 201 ... // do more stuff. No abrupt completions. 202 203 // Unless there's an abrupt completion since entering the outer 204 // finally block, the savedCompletionTypeRegister will remain set 205 // to CompletionType::Return. If we don't explicitly check if the 206 // savedCompletionTypeRegister is CompletionType::Throw before 207 // throwing here, we'll end up erroneously throwing "r1". 208 } 209 210 ... 211 } 212 213 6. restoreScopeRegister() 214 215 Since the needed scope objects are always stored in a local, we can restore 216 the scope register by simply moving from that local instead of going through 217 op_get_parent_scope. 218 219 7. m_controlFlowScopeStack needs to be a SegmentedVector instead of a Vector. 220 This makes it easier to keep a pointer to the FinallyContext on that stack, 221 and not have to worry about the vector being realloc'ed due to resizing. 222 223 Performance appears to be neutral both on ES6SampleBench (run via cli) and the 224 JSC benchmarks. 225 226 Relevant spec references: 227 https://tc39.github.io/ecma262/#sec-completion-record-specification-type 228 https://tc39.github.io/ecma262/#sec-try-statement-runtime-semantics-evaluation 229 230 * bytecode/HandlerInfo.h: 231 (JSC::HandlerInfoBase::typeName): 232 * bytecompiler/BytecodeGenerator.cpp: 233 (JSC::BytecodeGenerator::generate): 234 (JSC::BytecodeGenerator::BytecodeGenerator): 235 (JSC::BytecodeGenerator::emitReturn): 236 (JSC::BytecodeGenerator::pushFinallyControlFlowScope): 237 (JSC::BytecodeGenerator::popFinallyControlFlowScope): 238 (JSC::BytecodeGenerator::allocateAndEmitScope): 239 (JSC::BytecodeGenerator::pushTry): 240 (JSC::BytecodeGenerator::popTry): 241 (JSC::BytecodeGenerator::emitCatch): 242 (JSC::BytecodeGenerator::restoreScopeRegister): 243 (JSC::BytecodeGenerator::labelScopeDepthToLexicalScopeIndex): 244 (JSC::BytecodeGenerator::labelScopeDepth): 245 (JSC::BytecodeGenerator::pushLocalControlFlowScope): 246 (JSC::BytecodeGenerator::popLocalControlFlowScope): 247 (JSC::BytecodeGenerator::emitEnumeration): 248 (JSC::BytecodeGenerator::emitIsNumber): 249 (JSC::BytecodeGenerator::emitYield): 250 (JSC::BytecodeGenerator::emitDelegateYield): 251 (JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded): 252 (JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded): 253 (JSC::BytecodeGenerator::emitFinallyCompletion): 254 (JSC::BytecodeGenerator::allocateCompletionRecordRegisters): 255 (JSC::BytecodeGenerator::releaseCompletionRecordRegisters): 256 (JSC::BytecodeGenerator::emitJumpIf): 257 (JSC::BytecodeGenerator::pushIteratorCloseControlFlowScope): Deleted. 258 (JSC::BytecodeGenerator::popIteratorCloseControlFlowScope): Deleted. 259 (JSC::BytecodeGenerator::emitComplexPopScopes): Deleted. 260 (JSC::BytecodeGenerator::emitPopScopes): Deleted. 261 (JSC::BytecodeGenerator::popTryAndEmitCatch): Deleted. 262 * bytecompiler/BytecodeGenerator.h: 263 (JSC::bytecodeOffsetToJumpID): 264 (JSC::FinallyJump::FinallyJump): 265 (JSC::FinallyContext::FinallyContext): 266 (JSC::FinallyContext::outerContext): 267 (JSC::FinallyContext::finallyLabel): 268 (JSC::FinallyContext::depth): 269 (JSC::FinallyContext::numberOfBreaksOrContinues): 270 (JSC::FinallyContext::incNumberOfBreaksOrContinues): 271 (JSC::FinallyContext::handlesReturns): 272 (JSC::FinallyContext::setHandlesReturns): 273 (JSC::FinallyContext::registerJump): 274 (JSC::FinallyContext::numberOfJumps): 275 (JSC::FinallyContext::jumps): 276 (JSC::ControlFlowScope::ControlFlowScope): 277 (JSC::ControlFlowScope::isLabelScope): 278 (JSC::ControlFlowScope::isFinallyScope): 279 (JSC::BytecodeGenerator::currentLexicalScopeIndex): 280 (JSC::BytecodeGenerator::CompletionRecordScope::CompletionRecordScope): 281 (JSC::BytecodeGenerator::CompletionRecordScope::~CompletionRecordScope): 282 (JSC::BytecodeGenerator::completionTypeRegister): 283 (JSC::BytecodeGenerator::completionValueRegister): 284 (JSC::BytecodeGenerator::emitSetCompletionType): 285 (JSC::BytecodeGenerator::emitSetCompletionValue): 286 (JSC::BytecodeGenerator::isInFinallyBlock): Deleted. 287 * bytecompiler/NodesCodegen.cpp: 288 (JSC::ContinueNode::emitBytecode): 289 (JSC::BreakNode::emitBytecode): 290 (JSC::ReturnNode::emitBytecode): 291 (JSC::TryNode::emitBytecode): 292 1 293 2016-12-22 Saam Barati <sbarati@apple.com> 2 294 -
trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h
r210010 r210116 32 32 33 33 enum class HandlerType { 34 Illegal= 0,35 Catch= 1,36 Finally= 2,34 Catch = 0, 35 Finally = 1, 36 SynthesizedCatch = 2, 37 37 SynthesizedFinally = 3 38 38 }; … … 54 54 case HandlerType::Finally: 55 55 return "finally"; 56 case HandlerType::SynthesizedCatch: 57 return "synthesized catch"; 56 58 case HandlerType::SynthesizedFinally: 57 59 return "synthesized finally"; -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r210023 r210116 156 156 continue; 157 157 158 ASSERT(range.tryData->handlerType != HandlerType::Illegal);159 158 UnlinkedHandlerInfo info(static_cast<uint32_t>(start), static_cast<uint32_t>(end), 160 159 static_cast<uint32_t>(range.tryData->target->bind()), range.tryData->handlerType); … … 679 678 pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ); 680 679 680 RefPtr<Label> catchLabel = newLabel(); 681 681 TryData* tryFormalParametersData = nullptr; 682 if (isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList) { 682 bool needTryCatch = isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList; 683 if (needTryCatch) { 683 684 RefPtr<Label> tryFormalParametersStart = emitLabel(newLabel().get()); 684 tryFormalParametersData = pushTry(tryFormalParametersStart.get() );685 tryFormalParametersData = pushTry(tryFormalParametersStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch); 685 686 } 686 687 687 688 initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, isSimpleParameterList, functionNode, functionSymbolTable, symbolTableConstantIndex, captures, shouldCreateArgumentsVariableInParameterScope); 688 689 689 if ( isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList) {690 if (needTryCatch) { 690 691 RefPtr<Label> didNotThrow = newLabel(); 691 692 emitJump(didNotThrow.get()); 692 RefPtr<RegisterID> exception = newTemporary(); 693 emitLabel(catchLabel.get()); 694 popTry(tryFormalParametersData, catchLabel.get()); 695 693 696 RefPtr<RegisterID> thrownValue = newTemporary(); 694 Re fPtr<Label> catchHere = emitLabel(newLabel().get());695 popTryAndEmitCatch(tryFormalParametersData, exception.get(), thrownValue.get(), catchHere.get(), HandlerType::Catch);697 RegisterID* unused = newTemporary(); 698 emitCatch(unused, thrownValue.get()); 696 699 697 700 // return promiseCapability.@reject(thrownValue) … … 3492 3495 } 3493 3496 3494 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src )3497 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src, ReturnFrom from) 3495 3498 { 3496 3499 if (isConstructor()) { … … 3498 3501 bool srcIsThis = src->index() == m_thisRegister.index(); 3499 3502 3500 if (mightBeDerived && srcIsThis)3503 if (mightBeDerived && (srcIsThis || from == ReturnFrom::Finally)) 3501 3504 emitTDZCheck(src); 3502 3505 3503 if (!srcIsThis ) {3506 if (!srcIsThis || from == ReturnFrom::Finally) { 3504 3507 RefPtr<Label> isObjectLabel = newLabel(); 3505 3508 emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get()); … … 3679 3682 } 3680 3683 3681 void BytecodeGenerator::pushFinallyControlFlowScope(StatementNode* finallyBlock)3684 FinallyContext* BytecodeGenerator::pushFinallyControlFlowScope(Label* finallyLabel) 3682 3685 { 3683 3686 // Reclaim free label scopes. … … 3685 3688 m_labelScopes.removeLast(); 3686 3689 3687 ControlFlowScope scope; 3688 scope.isFinallyBlock = true; 3689 FinallyContext context = { 3690 finallyBlock, 3691 nullptr, 3692 nullptr, 3693 static_cast<unsigned>(m_controlFlowScopeStack.size()), 3694 static_cast<unsigned>(m_switchContextStack.size()), 3695 static_cast<unsigned>(m_forInContextStack.size()), 3696 static_cast<unsigned>(m_tryContextStack.size()), 3697 static_cast<unsigned>(m_labelScopes.size()), 3698 static_cast<unsigned>(m_lexicalScopeStack.size()), 3699 m_finallyDepth, 3700 m_localScopeDepth 3701 }; 3702 scope.finallyContext = context; 3703 m_controlFlowScopeStack.append(scope); 3690 ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), FinallyContext(m_currentFinallyContext, finallyLabel, m_finallyDepth)); 3691 m_controlFlowScopeStack.append(WTFMove(scope)); 3692 3704 3693 m_finallyDepth++; 3705 } 3706 3707 void BytecodeGenerator::pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* node) 3708 { 3709 // Reclaim free label scopes. 3710 while (m_labelScopes.size() && !m_labelScopes.last().refCount()) 3711 m_labelScopes.removeLast(); 3712 3713 ControlFlowScope scope; 3714 scope.isFinallyBlock = true; 3715 FinallyContext context = { 3716 nullptr, 3717 iterator, 3718 node, 3719 static_cast<unsigned>(m_controlFlowScopeStack.size()), 3720 static_cast<unsigned>(m_switchContextStack.size()), 3721 static_cast<unsigned>(m_forInContextStack.size()), 3722 static_cast<unsigned>(m_tryContextStack.size()), 3723 static_cast<unsigned>(m_labelScopes.size()), 3724 static_cast<unsigned>(m_lexicalScopeStack.size()), 3725 m_finallyDepth, 3726 m_localScopeDepth 3727 }; 3728 scope.finallyContext = context; 3729 m_controlFlowScopeStack.append(scope); 3730 m_finallyDepth++; 3731 } 3732 3733 void BytecodeGenerator::popFinallyControlFlowScope() 3694 m_currentFinallyContext = &m_controlFlowScopeStack.last().finallyContext; 3695 return m_currentFinallyContext; 3696 } 3697 3698 FinallyContext BytecodeGenerator::popFinallyControlFlowScope() 3734 3699 { 3735 3700 ASSERT(m_controlFlowScopeStack.size()); 3736 ASSERT(m_controlFlowScopeStack.last().isFinallyBlock); 3737 ASSERT(m_controlFlowScopeStack.last().finallyContext.finallyBlock); 3738 ASSERT(!m_controlFlowScopeStack.last().finallyContext.iterator); 3739 ASSERT(!m_controlFlowScopeStack.last().finallyContext.enumerationNode); 3701 ASSERT(m_controlFlowScopeStack.last().isFinallyScope()); 3740 3702 ASSERT(m_finallyDepth > 0); 3741 m_controlFlowScopeStack.removeLast(); 3703 ASSERT(m_currentFinallyContext); 3704 m_currentFinallyContext = m_currentFinallyContext->outerContext(); 3742 3705 m_finallyDepth--; 3743 } 3744 3745 void BytecodeGenerator::popIteratorCloseControlFlowScope() 3746 { 3747 ASSERT(m_controlFlowScopeStack.size()); 3748 ASSERT(m_controlFlowScopeStack.last().isFinallyBlock); 3749 ASSERT(!m_controlFlowScopeStack.last().finallyContext.finallyBlock); 3750 ASSERT(m_controlFlowScopeStack.last().finallyContext.iterator); 3751 ASSERT(m_controlFlowScopeStack.last().finallyContext.enumerationNode); 3752 ASSERT(m_finallyDepth > 0); 3753 m_controlFlowScopeStack.removeLast(); 3754 m_finallyDepth--; 3706 return m_controlFlowScopeStack.takeLast().finallyContext; 3755 3707 } 3756 3708 … … 3852 3804 emitMove(m_topMostScope, scopeRegister()); 3853 3805 } 3854 3855 void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowScope* topScope, ControlFlowScope* bottomScope) 3856 { 3857 while (topScope > bottomScope) { 3858 // First we count the number of dynamic scopes we need to remove to get 3859 // to a finally block. 3860 int numberOfNormalScopes = 0; 3861 while (topScope > bottomScope) { 3862 if (topScope->isFinallyBlock) 3863 break; 3864 ++numberOfNormalScopes; 3865 --topScope; 3866 } 3867 3868 if (numberOfNormalScopes) { 3869 // We need to remove a number of dynamic scopes to get to the next 3870 // finally block 3871 RefPtr<RegisterID> parentScope = newTemporary(); 3872 while (numberOfNormalScopes--) { 3873 parentScope = emitGetParentScope(parentScope.get(), scope); 3874 emitMove(scope, parentScope.get()); 3875 } 3876 3877 // If topScope == bottomScope then there isn't a finally block left to emit. 3878 if (topScope == bottomScope) 3879 return; 3880 } 3881 3882 Vector<ControlFlowScope> savedControlFlowScopeStack; 3883 Vector<SwitchInfo> savedSwitchContextStack; 3884 Vector<RefPtr<ForInContext>> savedForInContextStack; 3885 Vector<TryContext> poppedTryContexts; 3886 Vector<LexicalScopeStackEntry> savedLexicalScopeStack; 3887 LabelScopeStore savedLabelScopes; 3888 while (topScope > bottomScope && topScope->isFinallyBlock) { 3889 RefPtr<Label> beforeFinally = emitLabel(newLabel().get()); 3890 3891 // Save the current state of the world while instating the state of the world 3892 // for the finally block. 3893 FinallyContext finallyContext = topScope->finallyContext; 3894 bool flipScopes = finallyContext.controlFlowScopeStackSize != m_controlFlowScopeStack.size(); 3895 bool flipSwitches = finallyContext.switchContextStackSize != m_switchContextStack.size(); 3896 bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size(); 3897 bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size(); 3898 bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size(); 3899 bool flipLexicalScopeStack = finallyContext.lexicalScopeStackSize != m_lexicalScopeStack.size(); 3900 int topScopeIndex = -1; 3901 int bottomScopeIndex = -1; 3902 if (flipScopes) { 3903 topScopeIndex = topScope - m_controlFlowScopeStack.begin(); 3904 bottomScopeIndex = bottomScope - m_controlFlowScopeStack.begin(); 3905 savedControlFlowScopeStack = m_controlFlowScopeStack; 3906 m_controlFlowScopeStack.shrink(finallyContext.controlFlowScopeStackSize); 3907 } 3908 if (flipSwitches) { 3909 savedSwitchContextStack = m_switchContextStack; 3910 m_switchContextStack.shrink(finallyContext.switchContextStackSize); 3911 } 3912 if (flipForIns) { 3913 savedForInContextStack = m_forInContextStack; 3914 m_forInContextStack.shrink(finallyContext.forInContextStackSize); 3915 } 3916 if (flipTries) { 3917 while (m_tryContextStack.size() != finallyContext.tryContextStackSize) { 3918 ASSERT(m_tryContextStack.size() > finallyContext.tryContextStackSize); 3919 TryContext context = m_tryContextStack.takeLast(); 3920 TryRange range; 3921 range.start = context.start; 3922 range.end = beforeFinally; 3923 range.tryData = context.tryData; 3924 m_tryRanges.append(range); 3925 poppedTryContexts.append(context); 3926 } 3927 } 3928 if (flipLabelScopes) { 3929 savedLabelScopes = m_labelScopes; 3930 while (m_labelScopes.size() > finallyContext.labelScopesSize) 3931 m_labelScopes.removeLast(); 3932 } 3933 if (flipLexicalScopeStack) { 3934 savedLexicalScopeStack = m_lexicalScopeStack; 3935 m_lexicalScopeStack.shrink(finallyContext.lexicalScopeStackSize); 3936 } 3937 int savedFinallyDepth = m_finallyDepth; 3938 m_finallyDepth = finallyContext.finallyDepth; 3939 int savedDynamicScopeDepth = m_localScopeDepth; 3940 m_localScopeDepth = finallyContext.dynamicScopeDepth; 3941 3942 if (finallyContext.finallyBlock) { 3943 // Emit the finally block. 3944 emitNode(finallyContext.finallyBlock); 3945 } else { 3946 // Emit the IteratorClose block. 3947 ASSERT(finallyContext.iterator); 3948 emitIteratorClose(finallyContext.iterator, finallyContext.enumerationNode); 3949 } 3950 3951 RefPtr<Label> afterFinally = emitLabel(newLabel().get()); 3952 3953 // Restore the state of the world. 3954 if (flipScopes) { 3955 m_controlFlowScopeStack = savedControlFlowScopeStack; 3956 topScope = &m_controlFlowScopeStack[topScopeIndex]; // assert it's within bounds 3957 bottomScope = m_controlFlowScopeStack.begin() + bottomScopeIndex; // don't assert, since it the index might be -1. 3958 } 3959 if (flipSwitches) 3960 m_switchContextStack = savedSwitchContextStack; 3961 if (flipForIns) 3962 m_forInContextStack = savedForInContextStack; 3963 if (flipTries) { 3964 ASSERT(m_tryContextStack.size() == finallyContext.tryContextStackSize); 3965 for (unsigned i = poppedTryContexts.size(); i--;) { 3966 TryContext context = poppedTryContexts[i]; 3967 context.start = afterFinally; 3968 m_tryContextStack.append(context); 3969 } 3970 poppedTryContexts.clear(); 3971 } 3972 if (flipLabelScopes) 3973 m_labelScopes = savedLabelScopes; 3974 if (flipLexicalScopeStack) 3975 m_lexicalScopeStack = savedLexicalScopeStack; 3976 m_finallyDepth = savedFinallyDepth; 3977 m_localScopeDepth = savedDynamicScopeDepth; 3978 3979 --topScope; 3980 } 3981 } 3982 } 3983 3984 void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth) 3985 { 3986 ASSERT(labelScopeDepth() - targetScopeDepth >= 0); 3987 3988 size_t scopeDelta = labelScopeDepth() - targetScopeDepth; 3989 ASSERT(scopeDelta <= m_controlFlowScopeStack.size()); 3990 if (!scopeDelta) 3991 return; 3992 3993 if (!m_finallyDepth) { 3994 RefPtr<RegisterID> parentScope = newTemporary(); 3995 while (scopeDelta--) { 3996 parentScope = emitGetParentScope(parentScope.get(), scope); 3997 emitMove(scope, parentScope.get()); 3998 } 3999 return; 4000 } 4001 4002 emitComplexPopScopes(scope, &m_controlFlowScopeStack.last(), &m_controlFlowScopeStack.last() - scopeDelta); 4003 } 4004 4005 TryData* BytecodeGenerator::pushTry(Label* start) 3806 3807 TryData* BytecodeGenerator::pushTry(Label* start, Label* handlerLabel, HandlerType handlerType) 4006 3808 { 4007 3809 TryData tryData; 4008 tryData.target = newLabel();4009 tryData.handlerType = HandlerType::Illegal;3810 tryData.target = handlerLabel; 3811 tryData.handlerType = handlerType; 4010 3812 m_tryData.append(tryData); 4011 3813 TryData* result = &m_tryData.last(); … … 4020 3822 } 4021 3823 4022 void BytecodeGenerator::popTry AndEmitCatch(TryData* tryData, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType handlerType)3824 void BytecodeGenerator::popTry(TryData* tryData, Label* end) 4023 3825 { 4024 3826 m_usesExceptions = true; … … 4032 3834 m_tryRanges.append(tryRange); 4033 3835 m_tryContextStack.removeLast(); 4034 4035 emitLabel(tryRange.tryData->target.get()); 4036 tryRange.tryData->handlerType = handlerType; 4037 3836 } 3837 3838 void BytecodeGenerator::emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister) 3839 { 4038 3840 emitOpcode(op_catch); 4039 3841 instructions().append(exceptionRegister->index()); 4040 3842 instructions().append(thrownValueRegister->index()); 4041 4042 bool foundLocalScope = false; 4043 for (unsigned i = m_lexicalScopeStack.size(); i--; ) { 4044 // Note that if we don't find a local scope in the current function/program, 4045 // we must grab the outer-most scope of this bytecode generation. 4046 if (m_lexicalScopeStack[i].m_scope) { 4047 foundLocalScope = true; 4048 emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope); 4049 break; 4050 } 4051 } 4052 if (!foundLocalScope) 4053 emitMove(scopeRegister(), m_topMostScope); 3843 } 3844 3845 void BytecodeGenerator::restoreScopeRegister(int lexicalScopeIndex) 3846 { 3847 if (lexicalScopeIndex == CurrentLexicalScopeIndex) 3848 return; // No change needed. 3849 3850 if (lexicalScopeIndex != OutermostLexicalScopeIndex) { 3851 ASSERT(lexicalScopeIndex < static_cast<int>(m_lexicalScopeStack.size())); 3852 int endIndex = lexicalScopeIndex + 1; 3853 for (size_t i = endIndex; i--; ) { 3854 if (m_lexicalScopeStack[i].m_scope) { 3855 emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope); 3856 return; 3857 } 3858 } 3859 } 3860 // Note that if we don't find a local scope in the current function/program, 3861 // we must grab the outer-most scope of this bytecode generation. 3862 emitMove(scopeRegister(), m_topMostScope); 3863 } 3864 3865 void BytecodeGenerator::restoreScopeRegister() 3866 { 3867 restoreScopeRegister(currentLexicalScopeIndex()); 3868 } 3869 3870 int BytecodeGenerator::labelScopeDepthToLexicalScopeIndex(int targetLabelScopeDepth) 3871 { 3872 ASSERT(labelScopeDepth() - targetLabelScopeDepth >= 0); 3873 size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth; 3874 ASSERT(scopeDelta <= m_controlFlowScopeStack.size()); 3875 if (!scopeDelta) 3876 return CurrentLexicalScopeIndex; 3877 3878 ControlFlowScope& targetScope = m_controlFlowScopeStack[targetLabelScopeDepth]; 3879 return targetScope.lexicalScopeIndex; 4054 3880 } 4055 3881 … … 4060 3886 4061 3887 int BytecodeGenerator::labelScopeDepth() const 4062 { 4063 return localScopeDepth() + m_finallyDepth; 3888 { 3889 int depth = localScopeDepth() + m_finallyDepth; 3890 ASSERT(depth == static_cast<int>(m_controlFlowScopeStack.size())); 3891 return depth; 4064 3892 } 4065 3893 … … 4131 3959 void BytecodeGenerator::pushLocalControlFlowScope() 4132 3960 { 4133 ControlFlowScope scope; 4134 scope.isFinallyBlock = false; 4135 m_controlFlowScopeStack.append(scope); 3961 ControlFlowScope scope(ControlFlowScope::Label, currentLexicalScopeIndex()); 3962 m_controlFlowScopeStack.append(WTFMove(scope)); 4136 3963 m_localScopeDepth++; 4137 3964 } … … 4140 3967 { 4141 3968 ASSERT(m_controlFlowScopeStack.size()); 4142 ASSERT(!m_controlFlowScopeStack.last().isFinally Block);3969 ASSERT(!m_controlFlowScopeStack.last().isFinallyScope()); 4143 3970 m_controlFlowScopeStack.removeLast(); 4144 3971 m_localScopeDepth--; … … 4295 4122 return false; 4296 4123 } 4297 4124 4298 4125 void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, ForOfNode* forLoopNode, RegisterID* forLoopSymbolTable) 4299 4126 { 4127 CompletionRecordScope completionRecordScope(*this); 4128 4300 4129 RefPtr<RegisterID> subject = newTemporary(); 4301 4130 emitNode(subject.get(), subjectNode); … … 4308 4137 4309 4138 RefPtr<Label> loopDone = newLabel(); 4139 RefPtr<Label> tryStartLabel = newLabel(); 4140 RefPtr<Label> finallyViaThrowLabel = newLabel(); 4141 RefPtr<Label> finallyLabel = newLabel(); 4142 RefPtr<Label> catchLabel = newLabel(); 4143 RefPtr<Label> endCatchLabel = newLabel(); 4144 4310 4145 // RefPtr<Register> iterator's lifetime must be longer than IteratorCloseContext. 4311 pushIteratorCloseControlFlowScope(iterator.get(), node); 4146 FinallyContext* finallyContext = pushFinallyControlFlowScope(finallyLabel.get()); 4147 4312 4148 { 4313 4149 LabelScopePtr scope = newLabelScope(LabelScope::Loop); … … 4321 4157 emitLoopHint(); 4322 4158 4323 RefPtr<Label> tryStartLabel = newLabel();4324 4159 emitLabel(tryStartLabel.get()); 4325 TryData* tryData = pushTry(tryStartLabel.get() );4160 TryData* tryData = pushTry(tryStartLabel.get(), finallyViaThrowLabel.get(), HandlerType::SynthesizedFinally); 4326 4161 callBack(*this, value.get()); 4327 4162 emitJump(scope->continueTarget()); 4328 4163 4329 // IteratorClose sequence for throw-ed control flow.4164 // IteratorClose sequence for abrupt completions. 4330 4165 { 4331 RefPtr<Label> catchHere = emitLabel(newLabel().get()); 4332 RefPtr<RegisterID> exceptionRegister = newTemporary(); 4333 RefPtr<RegisterID> thrownValueRegister = newTemporary(); 4334 popTryAndEmitCatch(tryData, exceptionRegister.get(), 4335 thrownValueRegister.get(), catchHere.get(), HandlerType::SynthesizedFinally); 4336 4337 RefPtr<Label> catchDone = newLabel(); 4166 // Finally block for the enumeration. 4167 emitLabel(finallyViaThrowLabel.get()); 4168 popTry(tryData, finallyViaThrowLabel.get()); 4169 4170 RefPtr<Label> finallyBodyLabel = newLabel(); 4171 RefPtr<RegisterID> finallyExceptionRegister = newTemporary(); 4172 RegisterID* unused = newTemporary(); 4173 4174 emitCatch(completionValueRegister(), unused); 4175 emitSetCompletionType(CompletionType::Throw); 4176 emitMove(finallyExceptionRegister.get(), completionValueRegister()); 4177 emitJump(finallyBodyLabel.get()); 4178 4179 emitLabel(finallyLabel.get()); 4180 emitMoveEmptyValue(finallyExceptionRegister.get()); 4181 4182 emitLabel(finallyBodyLabel.get()); 4183 restoreScopeRegister(); 4184 4185 RefPtr<Label> finallyDone = newLabel(); 4338 4186 4339 4187 RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword); 4340 emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), catchDone.get());4188 emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), finallyDone.get()); 4341 4189 4342 4190 RefPtr<Label> returnCallTryStart = newLabel(); 4343 4191 emitLabel(returnCallTryStart.get()); 4344 TryData* returnCallTryData = pushTry(returnCallTryStart.get() );4192 TryData* returnCallTryData = pushTry(returnCallTryStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch); 4345 4193 4346 4194 CallArguments returnArguments(*this, nullptr); 4347 4195 emitMove(returnArguments.thisRegister(), iterator.get()); 4348 4196 emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd(), DebuggableCall::No); 4349 4350 emitLabel(catchDone.get()); 4351 emitThrow(exceptionRegister.get()); 4352 4353 // Absorb exception. 4354 popTryAndEmitCatch(returnCallTryData, newTemporary(), 4355 newTemporary(), catchDone.get(), HandlerType::SynthesizedFinally); 4356 emitThrow(exceptionRegister.get()); 4197 emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), finallyDone.get()); 4198 emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object.")); 4199 4200 emitLabel(finallyDone.get()); 4201 emitFinallyCompletion(*finallyContext, completionTypeRegister(), endCatchLabel.get()); 4202 4203 popTry(returnCallTryData, finallyDone.get()); 4204 4205 // Catch block for exceptions that may be thrown while calling the return 4206 // handler in the enumeration finally block. The only reason we need this 4207 // catch block is because if entered the above finally block due to a thrown 4208 // exception, then we want to re-throw the original exception on exiting 4209 // the finally block. Otherwise, we'll let any new exception pass through. 4210 { 4211 emitLabel(catchLabel.get()); 4212 RefPtr<RegisterID> exceptionRegister = newTemporary(); 4213 RegisterID* unused = newTemporary(); 4214 emitCatch(exceptionRegister.get(), unused); 4215 // Since this is a synthesized catch block and we're guaranteed to never need 4216 // to resolve any symbols from the scope, we can skip restoring the scope 4217 // register here. 4218 4219 RefPtr<Label> throwLabel = newLabel(); 4220 emitJumpIfTrue(emitIsEmpty(newTemporary(), finallyExceptionRegister.get()), throwLabel.get()); 4221 emitMove(exceptionRegister.get(), finallyExceptionRegister.get()); 4222 4223 emitLabel(throwLabel.get()); 4224 emitThrow(exceptionRegister.get()); 4225 4226 emitLabel(endCatchLabel.get()); 4227 } 4357 4228 } 4358 4229 … … 4375 4246 4376 4247 // IteratorClose sequence for break-ed control flow. 4377 pop IteratorCloseControlFlowScope();4248 popFinallyControlFlowScope(); 4378 4249 emitIteratorClose(iterator.get(), node); 4379 4250 emitLabel(loopDone.get()); … … 4491 4362 { 4492 4363 emitOpcode(op_is_object); 4364 instructions().append(dst->index()); 4365 instructions().append(src->index()); 4366 return dst; 4367 } 4368 4369 RegisterID* BytecodeGenerator::emitIsNumber(RegisterID* dst, RegisterID* src) 4370 { 4371 emitOpcode(op_is_number); 4493 4372 instructions().append(dst->index()); 4494 4373 instructions().append(src->index()); … … 4783 4662 { 4784 4663 RefPtr<RegisterID> returnRegister = generatorValueRegister(); 4785 if (isInFinallyBlock()) { 4786 returnRegister = emitMove(newTemporary(), returnRegister.get()); 4787 emitPopScopes(scopeRegister(), 0); 4788 } 4789 emitReturn(returnRegister.get()); 4664 bool hasFinally = emitReturnViaFinallyIfNeeded(returnRegister.get()); 4665 if (!hasFinally) 4666 emitReturn(returnRegister.get()); 4790 4667 } 4791 4668 … … 4890 4767 4891 4768 emitLabel(returnSequence.get()); 4892 if (isInFinallyBlock())4893 emitPopScopes(scopeRegister(), 0);4894 emitReturn(value.get());4769 bool hasFinally = emitReturnViaFinallyIfNeeded(value.get()); 4770 if (!hasFinally) 4771 emitReturn(value.get()); 4895 4772 } 4896 4773 … … 4920 4797 RegisterID* completedState = emitLoad(nullptr, jsNumber(state)); 4921 4798 emitPutById(generatorRegister(), propertyNames().builtinNames().generatorStatePrivateName(), completedState); 4799 } 4800 4801 bool BytecodeGenerator::emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget) 4802 { 4803 ASSERT(labelScopeDepth() - targetLabelScopeDepth >= 0); 4804 size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth; 4805 ASSERT(scopeDelta <= m_controlFlowScopeStack.size()); 4806 if (!scopeDelta) 4807 return false; // No finallys to thread through. 4808 4809 ControlFlowScope* topScope = &m_controlFlowScopeStack.last(); 4810 ControlFlowScope* bottomScope = &m_controlFlowScopeStack.last() - scopeDelta; 4811 4812 FinallyContext* innermostFinallyContext = nullptr; 4813 FinallyContext* outermostFinallyContext = nullptr; 4814 while (topScope > bottomScope) { 4815 if (topScope->isFinallyScope()) { 4816 FinallyContext* finallyContext = &topScope->finallyContext; 4817 if (!innermostFinallyContext) 4818 innermostFinallyContext = finallyContext; 4819 outermostFinallyContext = finallyContext; 4820 finallyContext->incNumberOfBreaksOrContinues(); 4821 } 4822 --topScope; 4823 } 4824 if (!outermostFinallyContext) 4825 return false; // No finallys to thread through. 4826 4827 auto jumpID = bytecodeOffsetToJumpID(instructions().size()); 4828 int lexicalScopeIndex = labelScopeDepthToLexicalScopeIndex(targetLabelScopeDepth); 4829 outermostFinallyContext->registerJump(jumpID, lexicalScopeIndex, jumpTarget); 4830 4831 emitSetCompletionType(jumpID); 4832 emitJump(innermostFinallyContext->finallyLabel()); 4833 return true; // We'll be jumping to a finally block. 4834 } 4835 4836 bool BytecodeGenerator::emitReturnViaFinallyIfNeeded(RegisterID* returnRegister) 4837 { 4838 if (!m_controlFlowScopeStack.size()) 4839 return false; // No finallys to thread through. 4840 4841 ControlFlowScope* topScope = &m_controlFlowScopeStack.last(); 4842 ControlFlowScope* bottomScope = &m_controlFlowScopeStack.first(); 4843 4844 FinallyContext* innermostFinallyContext = nullptr; 4845 while (topScope >= bottomScope) { 4846 if (topScope->isFinallyScope()) { 4847 FinallyContext* finallyContext = &topScope->finallyContext; 4848 if (!innermostFinallyContext) 4849 innermostFinallyContext = finallyContext; 4850 finallyContext->setHandlesReturns(); 4851 } 4852 --topScope; 4853 } 4854 if (!innermostFinallyContext) 4855 return false; // No finallys to thread through. 4856 4857 emitSetCompletionType(CompletionType::Return); 4858 emitSetCompletionValue(returnRegister); 4859 emitJump(innermostFinallyContext->finallyLabel()); 4860 return true; // We'll be jumping to a finally block. 4861 } 4862 4863 void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, RegisterID* completionTypeRegister, Label* normalCompletionLabel) 4864 { 4865 if (context.numberOfBreaksOrContinues() || context.handlesReturns()) { 4866 emitJumpIf(op_stricteq, completionTypeRegister, CompletionType::Normal, normalCompletionLabel); 4867 4868 FinallyContext* outerContext = context.outerContext(); 4869 4870 size_t numberOfJumps = context.numberOfJumps(); 4871 ASSERT(outerContext || numberOfJumps == context.numberOfBreaksOrContinues()); 4872 4873 for (size_t i = 0; i < numberOfJumps; i++) { 4874 RefPtr<Label> nextLabel = newLabel(); 4875 auto& jump = context.jumps(i); 4876 emitJumpIf(op_nstricteq, completionTypeRegister, jump.jumpID, nextLabel.get()); 4877 4878 restoreScopeRegister(jump.targetLexicalScopeIndex); 4879 emitSetCompletionType(CompletionType::Normal); 4880 emitJump(jump.targetLabel.get()); 4881 4882 emitLabel(nextLabel.get()); 4883 } 4884 4885 if (outerContext) { 4886 // We are not the outermost finally. 4887 bool hasBreaksOrContinuesNotCoveredByJumps = context.numberOfBreaksOrContinues() > numberOfJumps; 4888 if (hasBreaksOrContinuesNotCoveredByJumps || context.handlesReturns()) 4889 emitJumpIf(op_nstricteq, completionTypeRegister, CompletionType::Throw, outerContext->finallyLabel()); 4890 4891 } else { 4892 // We are the outermost finally. 4893 if (context.handlesReturns()) { 4894 RefPtr<Label> notReturnLabel = newLabel(); 4895 emitJumpIf(op_nstricteq, completionTypeRegister, CompletionType::Return, notReturnLabel.get()); 4896 4897 emitWillLeaveCallFrameDebugHook(); 4898 emitReturn(completionValueRegister(), ReturnFrom::Finally); 4899 4900 emitLabel(notReturnLabel.get()); 4901 } 4902 } 4903 } 4904 emitJumpIf(op_nstricteq, completionTypeRegister, CompletionType::Throw, normalCompletionLabel); 4905 emitThrow(completionValueRegister()); 4906 } 4907 4908 bool BytecodeGenerator::allocateCompletionRecordRegisters() 4909 { 4910 if (m_completionTypeRegister) 4911 return false; 4912 4913 ASSERT(!m_completionValueRegister); 4914 m_completionTypeRegister = newTemporary(); 4915 m_completionValueRegister = newTemporary(); 4916 4917 emitSetCompletionType(CompletionType::Normal); 4918 emitMoveEmptyValue(m_completionValueRegister.get()); 4919 return true; 4920 } 4921 4922 void BytecodeGenerator::releaseCompletionRecordRegisters() 4923 { 4924 ASSERT(m_completionTypeRegister && m_completionValueRegister); 4925 m_completionTypeRegister = nullptr; 4926 m_completionValueRegister = nullptr; 4927 } 4928 4929 void BytecodeGenerator::emitJumpIf(OpcodeID compareOpcode, RegisterID* completionTypeRegister, CompletionType type, Label* jumpTarget) 4930 { 4931 RefPtr<RegisterID> tempRegister = newTemporary(); 4932 RegisterID* valueConstant = addConstantValue(jsNumber(static_cast<int>(type))); 4933 OperandTypes operandTypes = OperandTypes(ResultType::numberTypeIsInt32(), ResultType::unknownType()); 4934 4935 auto equivalenceResult = emitBinaryOp(compareOpcode, tempRegister.get(), valueConstant, completionTypeRegister, operandTypes); 4936 emitJumpIfTrue(equivalenceResult, jumpTarget); 4922 4937 } 4923 4938 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
r210010 r210116 45 45 #include "UnlinkedCodeBlock.h" 46 46 #include <functional> 47 #include <wtf/CheckedArithmetic.h> 47 48 #include <wtf/HashTraits.h> 48 49 #include <wtf/PassRefPtr.h> … … 81 82 }; 82 83 84 // https://tc39.github.io/ecma262/#sec-completion-record-specification-type 85 // 86 // For the Break and Continue cases, instead of using the Break and Continue enum values 87 // below, we use the unique jumpID of the break and continue statement as the encoding 88 // for the CompletionType value. emitFinallyCompletion() uses this jumpID value later 89 // to determine the appropriate jump target to jump to after executing the relevant finally 90 // blocks. The jumpID is computed as: 91 // jumpID = bytecodeOffset (of the break/continue node) + CompletionType::NumberOfTypes. 92 // Hence, there won't be any collision between jumpIDs and CompletionType enums. 93 enum class CompletionType : int { 94 Normal, 95 Break, 96 Continue, 97 Return, 98 Throw, 99 100 NumberOfTypes 101 }; 102 103 inline CompletionType bytecodeOffsetToJumpID(unsigned offset) 104 { 105 int jumpIDAsInt = offset + static_cast<int>(CompletionType::NumberOfTypes); 106 ASSERT(jumpIDAsInt >= static_cast<int>(CompletionType::NumberOfTypes)); 107 return static_cast<CompletionType>(jumpIDAsInt); 108 } 109 110 struct FinallyJump { 111 FinallyJump(CompletionType jumpID, int targetLexicalScopeIndex, Label* targetLabel) 112 : jumpID(jumpID) 113 , targetLexicalScopeIndex(targetLexicalScopeIndex) 114 , targetLabel(targetLabel) 115 { } 116 117 CompletionType jumpID; 118 int targetLexicalScopeIndex; 119 RefPtr<Label> targetLabel; 120 }; 121 83 122 struct FinallyContext { 84 StatementNode* finallyBlock; 85 RegisterID* iterator; 86 ThrowableExpressionData* enumerationNode; 87 unsigned controlFlowScopeStackSize; 88 unsigned switchContextStackSize; 89 unsigned forInContextStackSize; 90 unsigned tryContextStackSize; 91 unsigned labelScopesSize; 92 unsigned lexicalScopeStackSize; 93 int finallyDepth; 94 int dynamicScopeDepth; 123 FinallyContext() { } 124 FinallyContext(FinallyContext* outerContext, Label* finallyLabel, int finallyDepth) 125 : m_outerContext(outerContext) 126 , m_finallyLabel(finallyLabel) 127 , m_finallyDepth(finallyDepth) 128 { 129 ASSERT(m_finallyDepth >= 0); 130 ASSERT(m_jumps.isEmpty()); 131 } 132 133 FinallyContext* outerContext() const { return m_outerContext; } 134 Label* finallyLabel() const { return m_finallyLabel; } 135 int depth() const { return m_finallyDepth; } 136 137 uint32_t numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues.unsafeGet(); } 138 void incNumberOfBreaksOrContinues() { m_numberOfBreaksOrContinues++; } 139 140 bool handlesReturns() const { return m_handlesReturns; } 141 void setHandlesReturns() { m_handlesReturns = true; } 142 143 void registerJump(CompletionType jumpID, int lexicalScopeIndex, Label* targetLabel) 144 { 145 m_jumps.append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel)); 146 } 147 148 size_t numberOfJumps() const { return m_jumps.size(); } 149 FinallyJump& jumps(size_t i) { return m_jumps[i]; } 150 151 private: 152 FinallyContext* m_outerContext { nullptr }; 153 Label* m_finallyLabel { nullptr }; 154 int m_finallyDepth { 0 }; 155 Checked<uint32_t, WTF::CrashOnOverflow> m_numberOfBreaksOrContinues; 156 bool m_handlesReturns { false }; 157 Vector<FinallyJump> m_jumps; 95 158 }; 96 159 97 160 struct ControlFlowScope { 98 bool isFinallyBlock; 161 typedef uint8_t Type; 162 enum { 163 Label, 164 Finally 165 }; 166 ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext&& finallyContext = FinallyContext()) 167 : type(type) 168 , lexicalScopeIndex(lexicalScopeIndex) 169 , finallyContext(std::forward<FinallyContext>(finallyContext)) 170 { } 171 172 bool isLabelScope() const { return type == Label; } 173 bool isFinallyScope() const { return type == Finally; } 174 175 Type type; 176 int lexicalScopeIndex; 99 177 FinallyContext finallyContext; 100 178 }; … … 606 684 RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*); 607 685 608 RegisterID* emitReturn(RegisterID* src); 686 enum class ReturnFrom { Normal, Finally }; 687 RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal); 609 688 RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); } 610 689 … … 627 706 PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target); 628 707 PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target); 629 void emitPopScopes(RegisterID* srcDst, int targetScopeDepth);630 708 631 709 void emitEnter(); … … 650 728 RegisterID* emitIsSet(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSSetType); } 651 729 RegisterID* emitIsObject(RegisterID* dst, RegisterID* src); 730 RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src); 652 731 RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src); 653 732 RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src); … … 664 743 665 744 // Start a try block. 'start' must have been emitted. 666 TryData* pushTry(Label* start );745 TryData* pushTry(Label* start, Label* handlerLabel, HandlerType); 667 746 // End a try block. 'end' must have been emitted. 668 void popTryAndEmitCatch(TryData*, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType); 747 void popTry(TryData*, Label* end); 748 void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister); 749 750 private: 751 static const int CurrentLexicalScopeIndex = -2; 752 static const int OutermostLexicalScopeIndex = -1; 753 754 int currentLexicalScopeIndex() const 755 { 756 int size = static_cast<int>(m_lexicalScopeStack.size()); 757 ASSERT(static_cast<size_t>(size) == m_lexicalScopeStack.size()); 758 ASSERT(size >= 0); 759 if (!size) 760 return OutermostLexicalScopeIndex; 761 return size - 1; 762 } 763 764 public: 765 void restoreScopeRegister(); 766 void restoreScopeRegister(int lexicalScopeIndex); 767 768 int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth); 669 769 670 770 void emitThrow(RegisterID* exc) … … 699 799 void emitWillLeaveCallFrameDebugHook(); 700 800 701 bool isInFinallyBlock() { return m_finallyDepth > 0; } 702 703 void pushFinallyControlFlowScope(StatementNode* finallyBlock); 704 void popFinallyControlFlowScope(); 705 void pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* enumerationNode); 706 void popIteratorCloseControlFlowScope(); 801 class CompletionRecordScope { 802 public: 803 CompletionRecordScope(BytecodeGenerator& generator, bool needCompletionRecordRegisters = true) 804 : m_generator(generator) 805 { 806 if (needCompletionRecordRegisters && m_generator.allocateCompletionRecordRegisters()) 807 m_needToReleaseOnDestruction = true; 808 } 809 ~CompletionRecordScope() 810 { 811 if (m_needToReleaseOnDestruction) 812 m_generator.releaseCompletionRecordRegisters(); 813 } 814 815 private: 816 BytecodeGenerator& m_generator; 817 bool m_needToReleaseOnDestruction { false }; 818 }; 819 820 RegisterID* completionTypeRegister() const 821 { 822 ASSERT(m_completionTypeRegister); 823 return m_completionTypeRegister.get(); 824 } 825 RegisterID* completionValueRegister() const 826 { 827 ASSERT(m_completionValueRegister); 828 return m_completionValueRegister.get(); 829 } 830 831 void emitSetCompletionType(CompletionType type) 832 { 833 emitLoad(completionTypeRegister(), JSValue(static_cast<int>(type))); 834 } 835 void emitSetCompletionValue(RegisterID* reg) 836 { 837 emitMove(completionValueRegister(), reg); 838 } 839 840 void emitJumpIf(OpcodeID compareOpcode, RegisterID* completionTypeRegister, CompletionType, Label* jumpTarget); 841 842 bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget); 843 bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister); 844 void emitFinallyCompletion(FinallyContext&, RegisterID* completionTypeRegister, Label* normalCompletionLabel); 845 846 private: 847 bool allocateCompletionRecordRegisters(); 848 void releaseCompletionRecordRegisters(); 849 850 public: 851 FinallyContext* pushFinallyControlFlowScope(Label* finallyLabel); 852 FinallyContext popFinallyControlFlowScope(); 707 853 708 854 void pushIndexedForInScope(RegisterID* local, RegisterID* index); … … 798 944 void allocateCalleeSaveSpace(); 799 945 void allocateAndEmitScope(); 800 void emitComplexPopScopes(RegisterID*, ControlFlowScope* topScope, ControlFlowScope* bottomScope);801 946 802 947 typedef HashMap<double, JSValue> NumberMap; … … 936 1081 RegisterID* m_promiseCapabilityRegister { nullptr }; 937 1082 1083 RefPtr<RegisterID> m_completionTypeRegister; 1084 RefPtr<RegisterID> m_completionValueRegister; 1085 1086 FinallyContext* m_currentFinallyContext { nullptr }; 1087 938 1088 SegmentedVector<RegisterID*, 16> m_localRegistersForCalleeSaveRegisters; 939 1089 SegmentedVector<RegisterID, 32> m_constantPoolRegisters; … … 950 1100 void popLocalControlFlowScope(); 951 1101 952 Vector<ControlFlowScope, 0, UnsafeVectorOverflow> m_controlFlowScopeStack; 1102 // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it. 1103 // https://bugs.webkit.org/show_bug.cgi?id=165980 1104 SegmentedVector<ControlFlowScope, 16> m_controlFlowScopeStack; 953 1105 Vector<SwitchInfo> m_switchContextStack; 954 1106 Vector<RefPtr<ForInContext>> m_forInContextStack; -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r210023 r210116 2999 2999 ASSERT(scope); 3000 3000 3001 generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth()); 3002 generator.emitJump(scope->continueTarget()); 3001 bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope->scopeDepth(), scope->continueTarget()); 3002 if (!hasFinally) { 3003 int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope->scopeDepth()); 3004 generator.restoreScopeRegister(lexicalScopeIndex); 3005 generator.emitJump(scope->continueTarget()); 3006 } 3003 3007 3004 3008 generator.emitProfileControlFlow(endOffset()); … … 3026 3030 ASSERT(scope); 3027 3031 3028 generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth()); 3029 generator.emitJump(scope->breakTarget()); 3032 bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope->scopeDepth(), scope->breakTarget()); 3033 if (!hasFinally) { 3034 int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope->scopeDepth()); 3035 generator.restoreScopeRegister(lexicalScopeIndex); 3036 generator.emitJump(scope->breakTarget()); 3037 } 3030 3038 3031 3039 generator.emitProfileControlFlow(endOffset()); … … 3044 3052 3045 3053 generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, divotStart(), divotEnd()); 3046 if (generator.isInFinallyBlock()) { 3047 returnRegister = generator.emitMove(generator.newTemporary(),returnRegister.get());3048 generator.emitPopScopes(generator.scopeRegister(), 0);3049 }3050 3051 generator.emitWillLeaveCallFrameDebugHook();3052 generator.emitReturn(returnRegister.get()); 3053 generator.emitProfileControlFlow(endOffset()); 3054 3055 bool hasFinally = generator.emitReturnViaFinallyIfNeeded(returnRegister.get()); 3056 if (!hasFinally) { 3057 generator.emitWillLeaveCallFrameDebugHook(); 3058 generator.emitReturn(returnRegister.get()); 3059 } 3060 3061 generator.emitProfileControlFlow(endOffset()); 3054 3062 // Emitting an unreachable return here is needed in case this op_profile_control_flow is the 3055 3063 // last opcode in a CodeBlock because a CodeBlock's instructions must end with a terminal opcode. … … 3280 3288 3281 3289 ASSERT(m_catchBlock || m_finallyBlock); 3290 BytecodeGenerator::CompletionRecordScope completionRecordScope(generator, m_finallyBlock); 3291 3292 RefPtr<Label> catchLabel; 3293 RefPtr<Label> catchEndLabel; 3294 RefPtr<Label> finallyViaThrowLabel; 3295 RefPtr<Label> finallyLabel; 3296 RefPtr<Label> finallyEndLabel; 3282 3297 3283 3298 RefPtr<Label> tryStartLabel = generator.newLabel(); 3284 3299 generator.emitLabel(tryStartLabel.get()); 3285 3300 3301 if (m_finallyBlock) { 3302 finallyViaThrowLabel = generator.newLabel(); 3303 finallyLabel = generator.newLabel(); 3304 finallyEndLabel = generator.newLabel(); 3305 3306 generator.pushFinallyControlFlowScope(finallyLabel.get()); 3307 } 3308 if (m_catchBlock) { 3309 catchLabel = generator.newLabel(); 3310 catchEndLabel = generator.newLabel(); 3311 } 3312 3313 Label* tryHandlerLabel = m_catchBlock ? catchLabel.get() : finallyViaThrowLabel.get(); 3314 HandlerType tryHandlerType = m_catchBlock ? HandlerType::Catch : HandlerType::Finally; 3315 TryData* tryData = generator.pushTry(tryStartLabel.get(), tryHandlerLabel, tryHandlerType); 3316 3317 generator.emitNode(dst, m_tryBlock); 3318 3286 3319 if (m_finallyBlock) 3287 generator.pushFinallyControlFlowScope(m_finallyBlock); 3288 TryData* tryData = generator.pushTry(tryStartLabel.get()); 3289 3290 generator.emitNode(dst, m_tryBlock); 3320 generator.emitJump(finallyLabel.get()); 3321 else 3322 generator.emitJump(catchEndLabel.get()); 3323 3324 RefPtr<Label> endTryLabel = generator.emitLabel(generator.newLabel().get()); 3325 generator.popTry(tryData, endTryLabel.get()); 3291 3326 3292 3327 if (m_catchBlock) { 3293 RefPtr<Label> catchEndLabel = generator.newLabel();3294 3295 // Normal path: jump over the catch block.3296 generator.emitJump(catchEndLabel.get());3297 3298 3328 // Uncaught exception path: the catch block. 3299 RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); 3300 RefPtr<RegisterID> exceptionRegister = generator.newTemporary(); 3329 generator.emitLabel(catchLabel.get()); 3301 3330 RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); 3302 generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), here.get(), HandlerType::Catch); 3303 3331 RegisterID* unused = generator.newTemporary(); 3332 generator.emitCatch(unused, thrownValueRegister.get()); 3333 generator.restoreScopeRegister(); 3334 3335 TryData* tryData = nullptr; 3304 3336 if (m_finallyBlock) { 3305 3337 // If the catch block throws an exception and we have a finally block, then the finally 3306 3338 // block should "catch" that exception. 3307 tryData = generator.pushTry( here.get());3339 tryData = generator.pushTry(catchLabel.get(), finallyViaThrowLabel.get(), HandlerType::Finally); 3308 3340 } 3309 3341 … … 3317 3349 generator.emitLoad(thrownValueRegister.get(), jsUndefined()); 3318 3350 generator.emitPopCatchScope(m_lexicalVariables); 3351 3352 if (m_finallyBlock) { 3353 generator.emitSetCompletionType(CompletionType::Normal); 3354 generator.emitJump(finallyLabel.get()); 3355 generator.popTry(tryData, finallyViaThrowLabel.get()); 3356 } 3357 3319 3358 generator.emitLabel(catchEndLabel.get()); 3359 generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1); 3320 3360 } 3321 3361 3322 3362 if (m_finallyBlock) { 3323 RefPtr<Label> preFinallyLabel = generator.emitLabel(generator.newLabel().get()); 3324 3325 generator.popFinallyControlFlowScope(); 3326 3327 RefPtr<Label> finallyEndLabel = generator.newLabel(); 3363 FinallyContext finallyContext = generator.popFinallyControlFlowScope(); 3364 3365 // Entry to the finally block for CompletionType::Throw. 3366 generator.emitLabel(finallyViaThrowLabel.get()); 3367 RegisterID* unused = generator.newTemporary(); 3368 generator.emitCatch(generator.completionValueRegister(), unused); 3369 generator.emitSetCompletionType(CompletionType::Throw); 3370 3371 // Entry to the finally block for CompletionTypes other than Throw. 3372 generator.emitLabel(finallyLabel.get()); 3373 generator.restoreScopeRegister(); 3374 3375 RefPtr<RegisterID> savedCompletionTypeRegister = generator.newTemporary(); 3376 generator.emitMove(savedCompletionTypeRegister.get(), generator.completionTypeRegister()); 3328 3377 3329 3378 int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1; 3330 3331 // Normal path: run the finally code, and jump to the end.3332 3379 generator.emitProfileControlFlow(finallyStartOffset); 3333 3380 generator.emitNodeInTailPosition(dst, m_finallyBlock); 3334 generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1); 3335 generator.emitJump(finallyEndLabel.get()); 3336 3337 // Uncaught exception path: invoke the finally block, then re-throw the exception. 3338 RefPtr<RegisterID> exceptionRegister = generator.newTemporary(); 3339 RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); 3340 generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), preFinallyLabel.get(), HandlerType::Finally); 3341 generator.emitProfileControlFlow(finallyStartOffset); 3342 generator.emitNodeInTailPosition(dst, m_finallyBlock); 3343 generator.emitThrow(exceptionRegister.get()); 3344 3381 3382 generator.emitFinallyCompletion(finallyContext, savedCompletionTypeRegister.get(), finallyEndLabel.get()); 3345 3383 generator.emitLabel(finallyEndLabel.get()); 3346 3384 generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1); 3347 } else 3348 generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1); 3349 3385 } 3350 3386 } 3351 3387 -
trunk/Source/WTF/ChangeLog
r210010 r210116 1 2016-12-22 Mark Lam <mark.lam@apple.com> 2 3 De-duplicate finally blocks. 4 https://bugs.webkit.org/show_bug.cgi?id=160168 5 6 Reviewed by Saam Barati. 7 8 Added some methods to bring SegmentedVector closer to parity with Vector. 9 10 * wtf/SegmentedVector.h: 11 (WTF::SegmentedVector::first): 12 (WTF::SegmentedVector::last): 13 (WTF::SegmentedVector::takeLast): 14 1 15 2016-12-19 Mark Lam <mark.lam@apple.com> 2 16 -
trunk/Source/WTF/wtf/SegmentedVector.h
r210010 r210116 128 128 } 129 129 130 T& first() 131 { 132 ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty()); 133 return at(0); 134 } 135 const T& first() const 136 { 137 ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty()); 138 return at(0); 139 } 130 140 T& last() 131 141 { 142 ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty()); 132 143 return at(size() - 1); 144 } 145 const T& last() const 146 { 147 ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty()); 148 return at(size() - 1); 149 } 150 151 T takeLast() 152 { 153 ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty()); 154 T result = WTFMove(last()); 155 --m_size; 156 return result; 133 157 } 134 158
Note:
See TracChangeset
for help on using the changeset viewer.