Changeset 242591 in webkit


Ignore:
Timestamp:
Mar 6, 2019 9:09:53 PM (5 years ago)
Author:
mark.lam@apple.com
Message:

Fix incorrect handling of try-finally completion values.
https://bugs.webkit.org/show_bug.cgi?id=195131
<rdar://problem/46222079>

Reviewed by Saam Barati and Yusuke Suzuki.

JSTests:

Added many permutations of new test case to test-finally.js. test-finally.js has
been run on Chrome and Firefox as a sanity check, and we confirmed that all the
tests passes there as well.

  • stress/test-finally.js:

Source/JavaScriptCore:

Consider the following:

function foo() { line 1

try {

return 42; line 3

} finally {

for (var j = 0; j < 1; j++) { line 5

try {

throw ; line 7

} finally {

continue; line 9

}

}

} line 11

}
var result = foo();

With the current (before fix) code base, result will be the exception object thrown
at line 7. The expected result should be 42, returned at line 3.

The bug is that we were previously only using one set of completion type and
value registers for the entire function. This is inadequate because the outer
try-finally needs to preserve its own completion type and value ({ Return, 42 }
in this case) in order to be able to complete correctly.

One might be deceived into thinking that the above example should complete with
the exception thrown at line 7. However, according to Section 13.15.8 of the
ECMAScript spec, the 'continue' in the finally at line 9 counts as an abrupt
completion. As a result, it overrides the throw from line 7. After the continue,
execution resumes at the top of the loop at line 5, followed by a normal completion
at line 11.

Also according to Section 13.15.8, given that the completion type of the outer
finally is normal, the resultant completion of the outer try-finally should be
the completion of the outer try block i.e. { Return, 42 }.

This patch makes the following changes:

  1. Fix handling of finally completion to use a unique set of completion type and value registers for each FinallyContext.
  1. Move the setting of Throw completion type to the out of line exception handler. This makes the mainline code slightly less branchy.
  1. Introduce emitOutOfLineCatchHandler(), emitOutOfLineFinallyHandler(), and emitOutOfLineExceptionHandler() to make it clearer that these are not emitting bytecode inline. Also, these make it clearer when we're emitting a handler for a catch vs a finally.
  1. Allocate the FinallyContext on the stack instead of as a member of the heap allocated ControlFlowScope. This simplifies its life-cycle management and reduces the amount of needed copying.
  1. Update emitFinallyCompletion() to propagate the completion type and value to the outer FinallyContext when needed.
  1. Fix emitJumpIf() to use the right order of operands. Previously, we were only using it to do op_stricteq and op_nstricteq comparisons. So, the order wasn't important. We now use it to also do op_beloweq comparisons. Hence, the order needs to be corrected.
  1. Remove the unused CompletionType::Break and Continue. These are encoded with the jumpIDs of the jump targets instead.

Relevant specifications:
Section 13.15.8: https://www.ecma-international.org/ecma-262/9.0/index.html#sec-try-statement-runtime-semantics-evaluation
Section 6.3.2.4: https://www.ecma-international.org/ecma-262/9.0/index.html#sec-updateempty

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::FinallyContext::FinallyContext):
(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::pushFinallyControlFlowScope):
(JSC::BytecodeGenerator::popFinallyControlFlowScope):
(JSC::BytecodeGenerator::emitOutOfLineCatchHandler):
(JSC::BytecodeGenerator::emitOutOfLineFinallyHandler):
(JSC::BytecodeGenerator::emitOutOfLineExceptionHandler):
(JSC::BytecodeGenerator::emitEnumeration):
(JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded):
(JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded):
(JSC::BytecodeGenerator::emitFinallyCompletion):
(JSC::BytecodeGenerator::emitJumpIf):
(JSC::BytecodeGenerator::emitCatch): Deleted.
(JSC::BytecodeGenerator::allocateCompletionRecordRegisters): Deleted.
(JSC::BytecodeGenerator::releaseCompletionRecordRegisters): Deleted.

  • bytecompiler/BytecodeGenerator.h:

(JSC::FinallyContext::completionTypeRegister const):
(JSC::FinallyContext::completionValueRegister const):
(JSC::ControlFlowScope::ControlFlowScope):
(JSC::BytecodeGenerator::emitLoad):
(JSC::BytecodeGenerator::CompletionRecordScope::CompletionRecordScope): Deleted.
(JSC::BytecodeGenerator::CompletionRecordScope::~CompletionRecordScope): Deleted.
(JSC::BytecodeGenerator::completionTypeRegister const): Deleted.
(JSC::BytecodeGenerator::completionValueRegister const): Deleted.
(JSC::BytecodeGenerator::emitSetCompletionType): Deleted.
(JSC::BytecodeGenerator::emitSetCompletionValue): Deleted.

  • bytecompiler/NodesCodegen.cpp:

(JSC::TryNode::emitBytecode):

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r242569 r242591  
     12019-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
    1152019-03-06  Saam Barati  <sbarati@apple.com>
    216
  • trunk/JSTests/stress/test-finally.js

    r221137 r242591  
    726726}, "a,b,c,y,d,z,f", NothingReturned, 600);
    727727
     728test(() => {
     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
     747test(() => {
     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
     766test(() => {
     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
     788test(() => {
     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
     812test(() => {
     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
     836test(() => {
     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
     860test(() => {
     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
     884test(() => {
     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
     908test(() => {
     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
     932test(() => {
     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
     957test(() => {
     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
     982test(() => {
     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
     1007test(() => {
     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
     1032test(() => {
     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
     1057test(() => {
     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
     1082test(() => {
     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
     1107test(() => {
     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
     1132test(() => {
     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
     1157test(() => {
     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
     1182test(() => {
     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
     1207test(() => {
     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
     1232test(() => {
     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
     1257test(() => {
     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
     1282test(() => {
     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
     1307test(() => {
     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
     1332test(() => {
     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
     1357test(() => {
     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
    7281382// No throw or return in for-of loop.
    7291383test(() => {
  • trunk/Source/JavaScriptCore/ChangeLog

    r242585 r242591  
     12019-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
    11082019-03-06  Saam Barati  <sbarati@apple.com>
    2109
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r241571 r242591  
    184184}
    185185
     186FinallyContext::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
    186197ParserError BytecodeGenerator::generate()
    187198{
     
    246257    }
    247258
    248     for (auto& tuple : m_catchesToEmit) {
     259    for (auto& tuple : m_exceptionHandlersToEmit) {
    249260        Ref<Label> realCatchTarget = newLabel();
     261        TryData* tryData = std::get<0>(tuple);
     262
    250263        OpCatch::emit(this, std::get<1>(tuple), std::get<2>(tuple));
    251264        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        }
    252273        m_codeBlock->addJumpTarget(m_lastInstruction.offset());
    253274
    254275
    255         TryData* tryData = std::get<0>(tuple);
    256276        emitJump(tryData->target.get());
    257277        tryData->target = WTFMove(realCatchTarget);
     
    789809
    790810        RefPtr<RegisterID> thrownValue = newTemporary();
    791         RegisterID* unused = newTemporary();
    792         emitCatch(unused, thrownValue.get(), tryFormalParametersData);
     811        emitOutOfLineCatchHandler(thrownValue.get(), nullptr, tryFormalParametersData);
    793812
    794813        // return promiseCapability.@reject(thrownValue)
     
    35143533}
    35153534
    3516 FinallyContext* BytecodeGenerator::pushFinallyControlFlowScope(Label& finallyLabel)
    3517 {
    3518     ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), FinallyContext(m_currentFinallyContext, finallyLabel));
     3535void BytecodeGenerator::pushFinallyControlFlowScope(FinallyContext& finallyContext)
     3536{
     3537    ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), &finallyContext);
    35193538    m_controlFlowScopeStack.append(WTFMove(scope));
    35203539
    35213540    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
     3544void BytecodeGenerator::popFinallyControlFlowScope()
    35273545{
    35283546    ASSERT(m_controlFlowScopeStack.size());
     
    35323550    m_currentFinallyContext = m_currentFinallyContext->outerContext();
    35333551    m_finallyDepth--;
    3534     return m_controlFlowScopeStack.takeLast().finallyContext;
     3552    m_controlFlowScopeStack.removeLast();
    35353553}
    35363554
     
    36433661}
    36443662
    3645 void BytecodeGenerator::emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, TryData* data)
    3646 {
    3647     m_catchesToEmit.append(CatchEntry { data, exceptionRegister, thrownValueRegister });
     3663void BytecodeGenerator::emitOutOfLineCatchHandler(RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData* data)
     3664{
     3665    RegisterID* unused = newTemporary();
     3666    emitOutOfLineExceptionHandler(unused, thrownValueRegister, completionTypeRegister, data);
     3667}
     3668
     3669void BytecodeGenerator::emitOutOfLineFinallyHandler(RegisterID* exceptionRegister, RegisterID* completionTypeRegister, TryData* data)
     3670{
     3671    RegisterID* unused = newTemporary();
     3672    ASSERT(completionTypeRegister);
     3673    emitOutOfLineExceptionHandler(exceptionRegister, unused, completionTypeRegister, data);
     3674}
     3675
     3676void 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 });
    36483680}
    36493681
     
    39603992    ASSERT(!isForAwait || (isForAwait && isAsyncFunctionParseMode(parseMode())));
    39613993
    3962     CompletionRecordScope completionRecordScope(*this);
    3963 
    39643994    RefPtr<RegisterID> subject = newTemporary();
    39653995    emitNode(subject.get(), subjectNode);
     
    39754005
    39764006    // 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);
    39784009
    39794010    {
     
    40014032            Ref<Label> finallyBodyLabel = newLabel();
    40024033            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());
    40084037            emitJump(finallyBodyLabel.get());
    40094038
     
    40114040            moveEmptyValue(finallyExceptionRegister.get());
    40124041
     4042            // Finally fall through case.
    40134043            emitLabel(finallyBodyLabel.get());
    40144044            restoreScopeRegister();
     
    40344064
    40354065            emitLabel(finallyDone.get());
    4036             emitFinallyCompletion(*finallyContext, completionTypeRegister(), endCatchLabel.get());
     4066            emitFinallyCompletion(finallyContext, endCatchLabel.get());
    40374067
    40384068            popTry(returnCallTryData, finallyDone.get());
     
    40454075            {
    40464076                emitLabel(catchLabel.get());
     4077
    40474078                RefPtr<RegisterID> exceptionRegister = newTemporary();
    4048                 RegisterID* unused = newTemporary();
    4049                 emitCatch(exceptionRegister.get(), unused, returnCallTryData);
     4079                emitOutOfLineFinallyHandler(exceptionRegister.get(), finallyContext.completionTypeRegister(), returnCallTryData);
    40504080                // Since this is a synthesized catch block and we're guaranteed to never need
    40514081                // to resolve any symbols from the scope, we can skip restoring the scope
     
    46864716        ControlFlowScope* scope = &m_controlFlowScopeStack[scopeIndex--];
    46874717        if (scope->isFinallyScope()) {
    4688             FinallyContext* finallyContext = &scope->finallyContext;
     4718            FinallyContext* finallyContext = scope->finallyContext;
    46894719            if (!innermostFinallyContext)
    46904720                innermostFinallyContext = finallyContext;
     
    47004730    outermostFinallyContext->registerJump(jumpID, lexicalScopeIndex, jumpTarget);
    47014731
    4702     emitSetCompletionType(jumpID);
     4732    emitLoad(innermostFinallyContext->completionTypeRegister(), jumpID);
    47034733    emitJump(*innermostFinallyContext->finallyLabel());
    47044734    return true; // We'll be jumping to a finally block.
     
    47164746        ControlFlowScope* scope = &m_controlFlowScopeStack[scopeIndex];
    47174747        if (scope->isFinallyScope()) {
    4718             FinallyContext* finallyContext = &scope->finallyContext;
     4748            FinallyContext* finallyContext = scope->finallyContext;
    47194749            if (!innermostFinallyContext)
    47204750                innermostFinallyContext = finallyContext;
     
    47254755        return false; // No finallys to thread through.
    47264756
    4727     emitSetCompletionType(CompletionType::Return);
    4728     emitSetCompletionValue(returnRegister);
     4757    emitLoad(innermostFinallyContext->completionTypeRegister(), CompletionType::Return);
     4758    move(innermostFinallyContext->completionValueRegister(), returnRegister);
    47294759    emitJump(*innermostFinallyContext->finallyLabel());
    47304760    return true; // We'll be jumping to a finally block.
    47314761}
    47324762
    4733 void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, RegisterID* completionTypeRegister, Label& normalCompletionLabel)
     4763void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, Label& normalCompletionLabel)
    47344764{
    47354765    if (context.numberOfBreaksOrContinues() || context.handlesReturns()) {
    4736         emitJumpIf<OpStricteq>(completionTypeRegister, CompletionType::Normal, normalCompletionLabel);
     4766        emitJumpIf<OpStricteq>(context.completionTypeRegister(), CompletionType::Normal, normalCompletionLabel);
    47374767
    47384768        FinallyContext* outerContext = context.outerContext();
     
    47414771        ASSERT(outerContext || numberOfJumps == context.numberOfBreaksOrContinues());
    47424772
     4773        // Handle Break or Continue completions that jumps into this FinallyContext.
    47434774        for (size_t i = 0; i < numberOfJumps; i++) {
    47444775            Ref<Label> nextLabel = newLabel();
    47454776            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.
    47484783            restoreScopeRegister(jump.targetLexicalScopeIndex);
    4749             emitSetCompletionType(CompletionType::Normal);
     4784            emitLoad(context.completionTypeRegister(), CompletionType::Normal);
    47504785            emitJump(jump.targetLabel.get());
    47514786
     
    47534788        }
    47544789
     4790        // Handle completions that take us out of this FinallyContext.
    47554791        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
    47574806            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            }
    47604823
    47614824        } else {
     
    47634826            if (context.handlesReturns()) {
    47644827                Ref<Label> notReturnLabel = newLabel();
    4765                 emitJumpIf<OpNstricteq>(completionTypeRegister, CompletionType::Return, notReturnLabel.get());
     4828                emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Return, notReturnLabel.get());
    47664829
    47674830                emitWillLeaveCallFrameDebugHook();
    4768                 emitReturn(completionValueRegister(), ReturnFrom::Finally);
    4769                
     4831                emitReturn(context.completionValueRegister(), ReturnFrom::Finally);
     4832
    47704833                emitLabel(notReturnLabel.get());
    47714834            }
    47724835        }
    47734836    }
    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());
    47974846}
    47984847
     
    48044853    OperandTypes operandTypes = OperandTypes(ResultType::numberTypeIsInt32(), ResultType::unknownType());
    48054854
    4806     auto equivalenceResult = emitBinaryOp<CompareOp>(tempRegister.get(), valueConstant, completionTypeRegister, operandTypes);
     4855    auto equivalenceResult = emitBinaryOp<CompareOp>(tempRegister.get(), completionTypeRegister, valueConstant, operandTypes);
    48074856    emitJumpIfTrue(equivalenceResult, jumpTarget);
    48084857}
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r241571 r242591  
    11/*
    2  * Copyright (C) 2008-2018 Apple Inc. All rights reserved.
     2 * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
    33 * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
    44 * Copyright (C) 2012 Igalia, S.L.
     
    9999    enum class CompletionType : int {
    100100        Normal,
    101         Break,
    102         Continue,
     101        Throw,
    103102        Return,
    104         Throw,
    105        
    106103        NumberOfTypes
    107104    };
     
    126123    };
    127124
    128     struct FinallyContext {
     125    class FinallyContext {
     126    public:
    129127        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);
    136129
    137130        FinallyContext* outerContext() const { return m_outerContext; }
    138131        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(); }
    139135
    140136        uint32_t numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues.unsafeGet(); }
     
    158154        bool m_handlesReturns { false };
    159155        Vector<FinallyJump> m_jumps;
     156        struct {
     157            RefPtr<RegisterID> typeRegister;
     158            RefPtr<RegisterID> valueRegister;
     159        } m_completionRecord;
    160160    };
    161161
     
    166166            Finally
    167167        };
    168         ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext&& finallyContext = FinallyContext())
     168        ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext* finallyContext = nullptr)
    169169            : type(type)
    170170            , lexicalScopeIndex(lexicalScopeIndex)
    171             , finallyContext(std::forward<FinallyContext>(finallyContext))
     171            , finallyContext(finallyContext)
    172172        { }
    173173
     
    177177        Type type;
    178178        int lexicalScopeIndex;
    179         FinallyContext finallyContext;
     179        FinallyContext* finallyContext;
    180180    };
    181181
     
    365365
    366366        friend class BoundLabel;
     367        friend class FinallyContext;
    367368        friend class Label;
    368369        friend class IndexedForInContext;
     
    876877        // End a try block. 'end' must have been emitted.
    877878        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*);
    879882
    880883    private:
     
    891894            return size - 1;
    892895        }
     896
     897        void emitOutOfLineExceptionHandler(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData*);
    893898
    894899    public:
     
    930935        void emitWillLeaveCallFrameDebugHook();
    931936
    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)));
    969940        }
    970941
     
    974945        bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label& jumpTarget);
    975946        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);
    981948
    982949    public:
    983         FinallyContext* pushFinallyControlFlowScope(Label& finallyLabel);
    984         FinallyContext popFinallyControlFlowScope();
     950        void pushFinallyControlFlowScope(FinallyContext&);
     951        void popFinallyControlFlowScope();
    985952
    986953        void pushIndexedForInScope(RegisterID* local, RegisterID* index);
     
    12461213        RegisterID* m_promiseCapabilityRegister { nullptr };
    12471214
    1248         RefPtr<RegisterID> m_completionTypeRegister;
    1249         RefPtr<RegisterID> m_completionValueRegister;
    1250 
    12511215        FinallyContext* m_currentFinallyContext { nullptr };
    12521216
     
    13151279        CompactVariableMap::Handle m_cachedVariablesUnderTDZ;
    13161280
    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    };
    13211284
    13221285} // namespace JSC
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r241104 r242591  
    22*  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
    33*  Copyright (C) 2001 Peter Kelly (pmk@post.com)
    4 *  Copyright (C) 2003-2018 Apple Inc. All rights reserved.
     4*  Copyright (C) 2003-2019 Apple Inc. All rights reserved.
    55*  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
    66*  Copyright (C) 2007 Maks Orlovich
     
    35833583
    35843584    ASSERT(m_catchBlock || m_finallyBlock);
    3585     BytecodeGenerator::CompletionRecordScope completionRecordScope(generator, m_finallyBlock);
    35863585
    35873586    RefPtr<Label> catchLabel;
    35883587    RefPtr<Label> catchEndLabel;
    3589     RefPtr<Label> finallyViaThrowLabel;
    35903588    RefPtr<Label> finallyLabel;
    35913589    RefPtr<Label> finallyEndLabel;
    3592 
    3593     Ref<Label> tryStartLabel = generator.newLabel();
    3594     generator.emitLabel(tryStartLabel.get());
     3590    Optional<FinallyContext> finallyContext;
    35953591
    35963592    if (m_finallyBlock) {
    3597         finallyViaThrowLabel = generator.newLabel();
    35983593        finallyLabel = generator.newLabel();
    35993594        finallyEndLabel = generator.newLabel();
    36003595
    3601         generator.pushFinallyControlFlowScope(*finallyLabel);
     3596        finallyContext.emplace(generator, *finallyLabel);
     3597        generator.pushFinallyControlFlowScope(finallyContext.value());
    36023598    }
    36033599    if (m_catchBlock) {
     
    36063602    }
    36073603
    3608     Label& tryHandlerLabel = m_catchBlock ? *catchLabel : *finallyViaThrowLabel;
     3604    Ref<Label> tryLabel = generator.newEmittedLabel();
     3605    Label& tryHandlerLabel = m_catchBlock ? *catchLabel : *finallyLabel;
    36093606    HandlerType tryHandlerType = m_catchBlock ? HandlerType::Catch : HandlerType::Finally;
    3610     TryData* tryData = generator.pushTry(tryStartLabel.get(), tryHandlerLabel, tryHandlerType);
     3607    TryData* tryData = generator.pushTry(tryLabel.get(), tryHandlerLabel, tryHandlerType);
    36113608    TryData* finallyTryData = nullptr;
    36123609    if (!m_catchBlock && m_finallyBlock)
     
    36203617        generator.emitJump(*catchEndLabel);
    36213618
    3622     Ref<Label> endTryLabel = generator.newEmittedLabel();
    3623     generator.popTry(tryData, endTryLabel.get());
     3619    Ref<Label> tryEndLabel = generator.newEmittedLabel();
     3620    generator.popTry(tryData, tryEndLabel.get());
    36243621
    36253622    if (m_catchBlock) {
     
    36273624        generator.emitLabel(*catchLabel);
    36283625        RefPtr<RegisterID> thrownValueRegister = generator.newTemporary();
    3629         RegisterID* unused = generator.newTemporary();
    3630         generator.emitCatch(unused, thrownValueRegister.get(), tryData);
     3626        RegisterID* completionTypeRegister = m_finallyBlock ? finallyContext->completionTypeRegister() : nullptr;
     3627        generator.emitOutOfLineCatchHandler(thrownValueRegister.get(), completionTypeRegister, tryData);
    36313628        generator.restoreScopeRegister();
    36323629
     
    36343631            // If the catch block throws an exception and we have a finally block, then the finally
    36353632            // block should "catch" that exception.
    3636             finallyTryData = generator.pushTry(*catchLabel, *finallyViaThrowLabel, HandlerType::Finally);
     3633            finallyTryData = generator.pushTry(*catchLabel, *finallyLabel, HandlerType::Finally);
    36373634        }
    36383635
     
    36533650
    36543651        if (m_finallyBlock) {
    3655             generator.emitSetCompletionType(CompletionType::Normal);
     3652            generator.emitLoad(finallyContext->completionTypeRegister(), CompletionType::Normal);
    36563653            generator.emitJump(*finallyLabel);
    3657             generator.popTry(finallyTryData, *finallyViaThrowLabel);
     3654            generator.popTry(finallyTryData, *finallyLabel);
    36583655        }
    36593656
     
    36633660
    36643661    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);
    36723666
    36733667        // Entry to the finally block for CompletionTypes other than Throw.
     
    36753669        generator.restoreScopeRegister();
    36763670
    3677         RefPtr<RegisterID> savedCompletionTypeRegister = generator.newTemporary();
    3678         generator.move(savedCompletionTypeRegister.get(), generator.completionTypeRegister());
    3679 
    36803671        int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1;
    36813672        generator.emitProfileControlFlow(finallyStartOffset);
    36823673        generator.emitNodeInTailPosition(m_finallyBlock);
    36833674
    3684         generator.emitFinallyCompletion(finallyContext, savedCompletionTypeRegister.get(), *finallyEndLabel);
     3675        generator.emitFinallyCompletion(finallyContext.value(), *finallyEndLabel);
    36853676        generator.emitLabel(*finallyEndLabel);
    36863677        generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
Note: See TracChangeset for help on using the changeset viewer.