summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/gc
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/gc')
-rw-r--r--js/src/jit-test/tests/gc/bug-1004457.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1016016.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1017141.js25
-rw-r--r--js/src/jit-test/tests/gc/bug-1028863.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1032206.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1035371.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1039516.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1053676.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1055219.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1070638.js20
-rw-r--r--js/src/jit-test/tests/gc/bug-1075546.js30
-rw-r--r--js/src/jit-test/tests/gc/bug-1104162.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1108007.js23
-rw-r--r--js/src/jit-test/tests/gc/bug-1108836.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1109913.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1109922.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1123648.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1124563.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1124653.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1136597.js25
-rw-r--r--js/src/jit-test/tests/gc/bug-1137341.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1143706.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1144738.js32
-rw-r--r--js/src/jit-test/tests/gc/bug-1146696.js21
-rw-r--r--js/src/jit-test/tests/gc/bug-1148383.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1155455.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1157577.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1161303.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1161968.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1165966.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1171909.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1175755.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1177778.js15
-rw-r--r--js/src/jit-test/tests/gc/bug-1191576.js14
-rw-r--r--js/src/jit-test/tests/gc/bug-1206677.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1208994.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1209001.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1210607.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1214006.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1214781.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1214846.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1215363-1.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1215363-2.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1215363-3.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1215678.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1216607.js18
-rw-r--r--js/src/jit-test/tests/gc/bug-1218900-2.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1218900.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1221359.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1221747.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1223021.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1224710.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1226896.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1231386.js18
-rw-r--r--js/src/jit-test/tests/gc/bug-1232386.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1234410.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1236473.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1237153.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1238548.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1238555.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1238575-2.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1238575.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1238582.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1240503.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1240527.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1241731.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1242812.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1245520.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1246593.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1252329.js32
-rw-r--r--js/src/jit-test/tests/gc/bug-1253124.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1254108.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1258407.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1259306.js19
-rw-r--r--js/src/jit-test/tests/gc/bug-1259490.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1261329.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1263862.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1263871.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1263884.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1271110.js38
-rw-r--r--js/src/jit-test/tests/gc/bug-1276631.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1278832.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1280588.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1280889.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1282986.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1286244.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1287399.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1287869.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1292564.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1293127.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1294241.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1298356.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1301377.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1301496.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1303015.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1305220.js22
-rw-r--r--js/src/jit-test/tests/gc/bug-1308048.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1310589.js298
-rw-r--r--js/src/jit-test/tests/gc/bug-1311060.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1313347.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1315946.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1321597.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1322420.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1322648.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1323868.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1324512.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1325551.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1328251.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1332773.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1337414.js46
-rw-r--r--js/src/jit-test/tests/gc/bug-1338383.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1340010.js22
-rw-r--r--js/src/jit-test/tests/gc/bug-1342261.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1354480.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1357022.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1359252.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1370069.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1371908.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1374797.js27
-rw-r--r--js/src/jit-test/tests/gc/bug-1382431.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1384047.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1388701.js18
-rw-r--r--js/src/jit-test/tests/gc/bug-1390087.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1399889.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1401141.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1411302.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1413914.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1430752.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1435295.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1435321.js30
-rw-r--r--js/src/jit-test/tests/gc/bug-1439284.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1449887.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1456508.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1456536.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1459568.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1459860.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1461319.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1461448.js40
-rw-r--r--js/src/jit-test/tests/gc/bug-1462337.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1464872.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1468792.js18
-rw-r--r--js/src/jit-test/tests/gc/bug-1472734.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1478943.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1490042.js31
-rw-r--r--js/src/jit-test/tests/gc/bug-1491326.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1498177.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1505622.js54
-rw-r--r--js/src/jit-test/tests/gc/bug-1513991.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1514927.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1515993.js15
-rw-r--r--js/src/jit-test/tests/gc/bug-1517158.js21
-rw-r--r--js/src/jit-test/tests/gc/bug-1520778.js18
-rw-r--r--js/src/jit-test/tests/gc/bug-1530643.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1531018.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1531626.js61
-rw-r--r--js/src/jit-test/tests/gc/bug-1532376.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1540670.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1542279.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1542982.js20
-rw-r--r--js/src/jit-test/tests/gc/bug-1543014.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1543589.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1556155.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1557928.js19
-rw-r--r--js/src/jit-test/tests/gc/bug-1565272.js20
-rw-r--r--js/src/jit-test/tests/gc/bug-1568119.js21
-rw-r--r--js/src/jit-test/tests/gc/bug-1568740.js20
-rw-r--r--js/src/jit-test/tests/gc/bug-1569840.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1571439.js24
-rw-r--r--js/src/jit-test/tests/gc/bug-1573458.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1574877.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1578462.js59
-rw-r--r--js/src/jit-test/tests/gc/bug-1579025.js58
-rw-r--r--js/src/jit-test/tests/gc/bug-1585159.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1590176.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1590904.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1592487.js28
-rw-r--r--js/src/jit-test/tests/gc/bug-1593975.js21
-rw-r--r--js/src/jit-test/tests/gc/bug-1597970.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1600238.js21
-rw-r--r--js/src/jit-test/tests/gc/bug-1602741.js24
-rw-r--r--js/src/jit-test/tests/gc/bug-1603330.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1603917.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1605348.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1605633.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1607495.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1607665.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1607687.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1608355.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1610621.js1
-rw-r--r--js/src/jit-test/tests/gc/bug-1620195.js15
-rw-r--r--js/src/jit-test/tests/gc/bug-1620196.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1620209.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1620213.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1620221.js23
-rw-r--r--js/src/jit-test/tests/gc/bug-1628440.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1643913.js36
-rw-r--r--js/src/jit-test/tests/gc/bug-1644985-2.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1644985.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1647747-debugger-weakmark.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1648901.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1651345.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1652425.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-1652492.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1654186.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1655917.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1657554.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1660293.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1667336.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1671125.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1688749.js160
-rw-r--r--js/src/jit-test/tests/gc/bug-1689039.js232
-rw-r--r--js/src/jit-test/tests/gc/bug-1689794.js22
-rw-r--r--js/src/jit-test/tests/gc/bug-1691901.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1692221.js39
-rw-r--r--js/src/jit-test/tests/gc/bug-1695861.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1696880.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1696886.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1698543.js34
-rw-r--r--js/src/jit-test/tests/gc/bug-1699364.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1714530.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1723840.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1723841.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1736310.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1739972.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1744979.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1749298.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1755257.js1
-rw-r--r--js/src/jit-test/tests/gc/bug-1755874.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1756590.js19
-rw-r--r--js/src/jit-test/tests/gc/bug-1757573.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-1762771.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-1766648-markQueue.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1766656.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-1768813.js19
-rw-r--r--js/src/jit-test/tests/gc/bug-1770266.js2
-rw-r--r--js/src/jit-test/tests/gc/bug-1779833.js20
-rw-r--r--js/src/jit-test/tests/gc/bug-1787351.js1
-rw-r--r--js/src/jit-test/tests/gc/bug-1791363.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1791975.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-1792338.js14
-rw-r--r--js/src/jit-test/tests/gc/bug-1796901.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-1799678.js21
-rw-r--r--js/src/jit-test/tests/gc/bug-1802308.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1802478.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-1803233.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1804629-2.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1804629.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-1804637.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-1806976.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-1817598.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-1820543.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1822995.js19
-rw-r--r--js/src/jit-test/tests/gc/bug-1825671.js12
-rw-r--r--js/src/jit-test/tests/gc/bug-1825936.js24
-rw-r--r--js/src/jit-test/tests/gc/bug-1828396.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-1830921.js15
-rw-r--r--js/src/jit-test/tests/gc/bug-1852729.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-787703.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-820186.js297
-rw-r--r--js/src/jit-test/tests/gc/bug-821551.js16
-rw-r--r--js/src/jit-test/tests/gc/bug-824321.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-825326.js18
-rw-r--r--js/src/jit-test/tests/gc/bug-832103.js27
-rw-r--r--js/src/jit-test/tests/gc/bug-880816.js30
-rw-r--r--js/src/jit-test/tests/gc/bug-880886.js10
-rw-r--r--js/src/jit-test/tests/gc/bug-886551-1.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-886551-2.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-886560.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-886630.js109
-rw-r--r--js/src/jit-test/tests/gc/bug-889682-1.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-889682-2.js14
-rw-r--r--js/src/jit-test/tests/gc/bug-889682-3.js14
-rw-r--r--js/src/jit-test/tests/gc/bug-891773.js14
-rw-r--r--js/src/jit-test/tests/gc/bug-906236.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-906241.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-912813.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-913224.js1
-rw-r--r--js/src/jit-test/tests/gc/bug-913715.js31
-rw-r--r--js/src/jit-test/tests/gc/bug-919536.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-924690.js25
-rw-r--r--js/src/jit-test/tests/gc/bug-935022.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-939499.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-945275.js11
-rw-r--r--js/src/jit-test/tests/gc/bug-945280.js4
-rw-r--r--js/src/jit-test/tests/gc/bug-945285.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-950927.js31
-rw-r--r--js/src/jit-test/tests/gc/bug-952819.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-956324.js28
-rw-r--r--js/src/jit-test/tests/gc/bug-957110.js7
-rw-r--r--js/src/jit-test/tests/gc/bug-957114.js13
-rw-r--r--js/src/jit-test/tests/gc/bug-961741.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-961877.js14
-rw-r--r--js/src/jit-test/tests/gc/bug-969012.js60
-rw-r--r--js/src/jit-test/tests/gc/bug-978353.js6
-rw-r--r--js/src/jit-test/tests/gc/bug-978802.js17
-rw-r--r--js/src/jit-test/tests/gc/bug-981289.js8
-rw-r--r--js/src/jit-test/tests/gc/bug-981295.js9
-rw-r--r--js/src/jit-test/tests/gc/bug-985732.js84
-rw-r--r--js/src/jit-test/tests/gc/bug-993768.js13
-rw-r--r--js/src/jit-test/tests/gc/bug1116306.js8
-rw-r--r--js/src/jit-test/tests/gc/bug1146213.js10
-rw-r--r--js/src/jit-test/tests/gc/bug1191756.js18
-rw-r--r--js/src/jit-test/tests/gc/bug1246607.js15
-rw-r--r--js/src/jit-test/tests/gc/bug1282113.js6
-rw-r--r--js/src/jit-test/tests/gc/bug1283169.js5
-rw-r--r--js/src/jit-test/tests/gc/bug1285186.js5
-rw-r--r--js/src/jit-test/tests/gc/bug1285490.js3
-rw-r--r--js/src/jit-test/tests/gc/bug1287063.js4
-rw-r--r--js/src/jit-test/tests/gc/bug1326343-gcstats.js6
-rw-r--r--js/src/jit-test/tests/gc/bug1335642.js6
-rw-r--r--js/src/jit-test/tests/gc/bug1335643.js5
-rw-r--r--js/src/jit-test/tests/gc/bug1336866.js1
-rw-r--r--js/src/jit-test/tests/gc/bug1337324.js6
-rw-r--r--js/src/jit-test/tests/gc/bug1471949.js5
-rw-r--r--js/src/jit-test/tests/gc/bug1511412.js17
-rw-r--r--js/src/jit-test/tests/gc/bug1532289.js11
-rw-r--r--js/src/jit-test/tests/gc/bug1600017.js21
-rw-r--r--js/src/jit-test/tests/gc/bug1600488-1.js14
-rw-r--r--js/src/jit-test/tests/gc/bug1600488-2.js14
-rw-r--r--js/src/jit-test/tests/gc/bug1698557.js6
-rw-r--r--js/src/jit-test/tests/gc/bug1704451.js6
-rw-r--r--js/src/jit-test/tests/gc/bug1709537.js9
-rw-r--r--js/src/jit-test/tests/gc/bug888463.js66
-rw-r--r--js/src/jit-test/tests/gc/compartment-revived-gc.js133
-rw-r--r--js/src/jit-test/tests/gc/dedupe-02.js39
-rw-r--r--js/src/jit-test/tests/gc/dedupe.js44
-rw-r--r--js/src/jit-test/tests/gc/dedupeTenuredBase.js45
-rw-r--r--js/src/jit-test/tests/gc/deduplicateTenuringStrings.js205
-rw-r--r--js/src/jit-test/tests/gc/elements-post-write-barrier.js27
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-ccw.js81
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-cleanupSome-recursive.js51
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-gray.js10
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-oom1.js8
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-oom2.js4
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-oom3.js5
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-oom4.js5
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry-records-not-initialized.js6
-rw-r--r--js/src/jit-test/tests/gc/finalizationRegistry.js247
-rw-r--r--js/src/jit-test/tests/gc/gcparam.js64
-rw-r--r--js/src/jit-test/tests/gc/gczeal-range.js5
-rw-r--r--js/src/jit-test/tests/gc/helper-thread-params.js34
-rw-r--r--js/src/jit-test/tests/gc/incremental-01.js31
-rw-r--r--js/src/jit-test/tests/gc/incremental-02.js30
-rw-r--r--js/src/jit-test/tests/gc/incremental-AccessorShape-barrier.js18
-rw-r--r--js/src/jit-test/tests/gc/incremental-abort.js57
-rw-r--r--js/src/jit-test/tests/gc/incremental-compacting.js43
-rw-r--r--js/src/jit-test/tests/gc/incremental-state.js96
-rw-r--r--js/src/jit-test/tests/gc/jsscript-mark-children.js24
-rw-r--r--js/src/jit-test/tests/gc/marking-thread-count.js12
-rw-r--r--js/src/jit-test/tests/gc/multi-01.js9
-rw-r--r--js/src/jit-test/tests/gc/multi-02.js10
-rw-r--r--js/src/jit-test/tests/gc/multi-03.js11
-rw-r--r--js/src/jit-test/tests/gc/oomInArrayProtoTest.js22
-rw-r--r--js/src/jit-test/tests/gc/oomInByteSize.js18
-rw-r--r--js/src/jit-test/tests/gc/oomInDebugger.js4
-rw-r--r--js/src/jit-test/tests/gc/oomInDtoa.js3
-rw-r--r--js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js14
-rw-r--r--js/src/jit-test/tests/gc/oomInFindPath.js18
-rw-r--r--js/src/jit-test/tests/gc/oomInFormatStackDump.js3
-rw-r--r--js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js3
-rw-r--r--js/src/jit-test/tests/gc/oomInNewGlobal.js3
-rw-r--r--js/src/jit-test/tests/gc/oomInOffTheadCompile.js15
-rw-r--r--js/src/jit-test/tests/gc/oomInOffTheadCompile2.js7
-rw-r--r--js/src/jit-test/tests/gc/oomInOffTheadCompile3.js17
-rw-r--r--js/src/jit-test/tests/gc/oomInParseAsmJS.js16
-rw-r--r--js/src/jit-test/tests/gc/oomInParseFunction.js3
-rw-r--r--js/src/jit-test/tests/gc/oomInRegExp.js5
-rw-r--r--js/src/jit-test/tests/gc/oomInRegExp2.js5
-rw-r--r--js/src/jit-test/tests/gc/oomInRegExpAlternativeGeneration.js16
-rw-r--r--js/src/jit-test/tests/gc/oomInWeakMap.js6
-rw-r--r--js/src/jit-test/tests/gc/pretenure-array-long-lived.js17
-rw-r--r--js/src/jit-test/tests/gc/pretenure-array-long-then-short-lived.js28
-rw-r--r--js/src/jit-test/tests/gc/pretenure-array-short-lived.js16
-rw-r--r--js/src/jit-test/tests/gc/pretenure-array-short-then-long-lived.js28
-rw-r--r--js/src/jit-test/tests/gc/pretenure-object-long-lived.js17
-rw-r--r--js/src/jit-test/tests/gc/pretenure-object-long-then-short-lived.js28
-rw-r--r--js/src/jit-test/tests/gc/pretenure-object-short-lived.js16
-rw-r--r--js/src/jit-test/tests/gc/pretenure-object-short-then-long-lived.js28
-rw-r--r--js/src/jit-test/tests/gc/pretenuring.js42
-rw-r--r--js/src/jit-test/tests/gc/regress-1711413.js6
-rw-r--r--js/src/jit-test/tests/gc/str-atom-dedupe.js16
-rw-r--r--js/src/jit-test/tests/gc/test-root-arrays.js10
-rw-r--r--js/src/jit-test/tests/gc/weak-marking-01.js220
-rw-r--r--js/src/jit-test/tests/gc/weak-marking-02.js338
-rw-r--r--js/src/jit-test/tests/gc/weak-marking-03.js696
-rw-r--r--js/src/jit-test/tests/gc/weak-marking-varying.js90
-rw-r--r--js/src/jit-test/tests/gc/weakRef_in_promise.js18
-rw-r--r--js/src/jit-test/tests/gc/weakRefs-basic.js81
-rw-r--r--js/src/jit-test/tests/gc/weakRefs.js122
-rw-r--r--js/src/jit-test/tests/gc/weakmap-expose.js43
-rw-r--r--js/src/jit-test/tests/gc/weakmap-nursery-value.js35
-rw-r--r--js/src/jit-test/tests/gc/weakmark-remap.js16
-rw-r--r--js/src/jit-test/tests/gc/weakmark-remap2.js21
393 files changed, 8453 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/gc/bug-1004457.js b/js/src/jit-test/tests/gc/bug-1004457.js
new file mode 100644
index 0000000000..80e1150f34
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1004457.js
@@ -0,0 +1,3 @@
+var argObj = (function () { return arguments })();
+gczeal(4);
+delete argObj.callee;
diff --git a/js/src/jit-test/tests/gc/bug-1016016.js b/js/src/jit-test/tests/gc/bug-1016016.js
new file mode 100644
index 0000000000..7fb6e64d12
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1016016.js
@@ -0,0 +1,16 @@
+// |jit-test| error:ReferenceError
+toPrinted(this.reason);
+function toPrinted(value) {
+ return value = String(value);
+}
+var lfcode = new Array();
+lfcode.push = loadFile;
+lfcode.push("enableTrackAllocations();");
+lfcode.push("\
+gczeal(9, 2);\
+newGlobal();\
+''.addDebuggee(g1);\
+");
+function loadFile(lfVarx) {
+ evaluate(lfVarx, { noScriptRval : true, isRunOnce : true });
+}
diff --git a/js/src/jit-test/tests/gc/bug-1017141.js b/js/src/jit-test/tests/gc/bug-1017141.js
new file mode 100644
index 0000000000..9c4533924c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1017141.js
@@ -0,0 +1,25 @@
+var min = gcparam('minEmptyChunkCount');
+var max = gcparam('maxEmptyChunkCount');
+
+gcparam('minEmptyChunkCount', 10);
+gcparam('maxEmptyChunkCount', 20);
+assertEq(gcparam('minEmptyChunkCount'), 10);
+assertEq(gcparam('maxEmptyChunkCount'), 20);
+gc();
+
+/* We maintain the invariant that maxEmptyChunkCount >= minEmptyChunkCount. */
+gcparam('minEmptyChunkCount', 30);
+assertEq(gcparam('minEmptyChunkCount'), 30);
+assertEq(gcparam('maxEmptyChunkCount'), 30);
+gc();
+
+gcparam('maxEmptyChunkCount', 5);
+assertEq(gcparam('minEmptyChunkCount'), 5);
+assertEq(gcparam('maxEmptyChunkCount'), 5);
+gc();
+
+gcparam('minEmptyChunkCount', min);
+gcparam('maxEmptyChunkCount', max);
+assertEq(gcparam('minEmptyChunkCount'), min);
+assertEq(gcparam('maxEmptyChunkCount'), max);
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1028863.js b/js/src/jit-test/tests/gc/bug-1028863.js
new file mode 100644
index 0000000000..699c02fb75
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1028863.js
@@ -0,0 +1,3 @@
+function writeTestCaseResult( expect, actual, string ) {}
+schedulegc(10);
+if (saveStack() == 3) done = true;
diff --git a/js/src/jit-test/tests/gc/bug-1032206.js b/js/src/jit-test/tests/gc/bug-1032206.js
new file mode 100644
index 0000000000..c58cc53a8d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1032206.js
@@ -0,0 +1,3 @@
+gczeal(4);
+var symbols = [Symbol(), Symbol("comet"), Symbol.for("moon"), Symbol.iterator, 0];
+for (var a of symbols) {}
diff --git a/js/src/jit-test/tests/gc/bug-1035371.js b/js/src/jit-test/tests/gc/bug-1035371.js
new file mode 100644
index 0000000000..b42a74656d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1035371.js
@@ -0,0 +1,4 @@
+x = function() {};
+y = new WeakMap;
+selectforgc({});;
+y.set(x, Symbol());
diff --git a/js/src/jit-test/tests/gc/bug-1039516.js b/js/src/jit-test/tests/gc/bug-1039516.js
new file mode 100644
index 0000000000..15f2a994d9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1039516.js
@@ -0,0 +1,6 @@
+gczeal(9);
+Symbol.for("a");
+gcslice(1);
+var a = Symbol.for("a");
+gcslice();
+print(Symbol.keyFor(a));
diff --git a/js/src/jit-test/tests/gc/bug-1053676.js b/js/src/jit-test/tests/gc/bug-1053676.js
new file mode 100644
index 0000000000..bebada5767
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1053676.js
@@ -0,0 +1,11 @@
+// |jit-test| --ion-eager
+
+var x
+(function() {
+ x
+}());
+verifyprebarriers();
+x = x * 0
+x = Symbol();
+gc();
+evalcx("x=1", this);
diff --git a/js/src/jit-test/tests/gc/bug-1055219.js b/js/src/jit-test/tests/gc/bug-1055219.js
new file mode 100644
index 0000000000..421aa55ae3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1055219.js
@@ -0,0 +1,5 @@
+gczeal(13);
+function A() {};
+A.prototype = [];
+function B() {};
+B.prototype = new A();
diff --git a/js/src/jit-test/tests/gc/bug-1070638.js b/js/src/jit-test/tests/gc/bug-1070638.js
new file mode 100644
index 0000000000..23793765cf
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1070638.js
@@ -0,0 +1,20 @@
+function m() {
+ for (var j = 0; j < 99; ++j) {
+ for (var k = 0; k < 99; ++k) {
+ try {
+ undefined()()
+ } catch (e) {}
+ }
+ }
+}
+m()
+m()
+m()
+m()
+for (var j = 0; j < 99; ++j) {
+ for (var k = 0; k < 99; ++k) {
+ try {
+ gcslice(1)()
+ } catch (e) {}
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1075546.js b/js/src/jit-test/tests/gc/bug-1075546.js
new file mode 100644
index 0000000000..b9fdb510b6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1075546.js
@@ -0,0 +1,30 @@
+for (var i = 0; i < 200; ++i) {
+ Object.getOwnPropertyNames(undefined + "");
+}
+function p(s) {
+ for (var i = 0; i < s.length; i++) {
+ s.charCodeAt(i);
+ }
+}
+function m(f) {
+ var a = [];
+ for (var j = 0; j < 700; ++j) {
+ try {
+ f()
+ } catch (e) {
+ a.push(e.toString());
+ }
+ }
+ p(JSON.stringify(a));
+}
+f = Function("\
+ function f() {\
+ functionf\n{}\
+ }\
+ m(f);\
+");
+f();
+f();
+for (var x = 0; x < 99; x++) {
+ newGlobal()
+}
diff --git a/js/src/jit-test/tests/gc/bug-1104162.js b/js/src/jit-test/tests/gc/bug-1104162.js
new file mode 100644
index 0000000000..33af69cda0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1104162.js
@@ -0,0 +1,7 @@
+gczeal(11);
+g = newGlobal({newCompartment: true})
+g.eval("undefined;function f(){}")
+Debugger(g).onDebuggerStatement = function(x) {
+ x.eval("f").return.script.setBreakpoint(0, {})
+}
+g.eval("debugger")
diff --git a/js/src/jit-test/tests/gc/bug-1108007.js b/js/src/jit-test/tests/gc/bug-1108007.js
new file mode 100644
index 0000000000..35daaefc91
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1108007.js
@@ -0,0 +1,23 @@
+// |jit-test| --no-threads; --no-ion; --no-baseline; skip-if: !('gczeal' in this)
+
+gczeal(2);
+(function() {
+ evaluate(cacheEntry((function() {
+ return "(new String(\"\"))"
+ })()), Object.create({ global: newGlobal({ cloneSingletons: true }) }, {
+ saveIncrementalBytecode: {
+ value: true
+ }
+ }))
+})();
+[[0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0]];
diff --git a/js/src/jit-test/tests/gc/bug-1108836.js b/js/src/jit-test/tests/gc/bug-1108836.js
new file mode 100644
index 0000000000..e8d32b3e3c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1108836.js
@@ -0,0 +1,9 @@
+const root = newGlobal();
+var g = newGlobal();
+for (var indexI = 0; indexI <= 65535; indexI++) {
+ eval("/*var " + String.fromCharCode(indexI) + "xx = 1*/");
+}
+for (var i = 0; i < 100; ++i) {
+ gc();
+ gcslice(1000000);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1109913.js b/js/src/jit-test/tests/gc/bug-1109913.js
new file mode 100644
index 0000000000..c84db18c4b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1109913.js
@@ -0,0 +1,7 @@
+// |jit-test| allow-oom; allow-unhandlable-oom
+
+gcparam("maxBytes", gcparam("gcBytes"));
+eval(`
+ gczeal(2, 1);
+ newGlobal();
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1109922.js b/js/src/jit-test/tests/gc/bug-1109922.js
new file mode 100644
index 0000000000..8bd5c52da7
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1109922.js
@@ -0,0 +1,6 @@
+if (this.hasOwnProperty("Intl")) {
+ gczeal(14);
+ b = {};
+ b.__proto__ = evalcx("lazy");
+ (function m(b) {})(b.Intl.Collator(0))
+}
diff --git a/js/src/jit-test/tests/gc/bug-1123648.js b/js/src/jit-test/tests/gc/bug-1123648.js
new file mode 100644
index 0000000000..54226949a0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1123648.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('ctypes' in this)
+
+gczeal(14, 1);
+ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []).ptr;
diff --git a/js/src/jit-test/tests/gc/bug-1124563.js b/js/src/jit-test/tests/gc/bug-1124563.js
new file mode 100644
index 0000000000..73599025b8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1124563.js
@@ -0,0 +1,4 @@
+try {
+ gc(0, 'shrinking')({x: 0})
+} catch (e) {}
+eval("({x: 0, x: 0})[{x: 0}]")
diff --git a/js/src/jit-test/tests/gc/bug-1124653.js b/js/src/jit-test/tests/gc/bug-1124653.js
new file mode 100644
index 0000000000..8dca8dc330
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1124653.js
@@ -0,0 +1,5 @@
+var o = {};
+gczeal(14);
+for (var i = 0; i < 200; i++) {
+ with (o) { }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1136597.js b/js/src/jit-test/tests/gc/bug-1136597.js
new file mode 100644
index 0000000000..075933acf8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1136597.js
@@ -0,0 +1,25 @@
+// |jit-test| error:ReferenceError
+var evalInFrame = (function (global) {
+ var dbgGlobal = newGlobal({newCompartment: true});
+ var dbg = new dbgGlobal.Debugger();
+ return function evalInFrame(upCount, code) {
+ dbg.addDebuggee(global);
+ };
+})(this);
+var gTestcases = new Array();
+var gTc = gTestcases.length;
+function TestCase() {
+ return gTestcases[gTc++] = this;
+}
+function checkCollation(extensionCoValue, usageValue) {
+ var collator = new Intl.Collator(["de-DE"]);
+ collator.resolvedOptions().collation;
+}
+checkCollation(undefined, "sort");
+checkCollation();
+for ( addpow = 0; addpow < 33; addpow++ ) {
+ new TestCase();
+}
+evalInFrame(0, "i(true)", true);
+gc(3, 'shrinking')
+eval("gc(); h = g1");
diff --git a/js/src/jit-test/tests/gc/bug-1137341.js b/js/src/jit-test/tests/gc/bug-1137341.js
new file mode 100644
index 0000000000..baac2d7bb0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1137341.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+gczeal(0);
+gc();
+
+schedulezone(this);
+startgc(0, "shrinking");
+var g = newGlobal();
+g.offThreadCompileToStencil('debugger;', {});
+var stencil = g.finishOffThreadStencil();
+g.evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1143706.js b/js/src/jit-test/tests/gc/bug-1143706.js
new file mode 100644
index 0000000000..2ae7930cb4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1143706.js
@@ -0,0 +1,2 @@
+gczeal(14, 1);
+var g = newGlobal();
diff --git a/js/src/jit-test/tests/gc/bug-1144738.js b/js/src/jit-test/tests/gc/bug-1144738.js
new file mode 100644
index 0000000000..ef121faaf8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1144738.js
@@ -0,0 +1,32 @@
+// |jit-test| error: ReferenceError; --fuzzing-safe; --thread-count=1; --ion-eager
+const ALL_TESTS = [
+ "CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE_IS_FUNCTION",
+ ];
+function r(keyword, tests) {
+ function Reserved(keyword, tests) {
+ this.keyword = keyword;
+ if (tests)
+ this.tests = tests;
+ else
+ this.tests = ALL_TESTS;
+ }
+ return new Reserved(keyword, tests);
+}
+for (var i = 2; i >= 0; i--) {
+ gc();
+ gczeal(14, 17);
+ [
+ r("break"),
+ r("case"),
+ r("catch"),
+ r("continue"),
+ ];
+ [
+ r("true"),
+ r("null"),
+ r("each"),
+ r("let")
+ ];
+}
+Failure;
+
diff --git a/js/src/jit-test/tests/gc/bug-1146696.js b/js/src/jit-test/tests/gc/bug-1146696.js
new file mode 100644
index 0000000000..02df084673
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1146696.js
@@ -0,0 +1,21 @@
+// |jit-test| --no-ggc; allow-oom; allow-unhandlable-oom
+gc();
+dbg1 = new Debugger();
+root2 = newGlobal({newCompartment: true});
+dbg1.memory.onGarbageCollection = function(){}
+dbg1.addDebuggee(root2);
+for (var j = 0; j < 9999; ++j) {
+ try {
+ a
+ } catch (e) {}
+}
+gcparam("maxBytes", gcparam("gcBytes") + 8000);
+function g(i) {
+ if (i == 0)
+ return;
+ var x = "";
+ function f() {}
+ eval('');
+ g(i - 1);
+}
+g(100);
diff --git a/js/src/jit-test/tests/gc/bug-1148383.js b/js/src/jit-test/tests/gc/bug-1148383.js
new file mode 100644
index 0000000000..486e6e0ba5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1148383.js
@@ -0,0 +1,17 @@
+// This testcase tests setting object metadata for objects created from JIT
+// code.
+opts = getJitCompilerOptions();
+if (!opts['ion.enable'] || !opts['baseline.enable'])
+ quit();
+
+function TestCase() {}
+function reportCompare () {
+ var output = "";
+ var testcase = new TestCase();
+ testcase.reason = output;
+}
+reportCompare();
+gczeal(4, 1000);
+enableShellAllocationMetadataBuilder();
+for (var i = 0; i < 10000; ++i)
+ reportCompare();
diff --git a/js/src/jit-test/tests/gc/bug-1155455.js b/js/src/jit-test/tests/gc/bug-1155455.js
new file mode 100644
index 0000000000..c9b8040884
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1155455.js
@@ -0,0 +1,16 @@
+// |jit-test| error: TypeError; skip-if: !('gczeal' in this)
+var g = newGlobal();
+gczeal(10, 2)
+var dbg = Debugger(g);
+dbg.onDebuggerStatement = function (frame1) {
+ function hit(frame2) {
+ return hit[0] = "mutated";
+ }
+ var s = frame1.script;
+ var offs = s.getLineOffsets(g.line0 + 2);
+ for (var i = 0; i < offs.length; i++)
+ s.setBreakpoint(offs[i], {hit: hit});
+ return;
+};
+var lfGlobal = newGlobal();
+g.eval("var line0 = Error().lineNumber;\n debugger;\nx = 1;\n");
diff --git a/js/src/jit-test/tests/gc/bug-1157577.js b/js/src/jit-test/tests/gc/bug-1157577.js
new file mode 100644
index 0000000000..2ec7556ea3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1157577.js
@@ -0,0 +1,4 @@
+x = evalcx('');
+gcslice(10);
+for (var p in x) {}
+
diff --git a/js/src/jit-test/tests/gc/bug-1161303.js b/js/src/jit-test/tests/gc/bug-1161303.js
new file mode 100644
index 0000000000..872f137632
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1161303.js
@@ -0,0 +1,7 @@
+function f(x) {
+ for (var i = 0; i < 100000; i++ ) {
+ [(x, 2)];
+ try { g(); } catch (t) {}
+ }
+}
+f(2);
diff --git a/js/src/jit-test/tests/gc/bug-1161968.js b/js/src/jit-test/tests/gc/bug-1161968.js
new file mode 100644
index 0000000000..5f83aa510c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1161968.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: !('gczeal' in this)
+
+// This test case is a simplified version of debug/Source-invisible.js.
+
+gczeal(2,21);
+
+var gi = newGlobal();
+
+var gv = newGlobal();
+gi.evaluate('function f() {}', {global: gv});
+
+var dbg = new Debugger;
diff --git a/js/src/jit-test/tests/gc/bug-1165966.js b/js/src/jit-test/tests/gc/bug-1165966.js
new file mode 100644
index 0000000000..79c58274cd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1165966.js
@@ -0,0 +1,6 @@
+// |jit-test| --no-ion; skip-if: !('oomTest' in this)
+
+var g = newGlobal();
+oomTest(function() {
+ Debugger(g);
+});
diff --git a/js/src/jit-test/tests/gc/bug-1171909.js b/js/src/jit-test/tests/gc/bug-1171909.js
new file mode 100644
index 0000000000..755c6ff89d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1171909.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest((function(x) { assertEq(x + y + ex, 25); }));
diff --git a/js/src/jit-test/tests/gc/bug-1175755.js b/js/src/jit-test/tests/gc/bug-1175755.js
new file mode 100644
index 0000000000..1b6960fb8c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1175755.js
@@ -0,0 +1,6 @@
+// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomAfterAllocations' in this)
+
+setGCCallback({
+ action: "majorGC",
+});
+oomAfterAllocations(50);
diff --git a/js/src/jit-test/tests/gc/bug-1177778.js b/js/src/jit-test/tests/gc/bug-1177778.js
new file mode 100644
index 0000000000..155763f3a2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1177778.js
@@ -0,0 +1,15 @@
+// |jit-test| skip-if: !('setGCCallback' in this)
+
+setGCCallback({
+ action: "majorGC",
+ phases: "both"
+});
+var g = newGlobal({newCompartment: true});
+var dbg = new Debugger;
+var gw = dbg.addDebuggee(g);
+g.eval("function h() { debugger; }");
+dbg.onDebuggerStatement = function(hframe) {
+ var env = hframe.older.environment;
+};
+g.eval("h();");
+
diff --git a/js/src/jit-test/tests/gc/bug-1191576.js b/js/src/jit-test/tests/gc/bug-1191576.js
new file mode 100644
index 0000000000..6346905256
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1191576.js
@@ -0,0 +1,14 @@
+// |jit-test| allow-oom; skip-if: !('gczeal' in this && 'oomAfterAllocations' in this)
+
+var lfcode = new Array();
+gczeal(14);
+loadFile(`
+for (let e of Object.values(newGlobal())) {
+ if (oomAfterAllocations(100))
+ continue;
+}
+`);
+function loadFile(lfVarx) {
+ for (lfLocal in this) {}
+ evaluate(lfVarx);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1206677.js b/js/src/jit-test/tests/gc/bug-1206677.js
new file mode 100644
index 0000000000..a0d2ff3a1f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1206677.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+var lfGlobal = newGlobal();
+for (lfLocal in this) {
+ if (!(lfLocal in lfGlobal)) {
+ lfGlobal[lfLocal] = this[lfLocal];
+ }
+}
+const script = 'oomTest(() => getBacktrace({args: true, locals: "123795", thisprops: true}));';
+lfGlobal.offThreadCompileToStencil(script);
+var stencil = lfGlobal.finishOffThreadStencil();
+lfGlobal.evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1208994.js b/js/src/jit-test/tests/gc/bug-1208994.js
new file mode 100644
index 0000000000..12c24f62af
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1208994.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => getBacktrace({args: oomTest[load+1], locals: true, thisprops: true}));
diff --git a/js/src/jit-test/tests/gc/bug-1209001.js b/js/src/jit-test/tests/gc/bug-1209001.js
new file mode 100644
index 0000000000..a737224d0d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1209001.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => parseModule('import v from "mod";'));
diff --git a/js/src/jit-test/tests/gc/bug-1210607.js b/js/src/jit-test/tests/gc/bug-1210607.js
new file mode 100644
index 0000000000..15312c810a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1210607.js
@@ -0,0 +1,6 @@
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
+
+var g = newGlobal({newCompartment: true});
+x = Debugger(g);
+selectforgc(g);
+oomAfterAllocations(1);
diff --git a/js/src/jit-test/tests/gc/bug-1214006.js b/js/src/jit-test/tests/gc/bug-1214006.js
new file mode 100644
index 0000000000..ed2c6468dc
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1214006.js
@@ -0,0 +1,7 @@
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
+
+function f() {
+ eval("(function() y)()");
+}
+oomTest(f);
+fullcompartmentchecks(true);
diff --git a/js/src/jit-test/tests/gc/bug-1214781.js b/js/src/jit-test/tests/gc/bug-1214781.js
new file mode 100644
index 0000000000..d18845812c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1214781.js
@@ -0,0 +1,7 @@
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
+
+try {
+ gcparam("maxBytes", gcparam("gcBytes"));
+ newGlobal("");
+} catch (e) {};
+oomTest(function() {})
diff --git a/js/src/jit-test/tests/gc/bug-1214846.js b/js/src/jit-test/tests/gc/bug-1214846.js
new file mode 100644
index 0000000000..23b5b9fe94
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1214846.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+enableGeckoProfiling();
+var s = newGlobal();
+s.offThreadCompileToStencil('oomTest(() => {});');
+var stencil = s.finishOffThreadStencil();
+s.evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1215363-1.js b/js/src/jit-test/tests/gc/bug-1215363-1.js
new file mode 100644
index 0000000000..3ed21e1f9a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1215363-1.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => parseModule(10));
diff --git a/js/src/jit-test/tests/gc/bug-1215363-2.js b/js/src/jit-test/tests/gc/bug-1215363-2.js
new file mode 100644
index 0000000000..4b51a5a96d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1215363-2.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var lfcode = new Array();
+oomTest((function(x) {
+ assertEq(...Object);
+}));
diff --git a/js/src/jit-test/tests/gc/bug-1215363-3.js b/js/src/jit-test/tests/gc/bug-1215363-3.js
new file mode 100644
index 0000000000..33495af2e1
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1215363-3.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var lfcode = new Array();
+oomTest(() => getBacktrace({}));
diff --git a/js/src/jit-test/tests/gc/bug-1215678.js b/js/src/jit-test/tests/gc/bug-1215678.js
new file mode 100644
index 0000000000..8a9723776b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1215678.js
@@ -0,0 +1,10 @@
+// |jit-test| error: ReferenceError
+if (!('oomTest' in this))
+ a;
+
+enableShellAllocationMetadataBuilder()
+oomTest(() => {
+ newGlobal()
+})
+gczeal(9, 1);
+a;
diff --git a/js/src/jit-test/tests/gc/bug-1216607.js b/js/src/jit-test/tests/gc/bug-1216607.js
new file mode 100644
index 0000000000..1afac7faab
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1216607.js
@@ -0,0 +1,18 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+enableGeckoProfilingWithSlowAssertions();
+try {
+(function() {
+ while (n--) {
+ }
+})();
+} catch(exc1) {}
+function arrayProtoOutOfRange() {
+ function f(obj) {}
+ function test() {
+ for (var i = 0; i < 1000; i++)
+ var r = f(i % 2 ? a : b);
+ }
+ test();
+}
+oomTest(arrayProtoOutOfRange);
diff --git a/js/src/jit-test/tests/gc/bug-1218900-2.js b/js/src/jit-test/tests/gc/bug-1218900-2.js
new file mode 100644
index 0000000000..6e99475266
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1218900-2.js
@@ -0,0 +1,3 @@
+// |jit-test| error: Error
+startTimingMutator();
+startTimingMutator();
diff --git a/js/src/jit-test/tests/gc/bug-1218900.js b/js/src/jit-test/tests/gc/bug-1218900.js
new file mode 100644
index 0000000000..a3bc82bd2b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1218900.js
@@ -0,0 +1,8 @@
+// |jit-test| --fuzzing-safe
+readline = function() {};
+Function.prototype.toString = function() {
+ for (var i = 0; i < 2; i++) {
+ this()
+ }
+};
+getBacktrace({thisprops: true});
diff --git a/js/src/jit-test/tests/gc/bug-1221359.js b/js/src/jit-test/tests/gc/bug-1221359.js
new file mode 100644
index 0000000000..dcbafeb446
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1221359.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => getBacktrace({
+ locals: true,
+ thisprops: true
+}));
diff --git a/js/src/jit-test/tests/gc/bug-1221747.js b/js/src/jit-test/tests/gc/bug-1221747.js
new file mode 100644
index 0000000000..5ff33dd64e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1221747.js
@@ -0,0 +1,6 @@
+// |jit-test| --dump-bytecode; skip-if: !('oomTest' in this)
+
+function f() {
+ eval("(function() {})()");
+}
+oomTest(f);
diff --git a/js/src/jit-test/tests/gc/bug-1223021.js b/js/src/jit-test/tests/gc/bug-1223021.js
new file mode 100644
index 0000000000..bbc40aa1fb
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1223021.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function f() {
+ return this === null;
+};
+
+function g() {
+ if (!f.apply(9)) {}
+}
+
+oomTest(g);
diff --git a/js/src/jit-test/tests/gc/bug-1224710.js b/js/src/jit-test/tests/gc/bug-1224710.js
new file mode 100644
index 0000000000..9cb9aa7cd3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1224710.js
@@ -0,0 +1,13 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(function() {
+ eval("\
+ function g() {\
+ \"use asm\";\
+ function f(d) {\
+ d = +d;\
+ print(.0 + d);\
+ }\
+ }\
+ ")
+})
diff --git a/js/src/jit-test/tests/gc/bug-1226896.js b/js/src/jit-test/tests/gc/bug-1226896.js
new file mode 100644
index 0000000000..0b8c513cc8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1226896.js
@@ -0,0 +1,6 @@
+// |jit-test| --ion-pruning=on; skip-if: !('oomTest' in this)
+
+oomTest(() => {
+ var g = newGlobal({sameZoneAs: this});
+ g.eval("(function() {})()");
+});
diff --git a/js/src/jit-test/tests/gc/bug-1231386.js b/js/src/jit-test/tests/gc/bug-1231386.js
new file mode 100644
index 0000000000..c2dc55b734
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1231386.js
@@ -0,0 +1,18 @@
+// |jit-test| slow; skip-if: !('oomTest' in this)
+
+function f1() {}
+function f2() {}
+r = [function() {}, function() {}, [], function() {}, f1, function() {}, f2];
+l = [0];
+function f3() {
+ return {
+ a: 0
+ };
+}
+var x = f3();
+var h = newGlobal({newCompartment: true});
+var dbg = new Debugger;
+dbg.addDebuggee(h);
+oomTest(() => getBacktrace({
+ thisprops: gc()
+}));
diff --git a/js/src/jit-test/tests/gc/bug-1232386.js b/js/src/jit-test/tests/gc/bug-1232386.js
new file mode 100644
index 0000000000..82a33a7ec4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1232386.js
@@ -0,0 +1,10 @@
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
+
+var dbg = new Debugger;
+dbg.onNewGlobalObject = function(global) {
+ global.seen = true;
+};
+
+oomTest(function() {
+ newGlobal({sameZoneAs: this})
+});
diff --git a/js/src/jit-test/tests/gc/bug-1234410.js b/js/src/jit-test/tests/gc/bug-1234410.js
new file mode 100644
index 0000000000..fe400f8013
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1234410.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+enableGeckoProfiling();
+oomTest(() => {
+ try {
+ for (var quit of oomTest.gcparam("//").ArrayBuffer(1)) {}
+ } catch (e) {}
+});
+
diff --git a/js/src/jit-test/tests/gc/bug-1236473.js b/js/src/jit-test/tests/gc/bug-1236473.js
new file mode 100644
index 0000000000..0051e789a6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1236473.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => {
+ offThreadCompileToStencil(`try {} catch (NaN) {}`);
+ var stencil = finishOffThreadStencil();
+ evalStencil(stencil);
+});
diff --git a/js/src/jit-test/tests/gc/bug-1237153.js b/js/src/jit-test/tests/gc/bug-1237153.js
new file mode 100644
index 0000000000..bdb0b011f2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1237153.js
@@ -0,0 +1,2 @@
+// |jit-test| error: Error
+gcparam("sliceTimeBudgetMS", -1);
diff --git a/js/src/jit-test/tests/gc/bug-1238548.js b/js/src/jit-test/tests/gc/bug-1238548.js
new file mode 100644
index 0000000000..4f2e1e51d9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238548.js
@@ -0,0 +1,2 @@
+// |jit-test| error: Error
+gcparam("highFrequencySmallHeapGrowth", 1);
diff --git a/js/src/jit-test/tests/gc/bug-1238555.js b/js/src/jit-test/tests/gc/bug-1238555.js
new file mode 100644
index 0000000000..4b9963292e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238555.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(
+ function x() {
+ try {
+ eval('let ')
+ } catch (ex) {
+ (function() {})()
+ }
+ }
+);
diff --git a/js/src/jit-test/tests/gc/bug-1238575-2.js b/js/src/jit-test/tests/gc/bug-1238575-2.js
new file mode 100644
index 0000000000..9fe011efa1
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238575-2.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => evalInWorker("1"));
diff --git a/js/src/jit-test/tests/gc/bug-1238575.js b/js/src/jit-test/tests/gc/bug-1238575.js
new file mode 100644
index 0000000000..8e6a629d9f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238575.js
@@ -0,0 +1,5 @@
+// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomAfterAllocations' in this)
+
+oomAfterAllocations(5)
+gcslice(11);
+evalInWorker("1");
diff --git a/js/src/jit-test/tests/gc/bug-1238582.js b/js/src/jit-test/tests/gc/bug-1238582.js
new file mode 100644
index 0000000000..b5dad7a64d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238582.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => { let a = [2147483651]; [a[0], a[undefined]].sort(); });
diff --git a/js/src/jit-test/tests/gc/bug-1240503.js b/js/src/jit-test/tests/gc/bug-1240503.js
new file mode 100644
index 0000000000..167752962b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1240503.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function arrayProtoOutOfRange() {
+ for (let [] = () => r, get;;)
+ var r = f(i % 2 ? a : b);
+}
+oomTest(arrayProtoOutOfRange);
diff --git a/js/src/jit-test/tests/gc/bug-1240527.js b/js/src/jit-test/tests/gc/bug-1240527.js
new file mode 100644
index 0000000000..ca4e0e3eb6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1240527.js
@@ -0,0 +1,8 @@
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this)
+
+offThreadCompileToStencil(`
+ oomTest(() => "".search(/d/));
+ fullcompartmentchecks(3);
+`);
+var stencil = finishOffThreadStencil();
+evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1241731.js b/js/src/jit-test/tests/gc/bug-1241731.js
new file mode 100644
index 0000000000..015c7f3e67
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1241731.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => serialize(0, [{}]));
diff --git a/js/src/jit-test/tests/gc/bug-1242812.js b/js/src/jit-test/tests/gc/bug-1242812.js
new file mode 100644
index 0000000000..df4ae09998
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1242812.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var lfcode = new Array();
+oomTest(() => { let a = [2147483651]; [-1, 0, 1, 31, 32].sort(); });
diff --git a/js/src/jit-test/tests/gc/bug-1245520.js b/js/src/jit-test/tests/gc/bug-1245520.js
new file mode 100644
index 0000000000..1f59c3dbab
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1245520.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var t = {};
+oomTest(() => serialize(t));
diff --git a/js/src/jit-test/tests/gc/bug-1246593.js b/js/src/jit-test/tests/gc/bug-1246593.js
new file mode 100644
index 0000000000..4d90bbb24b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1246593.js
@@ -0,0 +1,4 @@
+length = 10000;
+array = Array();
+for (i = length;i > -100000; i--)
+ array[i] = {};
diff --git a/js/src/jit-test/tests/gc/bug-1252329.js b/js/src/jit-test/tests/gc/bug-1252329.js
new file mode 100644
index 0000000000..8f5b1ce282
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1252329.js
@@ -0,0 +1,32 @@
+// |jit-test| allow-oom; skip-if: helperThreadCount() == 0 || !('oomAfterAllocations' in this)
+
+var lfcode = new Array();
+lfcode.push("5");
+lfcode.push(`
+gczeal(8, 2);
+try {
+ [new String, y]
+} catch (e) {}
+oomAfterAllocations(50);
+try {
+ (5).replace(r, () => {});
+} catch (x) {}
+`);
+while (true) {
+ var file = lfcode.shift(); if (file == undefined) { break; }
+ loadFile(file)
+}
+function loadFile(lfVarx) {
+ if (lfVarx.substr(-3) != ".js" && lfVarx.length != 1) {
+ switch (lfRunTypeId) {
+ case 5:
+ var lfGlobal = newGlobal();
+ for (lfLocal in this) {}
+ lfGlobal.offThreadCompileToStencil(lfVarx);
+ var stencil = lfGlobal.finishOffThreadStencil();
+ lfGlobal.evalStencil(stencil);
+ }
+ } else if (!isNaN(lfVarx)) {
+ lfRunTypeId = parseInt(lfVarx);
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1253124.js b/js/src/jit-test/tests/gc/bug-1253124.js
new file mode 100644
index 0000000000..6949605b00
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1253124.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+for (let i = 0; i < 10; i++)
+ toPrimitive = Date.prototype[Symbol.toPrimitive];
+assertThrowsInstanceOf(() => 0);
+obj = {};
+oomTest(() => assertThrowsInstanceOf(() => toPrimitive.call(obj, "boolean")));
+function assertThrowsInstanceOf(f) {
+ f();
+}
diff --git a/js/src/jit-test/tests/gc/bug-1254108.js b/js/src/jit-test/tests/gc/bug-1254108.js
new file mode 100644
index 0000000000..bf18e798ae
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1254108.js
@@ -0,0 +1,5 @@
+|jit-test| error: Error
+function testChangeParam(key) {
+ gcparam(key, 0x22222222);
+}
+testChangeParam("lowFrequencyHeapGrowth");
diff --git a/js/src/jit-test/tests/gc/bug-1258407.js b/js/src/jit-test/tests/gc/bug-1258407.js
new file mode 100644
index 0000000000..9338c981f2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1258407.js
@@ -0,0 +1,10 @@
+|jit-test| error: Error
+gcparam("lowFrequencyHeapGrowth", 0.1);
+gcparam("lowFrequencyHeapGrowth", 2);
+let ok = false;
+try {
+ gcparam("lowFrequencyHeapGrowth", 0x22222222);
+} catch (e) {
+ ok = true;
+}
+assertEq(ok, true);
diff --git a/js/src/jit-test/tests/gc/bug-1259306.js b/js/src/jit-test/tests/gc/bug-1259306.js
new file mode 100644
index 0000000000..fba5f71b6a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1259306.js
@@ -0,0 +1,19 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+let runCount = 0;
+oomTest(() => {
+ if (runCount < 5) {
+ let lfGlobal = newGlobal();
+ var lfVarx = `
+ gczeal(8, 1);
+ try {
+ (5).replace(r, () => {});
+ } catch (x) {}
+ gczeal(0);
+ `;
+ lfGlobal.offThreadCompileToStencil(lfVarx);
+ var stencil = lfGlobal.finishOffThreadStencil();
+ lfGlobal.evalStencil(stencil);
+ runCount++;
+ }
+});
diff --git a/js/src/jit-test/tests/gc/bug-1259490.js b/js/src/jit-test/tests/gc/bug-1259490.js
new file mode 100644
index 0000000000..d349bf27d4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1259490.js
@@ -0,0 +1,13 @@
+try { eval("3 ** 4") } catch (e) {
+ if (!(e instanceof SyntaxError))
+ throw e;
+ quit();
+}
+eval(`
+
+gczeal(8);
+for (var k = 0; k < 99; ++k) {
+ String(-(0 ** (Object | 0 * Object)))
+}
+
+`)
diff --git a/js/src/jit-test/tests/gc/bug-1261329.js b/js/src/jit-test/tests/gc/bug-1261329.js
new file mode 100644
index 0000000000..afa1db2c3d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1261329.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+print = function() {}
+function k() { return dissrc(print); }
+function j() { return k(); }
+function h() { return j(); }
+function f() { return h(); }
+f();
+oomTest(() => f())
diff --git a/js/src/jit-test/tests/gc/bug-1263862.js b/js/src/jit-test/tests/gc/bug-1263862.js
new file mode 100644
index 0000000000..955805047a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1263862.js
@@ -0,0 +1,8 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function loadFile(lfVarx) {
+ oomTest(() => eval(lfVarx));
+}
+for (var i = 0; i < 10; ++i) {
+ loadFile(`"use strict"; const s = () => s;`);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1263871.js b/js/src/jit-test/tests/gc/bug-1263871.js
new file mode 100644
index 0000000000..6680affedf
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1263871.js
@@ -0,0 +1,8 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+lfLogBuffer = `this[''] = function() {}`;
+loadFile(lfLogBuffer);
+loadFile(lfLogBuffer);
+function loadFile(lfVarx) {
+ return oomTest(function() { return parseModule(lfVarx); });
+}
diff --git a/js/src/jit-test/tests/gc/bug-1263884.js b/js/src/jit-test/tests/gc/bug-1263884.js
new file mode 100644
index 0000000000..949945e0a4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1263884.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(function() {
+ eval(`
+ var argObj = function () { return arguments }()
+ for (var p in argObj);
+ delete argObj.callee;
+ `);
+});
diff --git a/js/src/jit-test/tests/gc/bug-1271110.js b/js/src/jit-test/tests/gc/bug-1271110.js
new file mode 100644
index 0000000000..12d1617c57
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1271110.js
@@ -0,0 +1,38 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+gczeal(0);
+
+var x1 = [];
+var x2 = [];
+var x3 = [];
+var x4 = [];
+(function() {
+ var gns = Object.getOwnPropertyNames(this);
+ for (var i = 0; i < 49; ++i) {
+ var gn = gns[i];
+ var g = this[gn];
+ if (typeof g == "function") {
+ var hns = Object.getOwnPropertyNames(gn);
+ for (var j = 0; j < hns.length; ++j) {
+ x1.push("");
+ x1.push("");
+ x2.push("");
+ x2.push("");
+ x3.push("");
+ x3.push("");
+ x4.push("");
+ x4.push("");
+ }
+ }
+ }
+})();
+try {
+ __proto__ = function(){};
+} catch (e) {
+ "" + e;
+}
+startgc(9222);
+Function("\
+ (function() {})();\
+ oomTest(Debugger.Script);\
+")();
diff --git a/js/src/jit-test/tests/gc/bug-1276631.js b/js/src/jit-test/tests/gc/bug-1276631.js
new file mode 100644
index 0000000000..f80e7f0210
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1276631.js
@@ -0,0 +1,17 @@
+gczeal(15,5);
+try {
+ foobar();
+} catch (e) {}
+function newFunc(x) {
+ new Function(x)();
+};
+loadFile(`
+ try { gczeal(10, 2)() } catch (e) {}
+`);
+function loadFile(lfVarx) {
+ function newFunc(x) {
+ new Function(x)();
+ };
+ newFunc(lfVarx);
+ if (helperThreadCount() && getJitCompilerOptions()["offthread-compilation.enable"]) {}
+}
diff --git a/js/src/jit-test/tests/gc/bug-1278832.js b/js/src/jit-test/tests/gc/bug-1278832.js
new file mode 100644
index 0000000000..aa9e269b3d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1278832.js
@@ -0,0 +1,12 @@
+function assertThrowsInstanceOf() {}
+gczeal(15)
+try {
+ gczeal(10, 2)
+} catch (Atomics) {}
+for (define of[__defineSetter__]) {
+ let nonCallable = [{}]
+ for (let value of nonCallable) assertThrowsInstanceOf(TypeError)
+ key = {
+ [Symbol]() {}
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1280588.js b/js/src/jit-test/tests/gc/bug-1280588.js
new file mode 100644
index 0000000000..a6b2c4f075
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1280588.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var x = [];
+oomTest(() => setGCCallback({ action: "minorGC" }));
+oomTest(() => setGCCallback({ action: "majorGC" }));
diff --git a/js/src/jit-test/tests/gc/bug-1280889.js b/js/src/jit-test/tests/gc/bug-1280889.js
new file mode 100644
index 0000000000..3af1ee3e08
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1280889.js
@@ -0,0 +1,8 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+evalInWorker(`
+ function f() {
+ fullcompartmentchecks(f);
+ }
+ try { f(); } catch(e) {}
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1282986.js b/js/src/jit-test/tests/gc/bug-1282986.js
new file mode 100644
index 0000000000..934cea5b61
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1282986.js
@@ -0,0 +1,16 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var lfLogBuffer = `
+evalInWorker(\`
+ try { oomAfterAllocations(2); } catch(e) {}
+ \`);
+`;
+loadFile("");
+loadFile(lfLogBuffer);
+function loadFile(lfVarx) {
+ oomTest(function() {
+ let m = parseModule(lfVarx);
+ moduleLink(m);
+ moduleEvaluate(m);
+ });
+}
diff --git a/js/src/jit-test/tests/gc/bug-1286244.js b/js/src/jit-test/tests/gc/bug-1286244.js
new file mode 100644
index 0000000000..1b69fdae11
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1286244.js
@@ -0,0 +1,8 @@
+// |jit-test| skip-if: !getBuildConfiguration()['has-gczeal'] || helperThreadCount() === 0
+
+// This will fail with --no-threads.
+verifyprebarriers();
+var lfGlobal = newGlobal();
+lfGlobal.offThreadCompileToStencil(`
+ version(185);
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1287399.js b/js/src/jit-test/tests/gc/bug-1287399.js
new file mode 100644
index 0000000000..c7e6b8f44d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1287399.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: typeof gczeal !== 'function' || helperThreadCount() === 0
+
+var lfGlobal = newGlobal();
+gczeal(4);
+for (lfLocal in this) {}
+lfGlobal.offThreadCompileToStencil(`
+ var desc = {
+ value: 'bar',
+ value: false,
+ };
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1287869.js b/js/src/jit-test/tests/gc/bug-1287869.js
new file mode 100644
index 0000000000..dc04345ccf
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1287869.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('gczeal' in this)
+
+gczeal(16);
+let a = [];
+for (let i = 0; i < 1000; i++)
+ a.push({x: i});
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1292564.js b/js/src/jit-test/tests/gc/bug-1292564.js
new file mode 100644
index 0000000000..f292e1682c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1292564.js
@@ -0,0 +1,9 @@
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
+
+oomTest(() => {
+ let global = newGlobal({sameZoneAs: this});
+ Debugger(global).onDebuggerStatement = function (frame) {
+ frame.eval("f")
+ }
+ global.eval("debugger")
+}, false);
diff --git a/js/src/jit-test/tests/gc/bug-1293127.js b/js/src/jit-test/tests/gc/bug-1293127.js
new file mode 100644
index 0000000000..195798bf76
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1293127.js
@@ -0,0 +1,11 @@
+// Test that we can create 700 cross compartment wrappers to nursery objects
+// without triggering a minor GC.
+gczeal(0);
+let g = newGlobal({newCompartment: true});
+evalcx("function f(x) { return {x: x}; }", g);
+gc();
+let initial = gcparam("gcNumber");
+for (let i = 0; i < 700; i++)
+ g.f(i);
+let final = gcparam("gcNumber");
+assertEq(final, initial);
diff --git a/js/src/jit-test/tests/gc/bug-1294241.js b/js/src/jit-test/tests/gc/bug-1294241.js
new file mode 100644
index 0000000000..62c1acf62e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1294241.js
@@ -0,0 +1,13 @@
+if (helperThreadCount() == 0)
+ quit();
+
+ignoreUnhandledRejections();
+
+gczeal(9);
+function rejectionTracker(state) {}
+setPromiseRejectionTrackerCallback(rejectionTracker)
+ lfGlobal = newGlobal();
+offThreadCompileToStencil(`new Promise(()=>rej)`);
+var stencil = lfGlobal.finishOffThreadStencil();
+lfGlobal.evalStencil(stencil);
+for (lfLocal in this);
diff --git a/js/src/jit-test/tests/gc/bug-1298356.js b/js/src/jit-test/tests/gc/bug-1298356.js
new file mode 100644
index 0000000000..4c8a213125
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1298356.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+/x/;
+oomTest(function(){
+ offThreadCompileToStencil('');
+})
diff --git a/js/src/jit-test/tests/gc/bug-1301377.js b/js/src/jit-test/tests/gc/bug-1301377.js
new file mode 100644
index 0000000000..2843e264da
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1301377.js
@@ -0,0 +1,12 @@
+var lfLogBuffer = `
+ gczeal(14);
+ enableGeckoProfiling();
+ gczeal(15,3);
+ var s = "";
+ for (let i = 0; i != 30; i+=2) {}
+ readGeckoProfilingStack(s, "c0d1c0d1c0d1c0d1c0d1c0d1c0d1c0");
+`;
+loadFile(lfLogBuffer);
+function loadFile(lfVarx) {
+ evaluate(lfVarx);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1301496.js b/js/src/jit-test/tests/gc/bug-1301496.js
new file mode 100644
index 0000000000..e3aa0544f6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1301496.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+gczeal(0);
+startgc(1, 'shrinking');
+offThreadCompileToStencil("");
+// Adapted from randomly chosen test: js/src/jit-test/tests/parser/bug-1263355-13.js
+gczeal(9);
+newGlobal();
diff --git a/js/src/jit-test/tests/gc/bug-1303015.js b/js/src/jit-test/tests/gc/bug-1303015.js
new file mode 100644
index 0000000000..20b34d65d7
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1303015.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var x = ``.split();
+oomTest(function() {
+ var lfGlobal = newGlobal({sameZoneAs: this});
+ for (lfLocal in this) {
+ if (!(lfLocal in lfGlobal)) {
+ lfGlobal[lfLocal] = this[lfLocal];
+ }
+ }
+});
+
diff --git a/js/src/jit-test/tests/gc/bug-1305220.js b/js/src/jit-test/tests/gc/bug-1305220.js
new file mode 100644
index 0000000000..b6dad199a9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1305220.js
@@ -0,0 +1,22 @@
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
+
+s = newGlobal();
+evalcx("\
+ gczeal(10, 2);\
+ k = {\
+ [Symbol]() {}\
+ };\
+", s);
+gczeal(0);
+evalcx("\
+ var g = newGlobal();\
+ b = new Debugger;\
+ g.h = function() {\
+ g.oomAfterAllocations(1);\
+ };\
+ g.eval(\"\" + function f() { return g(); });\
+ g.eval(\"\" + function g() { return h(); });\
+ g.eval(\"(\" + function() {\
+ f();\
+ } + \")()\");\
+", s);
diff --git a/js/src/jit-test/tests/gc/bug-1308048.js b/js/src/jit-test/tests/gc/bug-1308048.js
new file mode 100644
index 0000000000..7de26784b4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1308048.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+m = 'x';
+for (var i = 0; i < 10; i++)
+ m += m;
+offThreadCompileToStencil("", ({elementAttributeName: m}));
+var n = newGlobal();
+gczeal(2,1);
+var stencil = n.finishOffThreadStencil();
+n.evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1310589.js b/js/src/jit-test/tests/gc/bug-1310589.js
new file mode 100644
index 0000000000..2907c3c440
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1310589.js
@@ -0,0 +1,298 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+a = o = s = r = []
+o2 = s2 = r2 = g2 = f2 = m2 = Map
+e2 = Set
+v2 = b2 = new ArrayBuffer
+t2 = new Uint8ClampedArray
+minorgc()
+x = /x/
+for (var i = 0; i < 4; ++i) {
+ function f1() {}
+}
+Object.defineProperty(a, 12, {}).push(1);
+toString = (function() { return a.reverse(); })
+oomTest(Date.prototype.toJSON)
+function f1000(){}
+function f1001(){}
+function f1002(){}
+function f1003(){}
+function f1004(){}
+function f1005(){}
+function f1006(){}
+function f1007(){}
+function f1008(){}
+function f1009(){}
+function f1010(){}
+function f1011(){}
+function f1012(){}
+function f1013(){}
+function f1014(){}
+function f1015(){}
+function f1016(){}
+function f1017(){}
+function f1018(){}
+function f1019(){}
+function f1020(){}
+function f1021(){}
+function f1022(){}
+function f1023(){}
+function f1024(){}
+function f1025(){}
+function f1026(){}
+function f1027(){}
+function f1028(){}
+function f1029(){}
+function f1030(){}
+function f1031(){}
+function f1032(){}
+function f1033(){}
+function f1034(){}
+function f1035(){}
+function f1036(){}
+function f1037(){}
+function f1038(){}
+function f1039(){}
+function f1040(){}
+function f1041(){}
+function f1042(){}
+function f1043(){}
+function f1044(){}
+function f1045(){}
+function f1046(){}
+function f1047(){}
+function f1048(){}
+function f1049(){}
+function f1050(){}
+function f1051(){}
+function f1052(){}
+function f1053(){}
+function f1054(){}
+function f1055(){}
+function f1056(){}
+function f1057(){}
+function f1058(){}
+function f1059(){}
+function f1060(){}
+function f1061(){}
+function f1062(){}
+function f1063(){}
+function f1064(){}
+function f1065(){}
+function f1066(){}
+function f1067(){}
+function f1068(){}
+function f1069(){}
+function f1070(){}
+function f1071(){}
+function f1072(){}
+function f1073(){}
+function f1074(){}
+function f1075(){}
+function f1076(){}
+function f1077(){}
+function f1078(){}
+function f1079(){}
+function f1080(){}
+function f1081(){}
+function f1082(){}
+function f1083(){}
+function f1084(){}
+function f1085(){}
+function f1086(){}
+function f1087(){}
+function f1088(){}
+function f1089(){}
+function f1090(){}
+function f1091(){}
+function f1092(){}
+function f1093(){}
+function f1094(){}
+function f1095(){}
+function f1096(){}
+function f1097(){}
+function f1098(){}
+function f1099(){}
+function f1100(){}
+function f1101(){}
+function f1102(){}
+function f1103(){}
+function f1104(){}
+function f1105(){}
+function f1106(){}
+function f1107(){}
+function f1108(){}
+function f1109(){}
+function f1110(){}
+function f1111(){}
+function f1112(){}
+function f1113(){}
+function f1114(){}
+function f1115(){}
+function f1116(){}
+function f1117(){}
+function f1118(){}
+function f1119(){}
+function f1120(){}
+function f1121(){}
+function f1122(){}
+function f1123(){}
+function f1124(){}
+function f1125(){}
+function f1126(){}
+function f1127(){}
+function f1128(){}
+function f1129(){}
+function f1130(){}
+function f1131(){}
+function f1132(){}
+function f1133(){}
+function f1134(){}
+function f1135(){}
+function f1136(){}
+function f1137(){}
+function f1138(){}
+function f1139(){}
+function f1140(){}
+function f1141(){}
+function f1142(){}
+function f1143(){}
+function f1144(){}
+function f1145(){}
+function f1146(){}
+function f1147(){}
+function f1148(){}
+function f1149(){}
+function f1150(){}
+function f1151(){}
+function f1152(){}
+function f1153(){}
+function f1154(){}
+function f1155(){}
+function f1156(){}
+function f1157(){}
+function f1158(){}
+function f1159(){}
+function f1160(){}
+function f1161(){}
+function f1162(){}
+function f1163(){}
+function f1164(){}
+function f1165(){}
+function f1166(){}
+function f1167(){}
+function f1168(){}
+function f1169(){}
+function f1170(){}
+function f1171(){}
+function f1172(){}
+function f1173(){}
+function f1174(){}
+function f1175(){}
+function f1176(){}
+function f1177(){}
+function f1178(){}
+function f1179(){}
+function f1180(){}
+function f1181(){}
+function f1182(){}
+function f1183(){}
+function f1184(){}
+function f1185(){}
+function f1186(){}
+function f1187(){}
+function f1188(){}
+function f1189(){}
+function f1190(){}
+function f1191(){}
+function f1192(){}
+function f1193(){}
+function f1194(){}
+function f1195(){}
+function f1196(){}
+function f1197(){}
+function f1198(){}
+function f1199(){}
+function f1200(){}
+function f1201(){}
+function f1202(){}
+function f1203(){}
+function f1204(){}
+function f1205(){}
+function f1206(){}
+function f1207(){}
+function f1208(){}
+function f1209(){}
+function f1210(){}
+function f1211(){}
+function f1212(){}
+function f1213(){}
+function f1214(){}
+function f1215(){}
+function f1216(){}
+function f1217(){}
+function f1218(){}
+function f1219(){}
+function f1220(){}
+function f1221(){}
+function f1222(){}
+function f1223(){}
+function f1224(){}
+function f1225(){}
+function f1226(){}
+function f1227(){}
+function f1228(){}
+function f1229(){}
+function f1230(){}
+function f1231(){}
+function f1232(){}
+function f1233(){}
+function f1234(){}
+function f1235(){}
+function f1236(){}
+function f1237(){}
+function f1238(){}
+function f1239(){}
+function f1240(){}
+function f1241(){}
+function f1242(){}
+function f1243(){}
+function f1244(){}
+function f1245(){}
+function f1246(){}
+function f1247(){}
+function f1248(){}
+function f1249(){}
+function f1250(){}
+function f1251(){}
+function f1252(){}
+function f1253(){}
+function f1254(){}
+function f1255(){}
+function f1256(){}
+function f1257(){}
+function f1258(){}
+function f1259(){}
+function f1260(){}
+function f1261(){}
+function f1262(){}
+function f1263(){}
+function f1264(){}
+function f1265(){}
+function f1266(){}
+function f1267(){}
+function f1268(){}
+function f1269(){}
+function f1270(){}
+function f1271(){}
+function f1272(){}
+function f1273(){}
+function f1274(){}
+function f1275(){}
+function f1276(){}
+function f1277(){}
+function f1278(){}
+function f1279(){}
+function f1280(){}
+function f1281(){}
+function f1282(){}
diff --git a/js/src/jit-test/tests/gc/bug-1311060.js b/js/src/jit-test/tests/gc/bug-1311060.js
new file mode 100644
index 0000000000..6237a4575d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1311060.js
@@ -0,0 +1,2 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+evalInWorker(`schedulezone("s1");`);
diff --git a/js/src/jit-test/tests/gc/bug-1313347.js b/js/src/jit-test/tests/gc/bug-1313347.js
new file mode 100644
index 0000000000..c5f684f7a6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1313347.js
@@ -0,0 +1,5 @@
+let tenured = {};
+gc();
+for (let i = 0; i < 100000; i++) {
+ tenured[i/2] = {};
+}
diff --git a/js/src/jit-test/tests/gc/bug-1315946.js b/js/src/jit-test/tests/gc/bug-1315946.js
new file mode 100644
index 0000000000..a0668d00b5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1315946.js
@@ -0,0 +1,13 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+// Don't run a full oomTest because it takes ages - a few iterations are
+// sufficient to trigger the bug.
+let i = 0;
+
+oomTest(Function(`
+ if (i < 10) {
+ i++;
+ gczeal(15,1);
+ foo;
+ }
+`));
diff --git a/js/src/jit-test/tests/gc/bug-1321597.js b/js/src/jit-test/tests/gc/bug-1321597.js
new file mode 100644
index 0000000000..d5d96ddab6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1321597.js
@@ -0,0 +1,6 @@
+gczeal(9,3);
+function test(s, okLine) { };
+var dbg = new Debugger;
+dbg.onNewGlobalObject = function(global) {};
+x = evalcx(test());
+shortestPaths(["\$4"], { start: this, maxNumPaths: 5 });
diff --git a/js/src/jit-test/tests/gc/bug-1322420.js b/js/src/jit-test/tests/gc/bug-1322420.js
new file mode 100644
index 0000000000..750eefa3c1
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1322420.js
@@ -0,0 +1,8 @@
+options('strict_mode');
+var g1 = newGlobal({newCompartment: true});
+var g2 = newGlobal({newCompartment: true});
+var dbg = new Debugger();
+dbg.addDebuggee(g1);
+g1.eval('function f() {}');
+gczeal(9, 1);
+dbg.findScripts({});
diff --git a/js/src/jit-test/tests/gc/bug-1322648.js b/js/src/jit-test/tests/gc/bug-1322648.js
new file mode 100644
index 0000000000..bb6279f525
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1322648.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+gczeal(0);
+print = function(s) {}
+startgc(1);
+offThreadCompileToStencil("");
+gczeal(10, 3);
+for (var count = 0; count < 20; count++) {
+ print(count);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1323868.js b/js/src/jit-test/tests/gc/bug-1323868.js
new file mode 100644
index 0000000000..1d37a19ba0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1323868.js
@@ -0,0 +1,6 @@
+// |jit-test| allow-oom; skip-if: helperThreadCount() === 0
+
+gczeal(0);
+startgc(8301);
+offThreadCompileToStencil("(({a,b,c}))");
+gcparam("maxBytes", gcparam("gcBytes"));
diff --git a/js/src/jit-test/tests/gc/bug-1324512.js b/js/src/jit-test/tests/gc/bug-1324512.js
new file mode 100644
index 0000000000..9366b2c277
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1324512.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+evalInWorker(`
+ if (!('gczeal' in this))
+ quit();
+ try {
+ gczeal(2,1);
+ throw new Error();
+ } catch (e) {
+ assertEq("" + e, "Error");
+ }
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1325551.js b/js/src/jit-test/tests/gc/bug-1325551.js
new file mode 100644
index 0000000000..700f61daf8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1325551.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+let g = newGlobal({newCompartment: true});
+let dbg = new Debugger;
+let gw = dbg.addDebuggee(g);
+g.eval("function f(){}");
+oomTest(() => {
+ gw.makeDebuggeeValue(g.f).script.source.sourceMapURL = 'a';
+});
+
diff --git a/js/src/jit-test/tests/gc/bug-1328251.js b/js/src/jit-test/tests/gc/bug-1328251.js
new file mode 100644
index 0000000000..5e492af8fe
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1328251.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+evalInWorker(`
+ if (!('gczeal' in this))
+ quit();
+ gczeal(2);
+ for (let i = 0; i < 30; i++) {
+ var a = [1, 2, 3];
+ a.indexOf(1);
+ relazifyFunctions(); }
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1332773.js b/js/src/jit-test/tests/gc/bug-1332773.js
new file mode 100644
index 0000000000..77445bc2eb
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1332773.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+evalInWorker(`
+var gTestcases = new Array();
+typeof document != "object" || !document.location.href.match(/jsreftest.html/);
+gczeal(4, 10);
+f = ([a = class target extends b {}, b] = [void 0]) => {};
+f()
+`)
diff --git a/js/src/jit-test/tests/gc/bug-1337414.js b/js/src/jit-test/tests/gc/bug-1337414.js
new file mode 100644
index 0000000000..9e647a810c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1337414.js
@@ -0,0 +1,46 @@
+var lfLogBuffer = `
+gczeal(15,10);
+try {
+ a = []
+ gczeal(2, 2)()
+} catch (e) {}
+a.every(function() {})
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 5
+`;
+lfLogBuffer = lfLogBuffer.split('\n');
+lfPreamble = `
+`;
+var lfCodeBuffer = "";
+var lfRunTypeLimit = 7;
+var lfOffThreadGlobal = newGlobal();
+try {} catch (lfVare5) {}
+var lfAccumulatedCode = lfPreamble;
+while (true) {
+ var line = lfLogBuffer.shift();
+ if (line == null) {
+ break;
+ } else if (line == "//corefuzz-dcd-endofdata") {
+ loadFile(lfCodeBuffer);
+ } else if (line.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+ loadFile(line);
+ } else {
+ lfCodeBuffer += line + "\n";
+ }
+}
+if (lfCodeBuffer) loadFile(lfCodeBuffer);
+function loadFile(lfVarx) {
+ try {
+ if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+ lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % lfRunTypeLimit;
+ } else {
+ switch (lfRunTypeId) {
+ case 5:
+ evalInWorker(lfAccumulatedCode);
+ evaluate(lfVarx);
+ }
+ }
+ } catch (lfVare) {
+ lfAccumulatedCode += "try { evaluate(`\n" + lfVarx + "\n`); } catch(exc) {}\n";
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1338383.js b/js/src/jit-test/tests/gc/bug-1338383.js
new file mode 100644
index 0000000000..930566492d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1338383.js
@@ -0,0 +1,17 @@
+// |jit-test| error: InternalError
+
+if (helperThreadCount() === 0)
+ throw InternalError();
+
+var lfOffThreadGlobal = newGlobal();
+enableShellAllocationMetadataBuilder()
+lfOffThreadGlobal.offThreadCompileToStencil(`
+ if ("gczeal" in this)
+ gczeal(8, 1)
+ function recurse(x) {
+ recurse(x + 1);
+ };
+ recurse(0);
+`);
+var stencil = lfOffThreadGlobal.finishOffThreadStencil();
+lfOffThreadGlobal.evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1340010.js b/js/src/jit-test/tests/gc/bug-1340010.js
new file mode 100644
index 0000000000..30c3cb08b9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1340010.js
@@ -0,0 +1,22 @@
+// |jit-test| skip-if: helperThreadCount() === 0 || !('deterministicgc' in this)
+gczeal(0);
+
+gc();
+function weighted(wa) {
+ var a = [];
+ for (var i = 0; i < 30; ++i) {
+ for (var j = 0; j < 20; ++j) {
+ a.push(0);
+ }
+ }
+ return a;
+}
+var statementMakers = weighted();
+if (typeof oomTest == "function") {
+ statementMakers = statementMakers.concat([function (d, b) {
+ return "oomTest(" + makeFunction(d - 1, b) + ")";
+ }, ]);
+}
+deterministicgc(true);
+startgc(9469, "shrinking");
+offThreadCompileToStencil("");
diff --git a/js/src/jit-test/tests/gc/bug-1342261.js b/js/src/jit-test/tests/gc/bug-1342261.js
new file mode 100644
index 0000000000..bcf4c586ba
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1342261.js
@@ -0,0 +1,7 @@
+// With --baseline-eager, create a cross-compartment wrapper IC referring to a
+// target in an otherwise-doomed compartment.
+
+fullcompartmentchecks(true);
+newGlobal({
+ sameZoneAs: []
+}).z;
diff --git a/js/src/jit-test/tests/gc/bug-1354480.js b/js/src/jit-test/tests/gc/bug-1354480.js
new file mode 100644
index 0000000000..c80ec01f9f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1354480.js
@@ -0,0 +1,10 @@
+// |jit-test| error: TypeError
+test = "function f(a) { return a } f`a$b`";
+evalWithCache(test, {});
+dbg = new Debugger();
+gczeal(9, 1);
+dbg.findScripts('January 0 0 is invalid');
+function evalWithCache(code, ctx) {
+ ctx.global = newGlobal();
+ evaluate(code, ctx)
+}
diff --git a/js/src/jit-test/tests/gc/bug-1357022.js b/js/src/jit-test/tests/gc/bug-1357022.js
new file mode 100644
index 0000000000..f7665f2d4b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1357022.js
@@ -0,0 +1,6 @@
+const root3 = newGlobal({newCompartment: true});
+function test(constructor) {
+ if (!nukeCCW(root3.load)) {}
+}
+test(Map);
+test(Set);
diff --git a/js/src/jit-test/tests/gc/bug-1359252.js b/js/src/jit-test/tests/gc/bug-1359252.js
new file mode 100644
index 0000000000..9359ec1a75
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1359252.js
@@ -0,0 +1,7 @@
+gczeal(4);
+setJitCompilerOption("ion.warmup.trigger", 1);
+function h() {
+ for ([a, b] in { z: 9 }) {}
+}
+for (var j = 0; j < 10; j++)
+ h();
diff --git a/js/src/jit-test/tests/gc/bug-1370069.js b/js/src/jit-test/tests/gc/bug-1370069.js
new file mode 100644
index 0000000000..7117a22175
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1370069.js
@@ -0,0 +1,6 @@
+// |jit-test| error:ReferenceError
+try {
+ eval("}");
+} catch (exc) {}
+gczeal(17, 1);
+6.900653737167637, (yield);
diff --git a/js/src/jit-test/tests/gc/bug-1371908.js b/js/src/jit-test/tests/gc/bug-1371908.js
new file mode 100644
index 0000000000..e669f6b63a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1371908.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+gczeal(0);
+offThreadCompileToStencil("");
+startgc(0);
+var stencil = finishOffThreadStencil();
+evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1374797.js b/js/src/jit-test/tests/gc/bug-1374797.js
new file mode 100644
index 0000000000..f22b06e278
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1374797.js
@@ -0,0 +1,27 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+// Exercise triggering GC of atoms zone while off-thread parsing is happening.
+
+gczeal(0);
+
+// Reduce some GC parameters so that we can trigger a GC more easily.
+gcparam('lowFrequencyHeapGrowth', 120);
+gcparam('highFrequencyLargeHeapGrowth', 120);
+gcparam('highFrequencySmallHeapGrowth', 120);
+gcparam('allocationThreshold', 1);
+gc();
+
+// Start an off-thread parse.
+offThreadCompileToStencil("print('Finished')");
+
+// Allocate lots of atoms, parsing occasionally.
+for (let i = 0; i < 10; i++) {
+ print(i);
+ for (let j = 0; j < 10000; j++)
+ Symbol.for(i + 10 * j);
+ eval(`${i}`);
+}
+
+// Finish the off-thread parse.
+var stencil = finishOffThreadStencil();
+evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug-1382431.js b/js/src/jit-test/tests/gc/bug-1382431.js
new file mode 100644
index 0000000000..86fb418121
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1382431.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+var fe = "vv";
+for (i = 0; i < 24; i++) fe += fe;
+offThreadCompileToStencil(fe, {});
diff --git a/js/src/jit-test/tests/gc/bug-1384047.js b/js/src/jit-test/tests/gc/bug-1384047.js
new file mode 100644
index 0000000000..4ec6a5272d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1384047.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+newGlobal();
+evalcx("oomTest(newGlobal);", newGlobal());
diff --git a/js/src/jit-test/tests/gc/bug-1388701.js b/js/src/jit-test/tests/gc/bug-1388701.js
new file mode 100644
index 0000000000..8f0a6292c8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1388701.js
@@ -0,0 +1,18 @@
+load(libdir + "asserts.js");
+
+// This is out of range
+assertErrorMessage(
+ () => gcparam('maxNurseryBytes', 0),
+ Error,
+ "Parameter value out of range");
+
+assertErrorMessage(
+ () => gcparam('maxNurseryBytes', 5),
+ Error,
+ "Parameter value out of range");
+
+// this is okay
+gcparam('maxNurseryBytes', 1024*1024);
+assertEq(gcparam('maxNurseryBytes'), 1024*1024);
+gc();
+
diff --git a/js/src/jit-test/tests/gc/bug-1390087.js b/js/src/jit-test/tests/gc/bug-1390087.js
new file mode 100644
index 0000000000..4d77467ca0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1390087.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+setGCCallback({
+ action: "majorGC"
+});
+gcparam("allocationThreshold", 1);
+offThreadCompileToStencil("");
+for (let i = 0; i < 40000; i++)
+ Symbol.for(i);
+eval(0);
diff --git a/js/src/jit-test/tests/gc/bug-1399889.js b/js/src/jit-test/tests/gc/bug-1399889.js
new file mode 100644
index 0000000000..356c682273
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1399889.js
@@ -0,0 +1,4 @@
+gczeal(7, 1);
+enableShellAllocationMetadataBuilder();
+var testMap = new Map();
+for (var [k, v] of testMap) {}
diff --git a/js/src/jit-test/tests/gc/bug-1401141.js b/js/src/jit-test/tests/gc/bug-1401141.js
new file mode 100644
index 0000000000..6ed1bea611
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1401141.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !('gczeal' in this) || helperThreadCount() === 0
+
+gczeal(15,1);
+setGCCallback({
+ action: "majorGC",
+});
+gcslice(3);
+var lfGlobal = newGlobal();
+lfGlobal.offThreadCompileToStencil("");
+
diff --git a/js/src/jit-test/tests/gc/bug-1411302.js b/js/src/jit-test/tests/gc/bug-1411302.js
new file mode 100644
index 0000000000..20c051edd9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1411302.js
@@ -0,0 +1,17 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+let lfPreamble = `
+ value:{
+`;
+try {
+ evaluate("");
+ evalInWorker("");
+} catch (exc) {}
+try {
+ evalInWorker("");
+} catch (exc) {}
+try {
+ oomTest(function() {
+ eval("function testDeepBail1() {");
+ });
+} catch (exc) {}
diff --git a/js/src/jit-test/tests/gc/bug-1413914.js b/js/src/jit-test/tests/gc/bug-1413914.js
new file mode 100644
index 0000000000..20f90d90b4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1413914.js
@@ -0,0 +1,4 @@
+let a = grayRoot();
+a[0] = {};
+gczeal(18);
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1430752.js b/js/src/jit-test/tests/gc/bug-1430752.js
new file mode 100644
index 0000000000..cb4986140f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1430752.js
@@ -0,0 +1,2 @@
+gczeal(18);
+gcslice(3);
diff --git a/js/src/jit-test/tests/gc/bug-1435295.js b/js/src/jit-test/tests/gc/bug-1435295.js
new file mode 100644
index 0000000000..01214a6214
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1435295.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this)
+
+oomTest(new Function(`function execOffThread(source) {
+ offThreadCompileModuleToStencil(source);
+ var stencil = finishOffThreadStencil();
+ return instantiateModuleStencil(stencil);
+}
+b = execOffThread("[1, 2, 3]")
+`));
diff --git a/js/src/jit-test/tests/gc/bug-1435321.js b/js/src/jit-test/tests/gc/bug-1435321.js
new file mode 100644
index 0000000000..f8c0172d24
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1435321.js
@@ -0,0 +1,30 @@
+// Check that corresponding parameters are updated to ensure that invariants are
+// preserved when updating various GC parameters.
+
+gcparam('highFrequencyLargeHeapGrowth', 200);
+gcparam('highFrequencySmallHeapGrowth', 400);
+assertEq(gcparam('highFrequencyLargeHeapGrowth'), 200);
+assertEq(gcparam('highFrequencySmallHeapGrowth'), 400);
+
+gcparam('highFrequencySmallHeapGrowth', 150);
+assertEq(gcparam('highFrequencyLargeHeapGrowth'), 150);
+assertEq(gcparam('highFrequencySmallHeapGrowth'), 150);
+
+gcparam('highFrequencyLargeHeapGrowth', 300);
+assertEq(gcparam('highFrequencyLargeHeapGrowth'), 300);
+assertEq(gcparam('highFrequencySmallHeapGrowth'), 300);
+
+// The following parameters are stored in bytes but specified/retrieved in MiB.
+
+gcparam('smallHeapSizeMax', 200);
+gcparam('largeHeapSizeMin', 500);
+assertEq(gcparam('smallHeapSizeMax'), 200);
+assertEq(gcparam('largeHeapSizeMin'), 500);
+
+gcparam('largeHeapSizeMin', 100);
+assertEq(gcparam('smallHeapSizeMax'), 99);
+assertEq(gcparam('largeHeapSizeMin'), 100);
+
+gcparam('smallHeapSizeMax', 300);
+assertEq(gcparam('smallHeapSizeMax'), 300);
+assertEq(gcparam('largeHeapSizeMin'), 300);
diff --git a/js/src/jit-test/tests/gc/bug-1439284.js b/js/src/jit-test/tests/gc/bug-1439284.js
new file mode 100644
index 0000000000..4e6bcd11b0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1439284.js
@@ -0,0 +1,16 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+gcparam('allocationThreshold', 1);
+setGCCallback({
+ action: "majorGC",
+});
+offThreadCompileToStencil(('Boolean.prototype.toString.call(new String())'));
+for (let i = 0; i < 10; i++) {
+ for (let j = 0; j < 10000; j++) Symbol.for(i + 10 * j);
+}
+try {
+ var stencil = finishOffThreadStencil();
+ evalStencil(stencil);
+} catch (e) {
+ assertEq(e.constructor, TypeError);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1449887.js b/js/src/jit-test/tests/gc/bug-1449887.js
new file mode 100644
index 0000000000..ef7fa45c7f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1449887.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(function() { x, 0, { z: function() {} } });
diff --git a/js/src/jit-test/tests/gc/bug-1456508.js b/js/src/jit-test/tests/gc/bug-1456508.js
new file mode 100644
index 0000000000..3a6c5be85a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1456508.js
@@ -0,0 +1,4 @@
+// |jit-test| error: ReferenceError
+gczeal(11, 5);
+gczeal(22, 9);
+grayRoot().map = wm;
diff --git a/js/src/jit-test/tests/gc/bug-1456536.js b/js/src/jit-test/tests/gc/bug-1456536.js
new file mode 100644
index 0000000000..adfe3f0c9b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1456536.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(new Function(`let a = grayRoot();`));
diff --git a/js/src/jit-test/tests/gc/bug-1459568.js b/js/src/jit-test/tests/gc/bug-1459568.js
new file mode 100644
index 0000000000..57affab64f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1459568.js
@@ -0,0 +1,7 @@
+gczeal(0);
+setMarkStackLimit(1);
+gczeal(18, 1);
+grayRoot()[0] = "foo";
+grayRoot()[1] = {};
+grayRoot().x = 0;
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1459860.js b/js/src/jit-test/tests/gc/bug-1459860.js
new file mode 100644
index 0000000000..d474995d4e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1459860.js
@@ -0,0 +1,8 @@
+// |jit-test| allow-overrecursed; skip-if: helperThreadCount() === 0
+function eval(source) {
+ offThreadCompileModuleToStencil(source);
+ let get = (eval("function w(){}") ++);
+};
+gczeal(21, 10);
+gczeal(11, 8);
+eval("");
diff --git a/js/src/jit-test/tests/gc/bug-1461319.js b/js/src/jit-test/tests/gc/bug-1461319.js
new file mode 100644
index 0000000000..46f9ad50cd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1461319.js
@@ -0,0 +1,7 @@
+// |jit-test| error: InternalError
+gczeal(14);
+var g = newGlobal({newCompartment: true});
+g.eval('function f(a) { evaluate("f(" + " - 1);", {newContext: true}); }');
+var dbg = new Debugger(g);
+dbg.onEnterFrame = function(frame) {};
+g.f();
diff --git a/js/src/jit-test/tests/gc/bug-1461448.js b/js/src/jit-test/tests/gc/bug-1461448.js
new file mode 100644
index 0000000000..b9f4d8dd0c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1461448.js
@@ -0,0 +1,40 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+gczeal(0);
+
+let lfPreamble = `
+ var lfOffThreadGlobal = newGlobal({newCompartment: true});
+ for (lfLocal in this)
+ try {} catch(lfVare5) {}
+`;
+evaluate(lfPreamble);
+evaluate(`
+ var g = newGlobal({newCompartment: true});
+ var dbg = new Debugger;
+ var gw = dbg.addDebuggee(g);
+ for (lfLocal in this)
+ if (!(lfLocal in lfOffThreadGlobal))
+ try {
+ lfOffThreadGlobal[lfLocal] = this[lfLocal];
+ } catch(lfVare5) {}
+ var g = newGlobal({newCompartment: true});
+ var gw = dbg.addDebuggee(g);
+`);
+lfOffThreadGlobal.offThreadCompileToStencil(`
+ setMarkStackLimit(1);
+ grayRoot()[0] = "foo";
+`);
+var stencil = lfOffThreadGlobal.finishOffThreadStencil();
+lfOffThreadGlobal.evalStencil(stencil);
+eval(`
+ var lfOffThreadGlobal = newGlobal({newCompartment: true});
+ try { evaluate(\`
+ gczeal(18, 1);
+ grayRoot()[0] = "foo";
+ let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+ \\\`(module
+ (memory (export "memory") 1 1)
+ )\\\`
+ )));
+\`); } catch(exc) {}
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1462337.js b/js/src/jit-test/tests/gc/bug-1462337.js
new file mode 100644
index 0000000000..84a5392a1f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1462337.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(function() {
+ grayRoot().x = Object.create((obj[name]++));
+});
+oomTest(function() {
+ gczeal(9);
+ gcslice(new.target);
+});
diff --git a/js/src/jit-test/tests/gc/bug-1464872.js b/js/src/jit-test/tests/gc/bug-1464872.js
new file mode 100644
index 0000000000..adf8c4f8c1
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1464872.js
@@ -0,0 +1,16 @@
+gczeal(0);
+
+var g = newGlobal({newCompartment: true});
+var dbg = Debugger(g);
+dbg.onEnterFrame = function(frame) {};
+
+var g2 = newGlobal({newCompartment: true});
+g2[g] = g;
+g2.evaluate("grayRoot()")
+g2 = undefined;
+
+g = undefined;
+dbg = undefined;
+
+gc();
+startgc(100000);
diff --git a/js/src/jit-test/tests/gc/bug-1468792.js b/js/src/jit-test/tests/gc/bug-1468792.js
new file mode 100644
index 0000000000..9049aca721
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1468792.js
@@ -0,0 +1,18 @@
+// |jit-test| error: ReferenceError
+
+loadFile(`
+ gczeal(2,9);
+ evaluate(\`
+ reportCompare(expect, actual, summary);
+ \`);
+`);
+function loadFile(lfVarx) {
+ try {
+ evaluate(lfVarx);
+ } catch (lfVare) {}
+}
+eval("(function(){({6953421313:0})})")();
+function f() {
+ x[6953421313] = "a";
+}
+f();
diff --git a/js/src/jit-test/tests/gc/bug-1472734.js b/js/src/jit-test/tests/gc/bug-1472734.js
new file mode 100644
index 0000000000..f88f3af1c6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1472734.js
@@ -0,0 +1,13 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+try {
+ oomTest(function() {
+ eval(`
+ function eval(source) {
+ offThreadCompileModuleToStencil(source);
+ minorgc();
+ }
+ eval("");
+ `);
+ });
+} catch (exc) {}
diff --git a/js/src/jit-test/tests/gc/bug-1478943.js b/js/src/jit-test/tests/gc/bug-1478943.js
new file mode 100644
index 0000000000..c736f11ab4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1478943.js
@@ -0,0 +1,3 @@
+gczeal(0);
+setMarkStackLimit(1);
+startgc(1);
diff --git a/js/src/jit-test/tests/gc/bug-1490042.js b/js/src/jit-test/tests/gc/bug-1490042.js
new file mode 100644
index 0000000000..b043f25486
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1490042.js
@@ -0,0 +1,31 @@
+// |jit-test| --no-ion; --no-baseline; --no-blinterp; skip-if: !('gcstate' in this && 'oomAfterAllocations' in this)
+
+gczeal(0);
+
+// Create a bunch of ObjectGroups with TypeNewScript attached.
+const count = 1000;
+let c = [];
+let a = [];
+for (let i = 0; i < count; i++) {
+ c[i] = function() { this.a = 1; this.b = 0; this.c = 2; };
+ a[i] = new c[i];
+}
+
+// Start an incremental GC and run until we're about to sweep objects.
+assertEq(gcstate(), "NotActive");
+gczeal(21);
+startgc(1);
+
+// Run incremental slices with simulated OOM set up to provoke OOM when sweeping
+// types.
+assertEq(gcstate(), "Sweep");
+gczeal(10);
+unsetgczeal(20);
+while (gcstate() == "Sweep") {
+ oomAfterAllocations(2);
+ gcslice(1);
+ resetOOMFailure();
+}
+
+// Ensure our type information stays alive.
+let x = c.length + a.length;
diff --git a/js/src/jit-test/tests/gc/bug-1491326.js b/js/src/jit-test/tests/gc/bug-1491326.js
new file mode 100644
index 0000000000..b750177d87
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1491326.js
@@ -0,0 +1,9 @@
+// |jit-test| --fuzzing-safe
+
+if (!('oomTest' in this))
+ quit();
+
+var g = newGlobal({newCompartment: true});
+g.parent = this;
+g.eval("new Debugger(parent).onExceptionUnwind = function() {}");
+oomTest(() => l, (true));
diff --git a/js/src/jit-test/tests/gc/bug-1498177.js b/js/src/jit-test/tests/gc/bug-1498177.js
new file mode 100644
index 0000000000..d423d47dcb
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1498177.js
@@ -0,0 +1,17 @@
+
+let a, b;
+for (i=0; i < 300000; i++) {
+ let c = { a: a, b: b };
+ a = b;
+ b = c;
+}
+
+gc();
+
+// GCRuntime::setLargeHeapSizeMinBytes will change the low value to be one
+// byte lower than the high value (if necessary). But this blew up later
+// when the values were mistakingly cast to float then compared, rather than
+// kept as size_t.
+gcparam('largeHeapSizeMin', 99);
+
+
diff --git a/js/src/jit-test/tests/gc/bug-1505622.js b/js/src/jit-test/tests/gc/bug-1505622.js
new file mode 100644
index 0000000000..3f6f83c076
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1505622.js
@@ -0,0 +1,54 @@
+// Test that we don't repeatedly trigger last-ditch GCs.
+
+// Turn of any zeal which will disrupt GC number checks.
+gczeal(0);
+
+function allocUntilFail() {
+ gc(null, 'shrinking');
+
+ const initialSize = gcparam("gcBytes");
+ const initialMaxSize = gcparam("maxBytes");
+ const initGCNumber = gcparam("majorGCNumber");
+
+ // Set a small heap limit.
+ gcparam("maxBytes", initialSize + 16 * 1024);
+
+ let error;
+ try {
+ let a = [];
+ while (true) {
+ a.push(Symbol()); // Symbols are tenured.
+ }
+ } catch(err) {
+ error = err;
+ }
+
+ const finalGCNumber = gcparam("majorGCNumber");
+
+ // Resetore heap limit.
+ gcparam("maxBytes", initialMaxSize);
+
+ gc();
+ assertEq(error, "out of memory");
+ return finalGCNumber - initGCNumber;
+}
+
+// Set the time limit for skipping last ditch GCs to 5 seconds.
+gcparam("minLastDitchGCPeriod", 5);
+assertEq(gcparam("minLastDitchGCPeriod"), 5);
+
+// Allocate up to the heap limit. This triggers a last ditch GC.
+let gcCount = allocUntilFail();
+assertEq(gcCount, 1)
+
+// Allocate up to the limit again. The second time we fail without
+// triggering a GC.
+gcCount = allocUntilFail();
+assertEq(gcCount, 0)
+
+// Wait for time limit to expire.
+sleep(6);
+
+// Check we trigger a GC again.
+gcCount = allocUntilFail();
+assertEq(gcCount, 1)
diff --git a/js/src/jit-test/tests/gc/bug-1513991.js b/js/src/jit-test/tests/gc/bug-1513991.js
new file mode 100644
index 0000000000..c694b04b39
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1513991.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+evalInWorker(`
+var sym4 = Symbol.match;
+function test(makeNonArray) {}
+function basicSweeping() {}
+var wm1 = new WeakMap();
+wm1.set(basicSweeping, sym4);
+startgc(100000, 'shrinking');
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1514927.js b/js/src/jit-test/tests/gc/bug-1514927.js
new file mode 100644
index 0000000000..8f2f1ef52b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1514927.js
@@ -0,0 +1,6 @@
+gczeal(0);
+var x = newGlobal();
+x.evaluate("grayRoot()");
+x = 0;
+setMarkStackLimit(4);
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1515993.js b/js/src/jit-test/tests/gc/bug-1515993.js
new file mode 100644
index 0000000000..8b87000918
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1515993.js
@@ -0,0 +1,15 @@
+ignoreUnhandledRejections();
+
+function checkGetOffsetsCoverage() {
+ var g = newGlobal({newCompartment: true});
+ var dbg = Debugger(g);
+ var topLevel;
+ dbg.onNewScript = function(s) {
+ topLevel = s;
+ };
+ g.eval(`import(() => 1)`);
+ topLevel.getChildScripts();
+}
+checkGetOffsetsCoverage();
+gczeal(14, 10);
+Object.defineProperty(this, "fuzzutils", {});
diff --git a/js/src/jit-test/tests/gc/bug-1517158.js b/js/src/jit-test/tests/gc/bug-1517158.js
new file mode 100644
index 0000000000..6ffd8ea40a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1517158.js
@@ -0,0 +1,21 @@
+gczeal(0);
+
+(function() {
+ var g = newGlobal({newCompartment: true});
+ g.debuggeeGlobal = this;
+ g.eval("(" + function() {
+ dbg = new Debugger(debuggeeGlobal);
+ dbg.onExceptionUnwind = function(frame, exc) {
+ var s = '!';
+ for (var f = frame; f; f = f.older)
+ if (f.type === "call")
+ s += f.callee.name;
+ };
+ } + ")();");
+ try {
+ h();
+ } catch (e) {}
+ g.dbg.enabled = false;
+})();
+// jsfunfuzz-generated
+startgc(114496726);
diff --git a/js/src/jit-test/tests/gc/bug-1520778.js b/js/src/jit-test/tests/gc/bug-1520778.js
new file mode 100644
index 0000000000..df72248983
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1520778.js
@@ -0,0 +1,18 @@
+// |jit-test| error: ReferenceError
+gczeal(0);
+setMarkStackLimit(1);
+var g = newGlobal({
+ newCompartment: true
+});
+var dbg = new Debugger;
+var gw = dbg.addDebuggee(g);
+dbg.onDebuggerStatement = function(frame) {
+ frame.environment.parent.getVariable('y')
+};
+g.eval(`
+ let y = 1;
+ g = function () { debugger; };
+ g();
+`);
+gczeal(9, 10);
+f4();
diff --git a/js/src/jit-test/tests/gc/bug-1530643.js b/js/src/jit-test/tests/gc/bug-1530643.js
new file mode 100644
index 0000000000..6e9d068cc7
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1530643.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomAtAllocation' in this); error: Error
+
+// OOM testing of worker threads is disallowed because it's not thread safe.
+oomAtAllocation(11, 11);
diff --git a/js/src/jit-test/tests/gc/bug-1531018.js b/js/src/jit-test/tests/gc/bug-1531018.js
new file mode 100644
index 0000000000..efca854197
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1531018.js
@@ -0,0 +1,3 @@
+gczeal(14,1);
+const one = BigInt(1);
+new Map([[one, 42]]).has(one);
diff --git a/js/src/jit-test/tests/gc/bug-1531626.js b/js/src/jit-test/tests/gc/bug-1531626.js
new file mode 100644
index 0000000000..bf003e96cc
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1531626.js
@@ -0,0 +1,61 @@
+// Test that setting the nursery size works as expected.
+//
+// It's an error to set the minimum size greater than the maximum
+// size. Parameter values are rounded to the nearest legal nursery
+// size.
+
+load(libdir + "asserts.js");
+
+const chunkSizeKB = gcparam('chunkBytes') / 1024;
+const pageSizeKB = gcparam('systemPageSizeKB');
+
+const testSizesKB = [128, 129, 255, 256, 516, 1023, 1024, 3*1024, 4*1024+1, 16*1024];
+
+// Valid maximum sizes must be >= 1MB.
+const testMaxSizesKB = testSizesKB.filter(x => x >= 1024);
+
+for (let max of testMaxSizesKB) {
+ // Don't test minimums greater than the maximum.
+ for (let min of testSizesKB.filter(x => x <= max)) {
+ setMinMax(min, max);
+ }
+}
+
+// The above loops raised the nursery size. Now reduce it to ensure that
+// forcibly-reducing it works correctly.
+setMinMax(256, 1024);
+
+// Try invalid configurations.
+const badSizesKB = [ 0, 1, 129 * 1024];
+function assertParamOutOfRange(f) {
+ assertErrorMessage(f, Object, "Parameter value out of range");
+}
+for (let size of badSizesKB) {
+ assertParamOutOfRange(() => gcparam('minNurseryBytes', size * 1024));
+ assertParamOutOfRange(() => gcparam('maxNurseryBytes', size * 1024));
+}
+
+function setMinMax(min, max) {
+ gcparam('minNurseryBytes', min * 1024);
+ gcparam('maxNurseryBytes', max * 1024);
+ assertEq(gcparam('minNurseryBytes'), nearestLegalSize(min) * 1024);
+ assertEq(gcparam('maxNurseryBytes'), nearestLegalSize(max) * 1024);
+ allocateSomeThings();
+ gc();
+}
+
+function allocateSomeThings() {
+ for (let i = 0; i < 1000; i++) {
+ let obj = { an: 'object', with: 'fields' };
+ }
+}
+
+function nearestLegalSize(sizeKB) {
+ let step = sizeKB >= chunkSizeKB ? chunkSizeKB : pageSizeKB;
+ return round(sizeKB, step);
+}
+
+function round(x, y) {
+ x += y / 2;
+ return x - (x % y);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1532376.js b/js/src/jit-test/tests/gc/bug-1532376.js
new file mode 100644
index 0000000000..c523ba06b8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1532376.js
@@ -0,0 +1,10 @@
+"use strict";
+function __f_276() {
+ this.getNameReallyHard = () => eval("eval('(() => this.name)()')")
+}
+for (var __v_1377 = 0; __v_1377 < 10000; __v_1377++) {
+ var __v_1378 = new __f_276();
+ try {
+ __v_1376[__getRandomProperty()];
+ } catch (e) {}
+__v_1378.getNameReallyHard()}
diff --git a/js/src/jit-test/tests/gc/bug-1540670.js b/js/src/jit-test/tests/gc/bug-1540670.js
new file mode 100644
index 0000000000..4b271a4fee
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1540670.js
@@ -0,0 +1,17 @@
+// Adapted from a fuzz test, the origianl test was:
+//
+// gcparam('minNurseryBytes', 0);
+// gczeal(4);
+//
+// And would trick the GC into a state where the nursery had been setup but
+// thought that it was disabled.
+
+load(libdir + "asserts.js");
+
+assertErrorMessage(
+ () => gcparam('minNurseryBytes', 0),
+ Error,
+ "Parameter value out of range");
+
+gczeal(4);
+
diff --git a/js/src/jit-test/tests/gc/bug-1542279.js b/js/src/jit-test/tests/gc/bug-1542279.js
new file mode 100644
index 0000000000..0efdc32ce2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1542279.js
@@ -0,0 +1,13 @@
+
+load(libdir + "asserts.js");
+
+assertErrorMessage(
+ () => gcparam('maxNurseryBytes', 2 ** 32 - 1),
+ Error,
+ "Parameter value out of range");
+gc()
+
+gcparam('minNurseryBytes', 32*1024);
+gcparam('maxNurseryBytes', 64*1024);
+gc()
+
diff --git a/js/src/jit-test/tests/gc/bug-1542982.js b/js/src/jit-test/tests/gc/bug-1542982.js
new file mode 100644
index 0000000000..feeb8522c2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1542982.js
@@ -0,0 +1,20 @@
+
+load(libdir + "asserts.js");
+
+assertErrorMessage(
+ () => gcparam('minNurseryBytes', 0),
+ Error,
+ "Parameter value out of range");
+
+assertErrorMessage(
+ () => gcparam('maxNurseryBytes', 256*1024*1024),
+ Error,
+ "Parameter value out of range");
+
+// This is both bigger than the maximum and out of range. but there's no way
+// to test out of range without testing bigger than the maximum.
+assertErrorMessage(
+ () => gcparam('minNurseryBytes', 256*1024*1024),
+ Error,
+ "Parameter value out of range");
+
diff --git a/js/src/jit-test/tests/gc/bug-1543014.js b/js/src/jit-test/tests/gc/bug-1543014.js
new file mode 100644
index 0000000000..9f91ad3526
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1543014.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+gczeal(0);
+evalInWorker(`
+ var sym4 = Symbol.match;
+ function basicSweeping() {};
+ var wm1 = new WeakMap();
+ wm1.set(basicSweeping, sym4);
+ startgc(100000, 'shrinking');
+`);
+gczeal(2);
+var d1 = newGlobal({});
diff --git a/js/src/jit-test/tests/gc/bug-1543589.js b/js/src/jit-test/tests/gc/bug-1543589.js
new file mode 100644
index 0000000000..38d5eb9249
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1543589.js
@@ -0,0 +1,16 @@
+// |jit-test| skip-if: getBuildConfiguration()["arm64-simulator"] === true
+// This test times out in ARM64 simulator builds.
+
+gczeal(0);
+gcparam('maxNurseryBytes', 16 * 1024 * 1024);
+gcparam('minNurseryBytes', 16 * 1024 * 1024);
+
+let a = [];
+for (var i = 0; i < 20000; i++) {
+ a.push(import("nonexistent.js"));
+ Symbol();
+}
+
+for (let p of a) {
+ p.catch(() => {});
+}
diff --git a/js/src/jit-test/tests/gc/bug-1556155.js b/js/src/jit-test/tests/gc/bug-1556155.js
new file mode 100644
index 0000000000..3c0dd11251
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1556155.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('oomTest' in this)
+a = [];
+minorgc();
+Object.defineProperty(a, 12, {}).push(1);
+toString = (function() { return a.reverse(); });
+oomTest(Date.prototype.toJSON);
+oomTest(Date.prototype.toJSON);
diff --git a/js/src/jit-test/tests/gc/bug-1557928.js b/js/src/jit-test/tests/gc/bug-1557928.js
new file mode 100644
index 0000000000..7746d353fb
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1557928.js
@@ -0,0 +1,19 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+var g = newGlobal({ newCompartment: true });
+var dbg = new Debugger;
+var gw = dbg.addDebuggee(g);
+lfOffThreadGlobal = g;
+lfOffThreadGlobal.offThreadCompileToStencil(`
+ grayRoot()[0] = "foo";
+ `);
+var stencil = lfOffThreadGlobal.finishOffThreadStencil();
+lfOffThreadGlobal.evalStencil(stencil);
+var g = newGlobal({newCompartment: true});
+var gw = dbg.addDebuggee(g);
+lfOffThreadGlobal = null;
+gc();
+schedulezone(this);
+schedulezone('atoms');
+gc('zone');
+
diff --git a/js/src/jit-test/tests/gc/bug-1565272.js b/js/src/jit-test/tests/gc/bug-1565272.js
new file mode 100644
index 0000000000..96dc0c63b9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1565272.js
@@ -0,0 +1,20 @@
+// |jit-test| --fuzzing-safe; --ion-offthread-compile=off; --ion-warmup-threshold=10; skip-if: (getBuildConfiguration()['android'] && getBuildConfiguration()['debug'] && getBuildConfiguration()['arm64'])
+
+// Test that Nursery::disable() waits for poisoning to finish before
+// discarding and re-poisoning its chunks.
+
+for(var i = 0; i < 100; i++) {
+ try {
+ evalInWorker(`
+ function testOneSize(current_size) {
+ var eval_string = 'obj = {';
+ for (var current = 0; current <= current_size; ++current)
+ eval_string += 'k' + current + ':' + current + ','
+ }
+ testOneSize(1023);
+ testOneSize(1024);
+ gczeal(4);
+ `);
+ } catch (exc) {}
+}
+
diff --git a/js/src/jit-test/tests/gc/bug-1568119.js b/js/src/jit-test/tests/gc/bug-1568119.js
new file mode 100644
index 0000000000..1aed85c325
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1568119.js
@@ -0,0 +1,21 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function allocateSomeStuff() {
+ return {a: "a fish", b: [1, 2, 3]};
+}
+
+oomTest(() => {
+ // Run a minor GC with a small nursery.
+ gcparam('minNurseryBytes', 256 * 1024);
+ gcparam('maxNurseryBytes', 256 * 1024);
+ allocateSomeStuff();
+ minorgc();
+
+ // Run a minor GC with a larger nursery to get it to attempt to grow and
+ // fail the allocation there.
+ gcparam('maxNurseryBytes', 1024 * 1024);
+ gcparam('minNurseryBytes', 1024 * 1024);
+ allocateSomeStuff();
+ minorgc();
+});
+
diff --git a/js/src/jit-test/tests/gc/bug-1568740.js b/js/src/jit-test/tests/gc/bug-1568740.js
new file mode 100644
index 0000000000..6cc003cb94
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1568740.js
@@ -0,0 +1,20 @@
+gczeal(0);
+
+function setAndTest(param, value) {
+ gcparam(param, value);
+ assertEq(gcparam(param), value);
+}
+
+
+// Set a large nursery size.
+setAndTest("maxNurseryBytes", 1024*1024);
+setAndTest("minNurseryBytes", 1024*1024);
+minorgc();
+assertEq(gcparam("nurseryBytes"), 1024*1024);
+
+// Force it to shrink by more then one half.
+setAndTest("minNurseryBytes", 64*1024);
+setAndTest("maxNurseryBytes", 64*1024);
+minorgc();
+assertEq(gcparam("nurseryBytes"), 64*1024);
+
diff --git a/js/src/jit-test/tests/gc/bug-1569840.js b/js/src/jit-test/tests/gc/bug-1569840.js
new file mode 100644
index 0000000000..70d28add73
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1569840.js
@@ -0,0 +1,9 @@
+
+gczeal(0);
+
+gcparam("maxNurseryBytes", 1024*1024);
+gcparam("minNurseryBytes", 1024*1024);
+var obj = { foo: 'bar', baz: [1, 2, 3]};
+minorgc();
+assertEq(gcparam("nurseryBytes"), 1024*1024);
+
diff --git a/js/src/jit-test/tests/gc/bug-1571439.js b/js/src/jit-test/tests/gc/bug-1571439.js
new file mode 100644
index 0000000000..ebaa8e80d2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1571439.js
@@ -0,0 +1,24 @@
+// |jit-test| --ion-offthread-compile=off; --blinterp-warmup-threshold=1; error:TypeError
+
+gcparam("maxBytes", 1024*1024);
+function complex(aReal, aImag) {
+ this.r35 = aReal;
+ gczeal(4, 10);
+ this.square = function() {
+ return new complex(this.r35 * this.r35 - this.i14 * this.i14, 2 * this.r35 * this.i14);
+ }
+}
+function mandelbrotValueOO(aC, aIterMax) {
+ let Z90 = new complex(0.0, 0.0);
+ Z90 = Z90.square().add(aC);
+}
+const width = 60;
+const height = 60;
+const max_iters = 50;
+for (let img_x = 0; img_x < width; img_x++) {
+ for (let img_y = 0; img_y < height; img_y++) {
+ let C57 = new complex(-2 + (img_x / width) * 3, -1.5 + (img_y / height) * 3);
+ var res = mandelbrotValueOO(C57, max_iters);
+ }
+}
+
diff --git a/js/src/jit-test/tests/gc/bug-1573458.js b/js/src/jit-test/tests/gc/bug-1573458.js
new file mode 100644
index 0000000000..f33b57eac5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1573458.js
@@ -0,0 +1,4 @@
+gczeal(0);
+setGCCallback({action: "enterNullRealm"});
+gczeal(2, 1);
+Symbol();
diff --git a/js/src/jit-test/tests/gc/bug-1574877.js b/js/src/jit-test/tests/gc/bug-1574877.js
new file mode 100644
index 0000000000..e53746bcb0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1574877.js
@@ -0,0 +1,17 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function parseModule(source) {
+ offThreadCompileModuleToStencil(source);
+ var stencil = finishOffThreadStencil();
+ return instantiateModuleStencil(stencil);
+}
+function loadFile(lfVarx) {
+ oomTest(function() {
+ parseModule(lfVarx);
+ });
+}
+loadFile(`
+ expect = new class prototype extends Object {
+ a43 = function () {}
+ }
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1578462.js b/js/src/jit-test/tests/gc/bug-1578462.js
new file mode 100644
index 0000000000..da3644511a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1578462.js
@@ -0,0 +1,59 @@
+// |jit-test| skip-if: !hasFunction["gczeal"]
+
+// Check that incoming CCW edges are marked gray in a zone GC if the
+// source is gray.
+
+// Set up a new compartment with a gray object wrapper to this
+// compartment.
+function createOtherCompartment() {
+ let t = {};
+ addMarkObservers([t]);
+ let g = newGlobal({newCompartment: true});
+ g.t = t;
+ g.eval(`grayRoot().push(t);`);
+ g.t = null;
+ t = null;
+ return g;
+}
+
+function startGCMarking() {
+ startgc(1);
+ while (gcstate() === "Prepare") {
+ gcslice(1);
+ }
+}
+
+gczeal(0);
+
+let g = createOtherCompartment();
+
+// The target should be gray in a full GC...
+gc();
+assertEq(getMarks()[0], "gray");
+
+// and subsequently gray in a zone GC of only this compartment.
+gc(this);
+assertEq(getMarks()[0], "gray");
+
+// If a barrier marks the gray wrapper black after the start of the
+// GC, the target ends up black.
+schedulezone(this);
+startGCMarking()
+assertEq(getMarks()[0], "unmarked");
+g.eval(`grayRoot()`); // Barrier marks gray roots black.
+assertEq(getMarks()[0], "black");
+finishgc();
+
+// A full collection resets the wrapper to gray.
+gc();
+assertEq(getMarks()[0], "gray");
+
+// If a barrier marks the gray wrapper black after the target has
+// already been marked gray, the target ends up black.
+gczeal(25); // Yield during gray marking.
+schedulezone(this);
+startGCMarking();
+assertEq(getMarks()[0], "gray");
+g.eval(`grayRoot()`); // Barrier marks gray roots black.
+assertEq(getMarks()[0], "black");
+finishgc();
diff --git a/js/src/jit-test/tests/gc/bug-1579025.js b/js/src/jit-test/tests/gc/bug-1579025.js
new file mode 100644
index 0000000000..c11e7fdf1e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1579025.js
@@ -0,0 +1,58 @@
+// Test that a zone GC collects the selected zones.
+
+function waitForState(state) {
+ while (gcstate() !== state && gcstate() !== "NotActive") {
+ gcslice(100);
+ }
+}
+
+gczeal(0);
+gc();
+
+let z1 = this;
+let z2 = newGlobal({newCompartment: true});
+
+// Initially nothing is being collected.
+
+assertEq(gcstate(), "NotActive");
+assertEq(gcstate(z1), "NoGC");
+assertEq(gcstate(z2), "NoGC");
+
+// No zones selected => full GC.
+
+startgc(1);
+
+// It's non-deterministic whether we see the prepare state or not.
+waitForState("Mark");
+
+assertEq(gcstate(), "Mark");
+assertEq(gcstate(z1), "MarkBlackOnly");
+assertEq(gcstate(z2), "MarkBlackOnly");
+finishgc();
+
+// Use of schedulezone() => zone GC.
+
+schedulezone(z1);
+startgc(1);
+waitForState("Mark");
+assertEq(gcstate(), "Mark");
+assertEq(gcstate(z1), "MarkBlackOnly");
+assertEq(gcstate(z2), "NoGC");
+finishgc();
+
+schedulezone(z2);
+startgc(1);
+waitForState("Mark");
+assertEq(gcstate(), "Mark");
+assertEq(gcstate(z1), "NoGC");
+assertEq(gcstate(z2), "MarkBlackOnly");
+finishgc();
+
+schedulezone(z1);
+schedulezone(z2);
+startgc(1);
+waitForState("Mark");
+assertEq(gcstate(), "Mark");
+assertEq(gcstate(z1), "MarkBlackOnly");
+assertEq(gcstate(z2), "MarkBlackOnly");
+finishgc();
diff --git a/js/src/jit-test/tests/gc/bug-1585159.js b/js/src/jit-test/tests/gc/bug-1585159.js
new file mode 100644
index 0000000000..077dee8832
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1585159.js
@@ -0,0 +1,11 @@
+
+// Set a nursery size larger than the current nursery size.
+gcparam("minNurseryBytes", 1024 * 1024);
+gcparam("maxBytes", gcparam("gcBytes") + 4096);
+
+// Allocate something in the nursery.
+let obj = {'foo': 38, 'bar': "A string"};
+
+// Trigger a last-ditch GC.
+print(gc(0, "last-ditch"));
+
diff --git a/js/src/jit-test/tests/gc/bug-1590176.js b/js/src/jit-test/tests/gc/bug-1590176.js
new file mode 100644
index 0000000000..db446d56ae
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1590176.js
@@ -0,0 +1,13 @@
+// Create function clones in a separate Zone so that the GC can relazify them.
+let g = newGlobal({newCompartment: true});
+g.evaluate(`
+ function factory() {
+ return function() { };
+ }
+`);
+
+// Delazify, Relazify, Delazify
+g.factory()();
+finishgc();
+startgc(0, 'shrinking');
+g.factory()();
diff --git a/js/src/jit-test/tests/gc/bug-1590904.js b/js/src/jit-test/tests/gc/bug-1590904.js
new file mode 100644
index 0000000000..ab3e7b432f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1590904.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !getBuildConfiguration()['has-gczeal']
+gczeal(0);
+gczeal(20);
+startgc(1);
+gczeal(10);
+while (gcstate() == "Sweep") {}
diff --git a/js/src/jit-test/tests/gc/bug-1592487.js b/js/src/jit-test/tests/gc/bug-1592487.js
new file mode 100644
index 0000000000..6f3343836f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1592487.js
@@ -0,0 +1,28 @@
+try {} catch (e) {}
+try {} catch (e) {}
+try {
+ (function() {
+ (function() {});
+ })
+} catch (e) {}
+try {
+ t;
+} catch (e) {}
+try {} catch (e) {}
+try {
+ try {
+ setMarkStackLimit(1);
+ } catch (e) {}
+} catch (e) {}
+try {
+ (function() {
+ ""(function() {});
+ })
+} catch (e) {}
+try {
+ for (t of Number) {
+ (function() {
+ (function() {});
+ })
+ }
+} catch (e) {}
diff --git a/js/src/jit-test/tests/gc/bug-1593975.js b/js/src/jit-test/tests/gc/bug-1593975.js
new file mode 100644
index 0000000000..69ebcafb94
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1593975.js
@@ -0,0 +1,21 @@
+// |jit-test| error: ReferenceError
+function runtest(func) {
+ func();
+}
+const g1 = newGlobal({
+ newCompartment: true
+});
+function transplantMarking() {
+ const vals = {};
+ vals.map = new WeakMap();
+ enqueueMark(vals.map);
+ enqueueMark("yield");
+ enqueueMark("enter-weak-marking-mode");
+}
+if (this.enqueueMark) {
+ enqueueMark("enter-weak-marking-mode");
+ runtest(transplantMarking);
+ egc = 60;
+ gcslice(egc * 100);
+}
+x();
diff --git a/js/src/jit-test/tests/gc/bug-1597970.js b/js/src/jit-test/tests/gc/bug-1597970.js
new file mode 100644
index 0000000000..5b384a399f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1597970.js
@@ -0,0 +1,5 @@
+enableShellAllocationMetadataBuilder();
+evaluate(`
+ gczeal(9,3);
+ new FinalizationRegistry(function() {});
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1600238.js b/js/src/jit-test/tests/gc/bug-1600238.js
new file mode 100644
index 0000000000..6b6aeb85ac
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1600238.js
@@ -0,0 +1,21 @@
+gczeal(0);
+newGlobal();
+nukeAllCCWs();
+function f() {
+ global = newGlobal({
+ newCompartment: true
+ });
+ try {
+ return global.eval("new FinalizationRegistry(function(){})");
+ } catch (e) {
+ if (e instanceof TypeError && e.message.includes('dead')) {
+ // Creating a new CCW to the global fails with
+ // --more-compartments option.
+ quit();
+ }
+ throw e;
+ }
+}
+r = f();
+r.register({}, {}, {});
+startgc();
diff --git a/js/src/jit-test/tests/gc/bug-1602741.js b/js/src/jit-test/tests/gc/bug-1602741.js
new file mode 100644
index 0000000000..5f1cc7eca8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1602741.js
@@ -0,0 +1,24 @@
+// Test that drainJobQueue() drains all jobs, including those queued
+// by FinalizationRegistry callbacks.
+
+let finalizeRan = false;
+let promiseRan = false;
+
+let fr = new FinalizationRegistry(() => {
+ finalizeRan = true;
+ Promise.resolve().then(() => {
+ promiseRan = true;
+ });
+});
+
+fr.register({}, {});
+
+gc();
+
+assertEq(finalizeRan, false);
+assertEq(promiseRan, false);
+
+drainJobQueue();
+
+assertEq(finalizeRan, true);
+assertEq(promiseRan, true);
diff --git a/js/src/jit-test/tests/gc/bug-1603330.js b/js/src/jit-test/tests/gc/bug-1603330.js
new file mode 100644
index 0000000000..888ae40a07
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1603330.js
@@ -0,0 +1,16 @@
+// Allocate the object in the function to prevent marked as a singleton so the
+// object won't be kept alive by IC stub.
+function allocObj() { return {}; }
+
+let wr;
+{
+ let obj = allocObj();
+ wr = new WeakRef(obj);
+}
+
+assertEq(wr.deref() !== undefined, true);
+
+clearKeptObjects();
+gc();
+
+assertEq(wr.deref(), undefined);
diff --git a/js/src/jit-test/tests/gc/bug-1603917.js b/js/src/jit-test/tests/gc/bug-1603917.js
new file mode 100644
index 0000000000..c1b58f9a28
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1603917.js
@@ -0,0 +1,2 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+evalInWorker("new WeakRef({});");
diff --git a/js/src/jit-test/tests/gc/bug-1605348.js b/js/src/jit-test/tests/gc/bug-1605348.js
new file mode 100644
index 0000000000..f55b13af32
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1605348.js
@@ -0,0 +1,5 @@
+fullcompartmentchecks(true);
+var g37 = newGlobal({
+ newCompartment: true
+});
+new g37.WeakRef({});
diff --git a/js/src/jit-test/tests/gc/bug-1605633.js b/js/src/jit-test/tests/gc/bug-1605633.js
new file mode 100644
index 0000000000..87f5668886
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1605633.js
@@ -0,0 +1,12 @@
+newGlobal();
+nukeAllCCWs();
+var g28 = newGlobal({
+ newCompartment: true
+});
+try {
+ let wr6 = new g28.WeakRef(newGlobal());
+ new WeakRef(wr6);
+} catch (e) {
+ assertEq(e.message == "can't access dead object", true);
+}
+
diff --git a/js/src/jit-test/tests/gc/bug-1607495.js b/js/src/jit-test/tests/gc/bug-1607495.js
new file mode 100644
index 0000000000..a759f27dff
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1607495.js
@@ -0,0 +1,6 @@
+gczeal(14, 2);
+var g32 = newGlobal();
+let wr6 = new g32.WeakRef(newGlobal({
+ newCompartment: true
+}));
+let wr7 = new WeakRef(wr6);
diff --git a/js/src/jit-test/tests/gc/bug-1607665.js b/js/src/jit-test/tests/gc/bug-1607665.js
new file mode 100644
index 0000000000..a54a4e0455
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1607665.js
@@ -0,0 +1,9 @@
+var ta = new BigInt64Array(12);
+
+var s = "";
+for (var i = 0; i < ta.length; ++i) {
+ var x = ta[i];
+ s += x;
+}
+
+assertEq(s, "000000000000");
diff --git a/js/src/jit-test/tests/gc/bug-1607687.js b/js/src/jit-test/tests/gc/bug-1607687.js
new file mode 100644
index 0000000000..dd7bde2ad1
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1607687.js
@@ -0,0 +1,4 @@
+var limit = 1 << 16;
+var m = new Map;
+for (var i = 1n; i < limit; i++)
+ m.set(i, i);
diff --git a/js/src/jit-test/tests/gc/bug-1608355.js b/js/src/jit-test/tests/gc/bug-1608355.js
new file mode 100644
index 0000000000..359292aabc
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1608355.js
@@ -0,0 +1,7 @@
+try {
+ enableShellAllocationMetadataBuilder();
+ gczeal(11);
+ gczeal(22);
+ grayRoot() = 0;
+} catch (e) {}
+newGlobal();
diff --git a/js/src/jit-test/tests/gc/bug-1610621.js b/js/src/jit-test/tests/gc/bug-1610621.js
new file mode 100644
index 0000000000..8db61cb264
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1610621.js
@@ -0,0 +1 @@
+assertEq(clearKeptObjects(), undefined);
diff --git a/js/src/jit-test/tests/gc/bug-1620195.js b/js/src/jit-test/tests/gc/bug-1620195.js
new file mode 100644
index 0000000000..7ee5540e26
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1620195.js
@@ -0,0 +1,15 @@
+var g99 = newGlobal({});
+nukeAllCCWs();
+let group = new FinalizationRegistry(x90 => 0);
+let other = newGlobal({
+ newCompartment: true
+});
+
+try {
+ group.register(evalcx('({})', other), 1);
+} catch (e) {
+ // With the args --more-compartments evalcx will throw.
+ assertEq(e.message == "can't access dead object" ||
+ e.message == "Invalid scope argument to evalcx",
+ true);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1620196.js b/js/src/jit-test/tests/gc/bug-1620196.js
new file mode 100644
index 0000000000..34fda22c80
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1620196.js
@@ -0,0 +1,7 @@
+gczeal(4);
+let heldValues = [];
+registry = new FinalizationRegistry(value => {
+ heldValues.push(value);
+});
+registry.register({}, 42);
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1620209.js b/js/src/jit-test/tests/gc/bug-1620209.js
new file mode 100644
index 0000000000..a7d7f61374
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1620209.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+verifyprebarriers();
+offThreadCompileToStencil('');
+var dbg = new Debugger();
+var objects = dbg.findObjects();
diff --git a/js/src/jit-test/tests/gc/bug-1620213.js b/js/src/jit-test/tests/gc/bug-1620213.js
new file mode 100644
index 0000000000..7eeb62a652
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1620213.js
@@ -0,0 +1,3 @@
+for (var i = 0; i < 32768; i++) {
+ new ArrayBuffer(1024*1024);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1620221.js b/js/src/jit-test/tests/gc/bug-1620221.js
new file mode 100644
index 0000000000..d363dab3dd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1620221.js
@@ -0,0 +1,23 @@
+function testStepping(script, expected) {
+ let g = newGlobal({newCompartment: true});
+ let f = g.eval(script);
+ let log = [];
+ function maybePause(frame) {
+ let line = frame.script.getOffsetLocation(frame.offset).lineNumber;
+ log.push(line);
+ }
+ let dbg = new Debugger(g);
+ dbg.onEnterFrame = frame => {
+ maybePause(frame);
+ };
+ f();
+}
+var g7 = newGlobal({newCompartment: true});
+g7.parent = this;
+g7.eval(`
+ Debugger(parent).onEnterFrame = function(frame) {
+ let v = frame.environment.getVariable('var0');
+ };
+`);
+testStepping("(function() {})");
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1628440.js b/js/src/jit-test/tests/gc/bug-1628440.js
new file mode 100644
index 0000000000..e128fcb7ac
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1628440.js
@@ -0,0 +1,9 @@
+// |jit-test| error: ReferenceError
+gczeal(10, 2);
+let cleanup = function(iter) {}
+let key = {"k": "this is my key"};
+let fg = new FinalizationRegistry(cleanup);
+let object = {};
+fg.register(object, {}, key);
+let success = fg.unregister(key);
+throw new ReferenceError();
diff --git a/js/src/jit-test/tests/gc/bug-1643913.js b/js/src/jit-test/tests/gc/bug-1643913.js
new file mode 100644
index 0000000000..1f83a9a0a3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1643913.js
@@ -0,0 +1,36 @@
+gczeal(0);
+
+for (let p of [false, true]) {
+ f(p);
+
+ // Run an incremental GC to completion.
+ startgc(1);
+ while (gcstate() !== 'NotActive') {
+ gcslice(10000, { dontStart: true });
+ }
+}
+
+function ccwToObject() {
+ return evalcx('({})', newGlobal({newCompartment: true}));
+}
+
+function ccwToRegistry() {
+ return evalcx('new FinalizationRegistry(value => {})',
+ newGlobal({newCompartment: true}));
+}
+
+function f(p) {
+ let registry = ccwToRegistry();
+ let target = ccwToObject();
+ registry.register(target, undefined);
+
+ // Add a CCW from registry to target zone or vice versa to control
+ // the order the zones are swept in.
+ if (p) {
+ registry.ptr = target;
+ } else {
+ target.ptr = registry;
+ }
+
+ gc();
+}
diff --git a/js/src/jit-test/tests/gc/bug-1644985-2.js b/js/src/jit-test/tests/gc/bug-1644985-2.js
new file mode 100644
index 0000000000..fbb00b59de
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1644985-2.js
@@ -0,0 +1,4 @@
+let fr = new FinalizationRegistry(x => 1);
+fr.register(evalcx('({})', newGlobal({newCompartment: true})));
+nukeAllCCWs();
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1644985.js b/js/src/jit-test/tests/gc/bug-1644985.js
new file mode 100644
index 0000000000..aac0e99277
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1644985.js
@@ -0,0 +1,4 @@
+let wr = new WeakRef(evalcx('({})', newGlobal({newCompartment: true})));
+nukeAllCCWs();
+clearKeptObjects();
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1647747-debugger-weakmark.js b/js/src/jit-test/tests/gc/bug-1647747-debugger-weakmark.js
new file mode 100644
index 0000000000..4eb4944c27
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1647747-debugger-weakmark.js
@@ -0,0 +1,17 @@
+var g27 = newGlobal({newCompartment: true});
+g27.debuggeeGlobal = this;
+g27.eval(`
+ dbg = new Debugger(debuggeeGlobal);
+ dbg.onExceptionUnwind = function (frame, exc) {};
+`);
+s45 = newGlobal({newCompartment: true});
+try {
+ evalcx(`
+ function h(h) {}
+ h.valueOf=g;
+ `, s45);
+} catch (x) {}
+try {
+ evalcx("throw h", s45)
+} catch (x) {}
+gcslice(100000);
diff --git a/js/src/jit-test/tests/gc/bug-1648901.js b/js/src/jit-test/tests/gc/bug-1648901.js
new file mode 100644
index 0000000000..13e4895068
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1648901.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('gczeal' in this)
+
+gczeal(15);
+enableShellAllocationMetadataBuilder();
+var registry = new FinalizationRegistry(x => 0);
+gczeal(9, 3);
+registry.register({}, 1, {});
diff --git a/js/src/jit-test/tests/gc/bug-1651345.js b/js/src/jit-test/tests/gc/bug-1651345.js
new file mode 100644
index 0000000000..54187c5d55
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1651345.js
@@ -0,0 +1,7 @@
+function main() {
+ const v3 = {get:Object};
+ const v5 = Object.defineProperty(Object,0,v3);
+ addMarkObservers(Object)
+ gc();
+}
+main();
diff --git a/js/src/jit-test/tests/gc/bug-1652425.js b/js/src/jit-test/tests/gc/bug-1652425.js
new file mode 100644
index 0000000000..fec591948c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1652425.js
@@ -0,0 +1,11 @@
+// |jit-test| error: Error
+
+function varying(mapColor, keyColor) {
+ enqueueMark(`set-color-${keyColor}`);
+ enqueueMark("yield");
+ startgc(100000);
+}
+for (const mapColor of ['gray', 'black']) {
+ for (const keyColor of ['gray', 'black', 'unmarked'])
+ varying(mapColor, keyColor);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1652492.js b/js/src/jit-test/tests/gc/bug-1652492.js
new file mode 100644
index 0000000000..a64f541a12
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1652492.js
@@ -0,0 +1,17 @@
+function f1() {
+ arr = f2;
+ var p = arr[(gczeal(9))|0];
+}
+f2 = f1;
+f2();
+try {
+ function allocObj() { return {}; }
+ {
+ let obj = allocObj();
+ wr = new WeakRef(obj);
+ }
+ clearKeptObjects();
+(new obj);
+} catch(exc) {}
+let obj = allocObj();
+wr = new WeakRef(obj);
diff --git a/js/src/jit-test/tests/gc/bug-1654186.js b/js/src/jit-test/tests/gc/bug-1654186.js
new file mode 100644
index 0000000000..562938d8f8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1654186.js
@@ -0,0 +1,8 @@
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
+
+gczeal(14, 5);
+var g = newGlobal();
+g.eval("(" + function() {
+ oomAfterAllocations(100);
+} + ")()");
+f.x("");
diff --git a/js/src/jit-test/tests/gc/bug-1655917.js b/js/src/jit-test/tests/gc/bug-1655917.js
new file mode 100644
index 0000000000..5805958ed7
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1655917.js
@@ -0,0 +1,10 @@
+var g = newGlobal({ newCompartment: true });
+g.eval(`
+ var obj = {};
+ var ref = new WeakRef(obj);
+ Promise.resolve().then(() => {
+ assertEq(ref.deref(), obj);
+ });
+`);
+nukeCCW(g.ref);
+drainJobQueue();
diff --git a/js/src/jit-test/tests/gc/bug-1657554.js b/js/src/jit-test/tests/gc/bug-1657554.js
new file mode 100644
index 0000000000..f751d442b9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1657554.js
@@ -0,0 +1,2 @@
+// |jit-test| skip-if: !('oomTest' in this)
+oomTest(() => eval("new WeakRef({});"));
diff --git a/js/src/jit-test/tests/gc/bug-1660293.js b/js/src/jit-test/tests/gc/bug-1660293.js
new file mode 100644
index 0000000000..a2c953c11f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1660293.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: !('oomAfterAllocations' in this)
+
+try {
+function varying(mapColor, keyColor) {
+ enqueueMark(`set-color-${keyColor}`);
+ enqueueMark("yield");
+ startgc(100000);
+}
+for (const mapColor of ['gray', 'black'])
+ varying(mapColor, 0x7fff);
+} catch (exc) {}
+oomAfterAllocations(100);
diff --git a/js/src/jit-test/tests/gc/bug-1667336.js b/js/src/jit-test/tests/gc/bug-1667336.js
new file mode 100644
index 0000000000..11b1fd456d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1667336.js
@@ -0,0 +1,17 @@
+// |jit-test| --ion-offthread-compile=off; skip-if: helperThreadCount() === 0
+
+var g = newGlobal();
+gczeal(9, 1);
+gczeal(11, 2);
+g.offThreadCompileToStencil("");
+setJitCompilerOption("offthread-compilation.enable", 1);
+
+for (let i = 0 ; i < 3000; i++) {
+ loadFile();
+}
+
+function loadFile() {
+ try {
+ x;
+ } catch(exc) {}
+}
diff --git a/js/src/jit-test/tests/gc/bug-1671125.js b/js/src/jit-test/tests/gc/bug-1671125.js
new file mode 100644
index 0000000000..74944ea350
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1671125.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+verifyprebarriers()
+evalInWorker(`
+ Object.defineProperty(this, "x", {});
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1688749.js b/js/src/jit-test/tests/gc/bug-1688749.js
new file mode 100644
index 0000000000..1135646d64
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1688749.js
@@ -0,0 +1,160 @@
+function f(x) {
+ Math.round == Math.round();
+}
+function g(f, y) {
+ for (var k = 0; k < 9999; ++k) {
+ f()
+ }
+}
+g(f, [
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ /x/,
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ createIsHTMLDDA(),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+ new Boolean(false),
+]);
+for (var k = 0; k < 99999; ++k) {
+ f()
+}
diff --git a/js/src/jit-test/tests/gc/bug-1689039.js b/js/src/jit-test/tests/gc/bug-1689039.js
new file mode 100644
index 0000000000..4bfe9bd140
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1689039.js
@@ -0,0 +1,232 @@
+// |jit-test| skip-if: !('oomAfterAllocations' in this)
+
+gczeal(7);
+for (let i = 0; i < 9999; ++i) {
+ undefined + "y";
+}
+function f(x) {
+ x.replace(/ /g, " ");
+ x.replace(/ /g, " ");
+}
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+f(" ");
+oomAfterAllocations(1);
diff --git a/js/src/jit-test/tests/gc/bug-1689794.js b/js/src/jit-test/tests/gc/bug-1689794.js
new file mode 100644
index 0000000000..159016edb8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1689794.js
@@ -0,0 +1,22 @@
+// |jit-test| error: Error; --cpu-count=2; --fast-warmup
+
+function main() {
+ new main;
+}
+try { main(); } catch(exc) {}
+evaluate(`
+ function main() {
+ function v0(v1,v2) {
+ try {
+ let v3 = v0();
+ } catch(v28) {
+ eval(\`
+ v13(v11,RegExp);
+ \`);
+ }
+ }
+ new Promise(v0);
+ }
+ main();
+ gczeal(7,1);
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1691901.js b/js/src/jit-test/tests/gc/bug-1691901.js
new file mode 100644
index 0000000000..c9bb02e7aa
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1691901.js
@@ -0,0 +1,4 @@
+gczeal(0);
+enableShellAllocationMetadataBuilder();
+setMarkStackLimit(1);
+Function('gc()'.replace(/x/))();
diff --git a/js/src/jit-test/tests/gc/bug-1692221.js b/js/src/jit-test/tests/gc/bug-1692221.js
new file mode 100644
index 0000000000..6300788ad9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1692221.js
@@ -0,0 +1,39 @@
+// |jit-test| allow-oom; skip-if: !('oomAtAllocation' in this)
+
+// Test TenuredChunk::decommitFreeArenasWithoutUnlocking updates chunk
+// metadata correctly. The data is checked by assertions so this test is about
+// exercising the code in question.
+
+function allocateGarbage() {
+ gc();
+ for (let j = 0; j < 100000; j++) {
+ Symbol();
+ }
+}
+
+function collectUntilDecommit() {
+ startgc(1);
+ while (gcstate() != "NotActive" && gcstate() != "Decommit") {
+ gcslice(1000);
+ }
+}
+
+function triggerSyncDecommit() {
+ reportLargeAllocationFailure(1);
+}
+
+gczeal(0);
+
+// Normally we skip decommit if GCs are happening frequently. Disable that for
+// this test
+gcparam("highFrequencyTimeLimit", 0);
+
+allocateGarbage();
+collectUntilDecommit();
+triggerSyncDecommit();
+
+allocateGarbage();
+collectUntilDecommit();
+oomAtAllocation(10);
+triggerSyncDecommit();
+resetOOMFailure();
diff --git a/js/src/jit-test/tests/gc/bug-1695861.js b/js/src/jit-test/tests/gc/bug-1695861.js
new file mode 100644
index 0000000000..6bb2a6a877
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1695861.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+evalInWorker(`
+ verifyprebarriers();
+ Number + 1 >> 2;
+`)
diff --git a/js/src/jit-test/tests/gc/bug-1696880.js b/js/src/jit-test/tests/gc/bug-1696880.js
new file mode 100644
index 0000000000..cb4890503d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1696880.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+gczeal(0);
+gczeal(4);
+function a(b) {
+ c = cacheEntry(b);
+ evaluate(c, {
+ saveIncrementalBytecode: true
+ });
+ return c;
+}
+offThreadDecodeStencil(a(""));
diff --git a/js/src/jit-test/tests/gc/bug-1696886.js b/js/src/jit-test/tests/gc/bug-1696886.js
new file mode 100644
index 0000000000..76b97ab90a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1696886.js
@@ -0,0 +1,9 @@
+gczeal(0);
+try { evaluate(`
+ for (let v3 = 0; v3 < 256; v3++)
+ x1 = this.__defineSetter__("x", function(z69) { })
+ const v9 = {};
+ const v10 = v9[gczeal(4)];
+`); } catch(exc) {}
+for (let v3 = 0; v3 < 256; v3++)
+ x1 = this.__defineSetter__("x", function(z69) {})
diff --git a/js/src/jit-test/tests/gc/bug-1698543.js b/js/src/jit-test/tests/gc/bug-1698543.js
new file mode 100644
index 0000000000..865607d12b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1698543.js
@@ -0,0 +1,34 @@
+// |jit-test| allow-overrecursed; skip-if: !getJitCompilerOptions()['blinterp.enable']
+
+foo = "";
+
+doit(`
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ function u() { broken(
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ // XXXXXXXXXXXXXXXXXXXXXXXXXXX
+`);
+
+gczeal(4);
+
+doit("");
+
+unescape(foo);
+
+function doit(x) {
+ try {
+ evaluate(x);
+ } catch (e) {
+ if (e instanceof SyntaxError)
+ doit(x);
+ }
+ try {
+ x = x.replace(/!/g, "");
+ } catch (e) {}
+ foo += x + " ";
+}
diff --git a/js/src/jit-test/tests/gc/bug-1699364.js b/js/src/jit-test/tests/gc/bug-1699364.js
new file mode 100644
index 0000000000..20542b7123
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1699364.js
@@ -0,0 +1,6 @@
+// |jit-test| allow-overrecursed; skip-if: !getJitCompilerOptions()['blinterp.enable']
+
+gczeal(4);
+for (let i = 0; i < 1000; i++) {
+ "".padStart(10000).startsWith();
+}
diff --git a/js/src/jit-test/tests/gc/bug-1714530.js b/js/src/jit-test/tests/gc/bug-1714530.js
new file mode 100644
index 0000000000..ed8adcaf6e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1714530.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: typeof Intl === 'undefined'
+
+gczeal(0); // Prevent timeouts with certain gczeal values.
+
+for (i = 0; i < 10000; i++, bailAfter(10)) {
+ if (hasOwnProperty) {
+ Intl.DateTimeFormat(0, {}).format();
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1723840.js b/js/src/jit-test/tests/gc/bug-1723840.js
new file mode 100644
index 0000000000..95bd7fd2df
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1723840.js
@@ -0,0 +1,12 @@
+gczeal(0);
+gcstress = 1;
+Object.defineProperty(this, "", {
+ value: eval
+});
+gczeal(11);
+gczeal(6, 5);
+const a = { b: 7 };
+const c = [a, ""];
+const d = [c];
+new WeakMap(d);
+
diff --git a/js/src/jit-test/tests/gc/bug-1723841.js b/js/src/jit-test/tests/gc/bug-1723841.js
new file mode 100644
index 0000000000..97ceccd70b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1723841.js
@@ -0,0 +1,7 @@
+gczeal(0);
+m0 = new WeakMap;
+o = {};
+s = '';
+m0.set(o,s);
+verifyprebarriers();
+startgc('');
diff --git a/js/src/jit-test/tests/gc/bug-1736310.js b/js/src/jit-test/tests/gc/bug-1736310.js
new file mode 100644
index 0000000000..da0d83188d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1736310.js
@@ -0,0 +1,16 @@
+gczeal(9, 10);
+function a() {
+ var b = new Int32Array(buffer);
+ function c(d) {
+ b[5] = d;
+ }
+ return c;
+}
+b = new Int32Array(6);
+var buffer = b.buffer;
+a()({
+ valueOf() {
+ detachArrayBuffer(buffer);
+ }
+})
+
diff --git a/js/src/jit-test/tests/gc/bug-1739972.js b/js/src/jit-test/tests/gc/bug-1739972.js
new file mode 100644
index 0000000000..7094635c8d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1739972.js
@@ -0,0 +1,5 @@
+enableShellAllocationMetadataBuilder();
+a = newGlobal();
+a.evaluate("function x() {}");
+for (i = 0; i < 20; ++i)
+ new a.x;
diff --git a/js/src/jit-test/tests/gc/bug-1744979.js b/js/src/jit-test/tests/gc/bug-1744979.js
new file mode 100644
index 0000000000..ada98946f7
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1744979.js
@@ -0,0 +1,3 @@
+gczeal(14, 3);
+Array(4294967).sort();
+reportLargeAllocationFailure(1);
diff --git a/js/src/jit-test/tests/gc/bug-1749298.js b/js/src/jit-test/tests/gc/bug-1749298.js
new file mode 100644
index 0000000000..7fa184501f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1749298.js
@@ -0,0 +1,17 @@
+// Test that a finalization registry target that is reachable from its global
+// does not keep the global alive indefinitely.
+
+let global = newGlobal({newCompartment: true});
+global.eval(`
+ this.target = {}; // Target is reachable from global.
+ this.registry = new FinalizationRegistry(() => assertEq(0, 1));
+ registry.register(target, 'held');
+ this.finalizeObserver = makeFinalizeObserver();
+`);
+
+assertEq(finalizeCount(), 0);
+
+global = undefined;
+gc();
+
+assertEq(finalizeCount(), 1);
diff --git a/js/src/jit-test/tests/gc/bug-1755257.js b/js/src/jit-test/tests/gc/bug-1755257.js
new file mode 100644
index 0000000000..052d2cc427
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1755257.js
@@ -0,0 +1 @@
+new FinalizationRegistry(a => 1).register(newGlobal({newCompartment: true}))
diff --git a/js/src/jit-test/tests/gc/bug-1755874.js b/js/src/jit-test/tests/gc/bug-1755874.js
new file mode 100644
index 0000000000..b1a1cdfe31
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1755874.js
@@ -0,0 +1,4 @@
+fullcompartmentchecks(true);
+let a = new FinalizationRegistry(b => {});
+let c = newGlobal({newCompartment: true});
+a.register(c);
diff --git a/js/src/jit-test/tests/gc/bug-1756590.js b/js/src/jit-test/tests/gc/bug-1756590.js
new file mode 100644
index 0000000000..5a5cadc1d9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1756590.js
@@ -0,0 +1,19 @@
+// |jit-test| error: Error
+
+function asyncGC(...targets) {
+ var finalizationRegistry = new FinalizationRegistry(() => {});
+ for (let target of targets) {
+ finalizationRegistry.register(target, 'target');
+ }
+ return Promise.resolve('tick').then(() => asyncGCDeref()).then(() => {
+ finalizationRegistry.cleanupSome(name => { names.push(name); });
+ });
+}
+const root = newGlobal({newCompartment: true});
+const dbg = new Debugger();
+dbg.each = asyncGC;
+const wrappedRoot = dbg.each (root)
+gczeal(14,10);
+evaluate(`
+ var StructType = class {};
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1757573.js b/js/src/jit-test/tests/gc/bug-1757573.js
new file mode 100644
index 0000000000..9a83cd0755
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1757573.js
@@ -0,0 +1,7 @@
+new FinalizationRegistry(a => {});
+b = newGlobal({newCompartment: true});
+b.eval(`
+ c = {};
+ d = new WeakRef(c);
+`);
+nukeCCW(b.d);
diff --git a/js/src/jit-test/tests/gc/bug-1762771.js b/js/src/jit-test/tests/gc/bug-1762771.js
new file mode 100644
index 0000000000..6a81910ec2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1762771.js
@@ -0,0 +1,9 @@
+if (this.enqueueMark) {
+ gczeal(0);
+ enqueueMark('set-color-gray');
+ enqueueMark('set-color-black');
+ enqueueMark(newGlobal());
+ enqueueMark('set-color-gray');
+ newGlobal();
+ startgc();
+}
diff --git a/js/src/jit-test/tests/gc/bug-1766648-markQueue.js b/js/src/jit-test/tests/gc/bug-1766648-markQueue.js
new file mode 100644
index 0000000000..bd5cb693e2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1766648-markQueue.js
@@ -0,0 +1,12 @@
+if (this.enqueueMark) {
+ enqueueMark('set-color-gray');
+ enqueueMark('set-color-black');
+ enqueueMark(newGlobal());
+ enqueueMark('set-color-gray');
+ enqueueMark('set-color-black');
+ enqueueMark(newGlobal());
+ enqueueMark('set-color-gray');
+ enqueueMark('set-color-black');
+ enqueueMark(newGlobal());
+ gc();
+}
diff --git a/js/src/jit-test/tests/gc/bug-1766656.js b/js/src/jit-test/tests/gc/bug-1766656.js
new file mode 100644
index 0000000000..7096f6b274
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1766656.js
@@ -0,0 +1,17 @@
+// |jit-test| error: Error
+
+const thisGlobal = this;
+const otherGlobalSameCompartment = newGlobal({sameCompartmentAs: thisGlobal});
+const globals = [thisGlobal, otherGlobalSameCompartment, undefined];
+function testProperties(global, count) {
+ let {object: source, transplant} = transplantableObject();
+ for (let i9 = 0; i9 < count; i9++) {
+ source[(0) + i9] = i9;
+ }
+ transplant(global);
+}
+for (let global of globals) {
+ for (let count of [0, 10, 30]) {
+ testProperties(global, count);
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1768813.js b/js/src/jit-test/tests/gc/bug-1768813.js
new file mode 100644
index 0000000000..531322cc31
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1768813.js
@@ -0,0 +1,19 @@
+gczeal(0);
+
+function f() {
+ let global = newGlobal({newCompartment: true});
+ global.a = Debugger(newGlobal({newCompartment: true}));
+ global.evaluate("grayRoot()");
+}
+
+function g(i) {
+ const str = " ".padStart(10000, " ");
+ str.startsWith("1");
+ if (i > 0) {
+ g(i - 1);
+ }
+}
+
+f();
+gczeal(11,3);
+g(5000);
diff --git a/js/src/jit-test/tests/gc/bug-1770266.js b/js/src/jit-test/tests/gc/bug-1770266.js
new file mode 100644
index 0000000000..b417e09d2f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1770266.js
@@ -0,0 +1,2 @@
+new FinalizationRegistry(() => 0).register(newGlobal({newCompartment: true}));
+recomputeWrappers();
diff --git a/js/src/jit-test/tests/gc/bug-1779833.js b/js/src/jit-test/tests/gc/bug-1779833.js
new file mode 100644
index 0000000000..7b2f132046
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1779833.js
@@ -0,0 +1,20 @@
+// This failed when run under ASAN.
+
+let size = 6;
+
+let map = new Map();
+assertEq(isNurseryAllocated(map), true);
+for (let i = 0; i < size; i++) {
+ map.set(i, {});
+}
+
+for (let i = 0; i < size - 1; i++) {
+ map.delete(i);
+}
+
+for (let i = 0; i < size - 1; i++) {
+ map.set(i, {});
+}
+
+map = undefined;
+minorgc();
diff --git a/js/src/jit-test/tests/gc/bug-1787351.js b/js/src/jit-test/tests/gc/bug-1787351.js
new file mode 100644
index 0000000000..28542b8bf1
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1787351.js
@@ -0,0 +1 @@
+// |jit-test| --gc-zeal=15
diff --git a/js/src/jit-test/tests/gc/bug-1791363.js b/js/src/jit-test/tests/gc/bug-1791363.js
new file mode 100644
index 0000000000..52d25db895
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1791363.js
@@ -0,0 +1,13 @@
+// |jit-test| allow-unhandlable-oom; skip-if: (getBuildConfiguration()['android'] && getBuildConfiguration()['debug'])
+
+gczeal(0);
+if (!this.enqueueMark) {
+ quit();
+}
+
+enqueueMark('set-color-gray');
+enqueueMark(newGlobal());
+enqueueMark('set-color-black');
+enqueueMark(newGlobal());
+setMarkStackLimit(1);
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1791975.js b/js/src/jit-test/tests/gc/bug-1791975.js
new file mode 100644
index 0000000000..a194a92dd0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1791975.js
@@ -0,0 +1,13 @@
+// |jit-test| skip-if: !('oomAtAllocation' in this)
+
+gczeal(10, 10);
+try {
+ throw 0;
+} catch {
+ for (let i = 1; i < 20 ; i++) {
+ oomAtAllocation(i);
+ try {
+ newGlobal();
+ } catch {}
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1792338.js b/js/src/jit-test/tests/gc/bug-1792338.js
new file mode 100644
index 0000000000..724f41b0fd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1792338.js
@@ -0,0 +1,14 @@
+// |jit-test| allow-unhandlable-oom; skip-if: (getBuildConfiguration()['android'] && getBuildConfiguration()['debug'])
+
+gczeal(0);
+if (!this.enqueueMark) {
+ quit();
+}
+
+enqueueMark('set-color-gray');
+enqueueMark(newGlobal({newCompartment: true}));
+enqueueMark('set-color-black');
+enqueueMark({});
+setMarkStackLimit(1);
+gc();
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1796901.js b/js/src/jit-test/tests/gc/bug-1796901.js
new file mode 100644
index 0000000000..081b5b8e6e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1796901.js
@@ -0,0 +1,4 @@
+// |jit-test| --no-threads
+gcslice(0);
+evalcx("lazy");
+abortgc();
diff --git a/js/src/jit-test/tests/gc/bug-1799678.js b/js/src/jit-test/tests/gc/bug-1799678.js
new file mode 100644
index 0000000000..a1423355ad
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1799678.js
@@ -0,0 +1,21 @@
+gczeal(0);
+setMarkStackLimit(1);
+loadFile(`
+ function wasmEvalText(str, imports) {
+ let binary = wasmTextToBinary(str);
+ m = new WebAssembly.Module(binary);
+ return new WebAssembly.Instance(m, imports);
+ }
+ let WasmFuncrefValues = [
+ wasmEvalText(\`(module (func (export "")))\`).exports[''],
+ ];
+ g1 = newGlobal({newCompartment: true});
+ gczeal(10,10);
+`);
+for (let i = 0; i < 1000; ++i)
+ loadFile("}");
+function loadFile(lfVarx) {
+ try {
+ evaluate(lfVarx);
+ } catch (lfVare) {}
+}
diff --git a/js/src/jit-test/tests/gc/bug-1802308.js b/js/src/jit-test/tests/gc/bug-1802308.js
new file mode 100644
index 0000000000..91e482dc4b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1802308.js
@@ -0,0 +1,8 @@
+function test() {
+ for (var i = 0; i < 10; i++)
+ /0|[1-9][0-9]*/.test("");
+};
+test();
+test();
+gczeal(4);
+enableShellAllocationMetadataBuilder();
diff --git a/js/src/jit-test/tests/gc/bug-1802478.js b/js/src/jit-test/tests/gc/bug-1802478.js
new file mode 100644
index 0000000000..05559c3f6f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1802478.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !('oomAfterAllocations' in this)
+
+enableTrackAllocations();
+for (a of "x") {
+ gczeal(2, 1);
+}
+oomAfterAllocations(1);
+try {
+ newString();
+} catch (x) {}
diff --git a/js/src/jit-test/tests/gc/bug-1803233.js b/js/src/jit-test/tests/gc/bug-1803233.js
new file mode 100644
index 0000000000..14e37f0065
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1803233.js
@@ -0,0 +1,5 @@
+// |jit-test| allow-oom; allow-unhandlable-oom
+
+gczeal(10, 1);
+gcparam("maxBytes", gcparam("gcBytes"));
+newGlobal();
diff --git a/js/src/jit-test/tests/gc/bug-1804629-2.js b/js/src/jit-test/tests/gc/bug-1804629-2.js
new file mode 100644
index 0000000000..fc7c75573b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1804629-2.js
@@ -0,0 +1,5 @@
+gczeal(0);
+enableShellAllocationMetadataBuilder();
+setMarkStackLimit(1);
+grayRoot()[1] = {};
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1804629.js b/js/src/jit-test/tests/gc/bug-1804629.js
new file mode 100644
index 0000000000..6ed62eec8c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1804629.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('gczeal' in this); error: ReferenceError
+
+gczeal(0);
+setMarkStackLimit(1);
+gczeal(4);
+a;
diff --git a/js/src/jit-test/tests/gc/bug-1804637.js b/js/src/jit-test/tests/gc/bug-1804637.js
new file mode 100644
index 0000000000..1f136b1965
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1804637.js
@@ -0,0 +1,8 @@
+// |jit-test| error: ReferenceError
+
+gczeal(0);
+enqueueMark('set-color-gray');
+enqueueMark({});
+gczeal(9);
+gczeal(11, 2);
+a;
diff --git a/js/src/jit-test/tests/gc/bug-1806976.js b/js/src/jit-test/tests/gc/bug-1806976.js
new file mode 100644
index 0000000000..9b3dab46cd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1806976.js
@@ -0,0 +1,5 @@
+gczeal(4);
+evaluate(`
+ for (x = 1; x < 2; ++x)
+ gcparam("maxHelperThreads", 1)
+`);
diff --git a/js/src/jit-test/tests/gc/bug-1817598.js b/js/src/jit-test/tests/gc/bug-1817598.js
new file mode 100644
index 0000000000..b13c3a74cc
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1817598.js
@@ -0,0 +1,3 @@
+const v2 = this.blackRoot();
+v2[1000n] = 1000n;
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1820543.js b/js/src/jit-test/tests/gc/bug-1820543.js
new file mode 100644
index 0000000000..0ef5f9915e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1820543.js
@@ -0,0 +1,16 @@
+gczeal(0);
+
+const v1 = ("DEB1").startsWith("DEB1");
+function f2(a3, a4, a5, a6) {
+ return ({"constructor":this,"b":a3,"__proto__":this}).newGlobal(f2);
+}
+f2.newCompartment = v1;
+with (f2()) {
+ function f11(a12, a13) {
+ return "DEB1";
+ }
+ const v15 = new FinalizationRegistry(f11);
+ v15.register(f2);
+}
+this.reportLargeAllocationFailure();
+gc()
diff --git a/js/src/jit-test/tests/gc/bug-1822995.js b/js/src/jit-test/tests/gc/bug-1822995.js
new file mode 100644
index 0000000000..5822b3a5ac
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1822995.js
@@ -0,0 +1,19 @@
+function f0(a1, a2, a3) {
+ try {
+ a2(a1, a2);
+ } catch(e5) {
+ const v7 = new Set();
+ const v8 = v7.add();
+ function f9(a10, a11) {
+ a11.sameZoneAs = v8;
+ return this;
+ }
+ f9(v8, f9).newGlobal(f9).blackRoot(a1);
+ }
+ return a2;
+}
+const v17 = this.wrapWithProto(f0, this);
+const v19 = Proxy.revocable(f0, v17);
+v19.proxy(v17, v17);
+v19.proxy(v19, v17);
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1825671.js b/js/src/jit-test/tests/gc/bug-1825671.js
new file mode 100644
index 0000000000..b8ab1b1cbe
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1825671.js
@@ -0,0 +1,12 @@
+let newTarget = Object.defineProperty(function () {}.bind(), "prototype", {
+ get() {
+ throw 0;
+ }
+});
+
+try {
+ Reflect.construct(FinalizationRegistry, [ 1 ], newTarget);
+} catch (n) {
+ assertEq(n === 0, false);
+ assertEq(n instanceof TypeError, true);
+}
diff --git a/js/src/jit-test/tests/gc/bug-1825936.js b/js/src/jit-test/tests/gc/bug-1825936.js
new file mode 100644
index 0000000000..6d87cc4667
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1825936.js
@@ -0,0 +1,24 @@
+let src = `
+function f0() {
+ return f0;
+}
+const v10 = f0.bind();
+v10.sameZoneAs = f0;
+const v37 = this.newGlobal(v10);
+
+try {
+ v37.moduleEvaluate();
+} catch(e48) {
+ this.grayRoot();
+ const v59 = new FinalizationRegistry(FinalizationRegistry);
+ v59.register(e48, v59, e48);
+ v59.register(f0, v59, e48);
+}
+this.nukeAllCCWs();
+`;
+
+gczeal(0);
+let global = newGlobal();
+global.eval(src);
+global = undefined;
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-1828396.js b/js/src/jit-test/tests/gc/bug-1828396.js
new file mode 100644
index 0000000000..465377f027
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1828396.js
@@ -0,0 +1,16 @@
+a = `
+ fullcompartmentchecks(true);
+ oomTest(Debugger)
+`.split('\n')
+c = "";
+while (true) {
+ d = a.shift()
+ if (d == null) break;
+ c += d
+ e(c);
+ function e(f) {
+ try {
+ evaluate(f);
+ } catch {}
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-1830921.js b/js/src/jit-test/tests/gc/bug-1830921.js
new file mode 100644
index 0000000000..fa4e8aee0f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1830921.js
@@ -0,0 +1,15 @@
+gczeal(4);
+x = [0, 0, 0, 0, 0];
+for (let i = 0; i < 5; ++i) {
+ for (let j = 0; j < x[i]; ++j) {}
+}
+gczeal(0);
+let y = [];
+for (let k = 0; k < 9999; ++k) {
+ try {
+ throw z;
+ } catch (e) {
+ y.push("" + e);
+ }
+}
+print(y);
diff --git a/js/src/jit-test/tests/gc/bug-1852729.js b/js/src/jit-test/tests/gc/bug-1852729.js
new file mode 100644
index 0000000000..460fe6b774
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1852729.js
@@ -0,0 +1,5 @@
+const g = newGlobal({newCompartment: true});
+const domObj = this.transplantableObject().object;
+const bar = new g.WeakRef(domObj);
+bar.deref();
+this.nukeAllCCWs();
diff --git a/js/src/jit-test/tests/gc/bug-787703.js b/js/src/jit-test/tests/gc/bug-787703.js
new file mode 100644
index 0000000000..bbc01bae73
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-787703.js
@@ -0,0 +1,7 @@
+// |jit-test| slow;
+
+eval(" function x() {}" + Array(241).join(" "));
+for (var i = 0; i < 100; i++) {
+ gczeal(4, 2);
+ String(x);
+}
diff --git a/js/src/jit-test/tests/gc/bug-820186.js b/js/src/jit-test/tests/gc/bug-820186.js
new file mode 100644
index 0000000000..cced86e208
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-820186.js
@@ -0,0 +1,297 @@
+// |jit-test| slow;
+
+function randomRecursion() {
+ var y = ""
+ if (rnd(2)) {
+ var x = 2;
+ "{" + x + "}";
+ randomRecursion();
+ randomRecursion();
+ return [""];
+ }
+ return [""];
+}
+
+function thisFunctionIsNeverCalled() {
+}
+
+function testOne() {
+ ox = newGlobal();
+ var code = randomRecursion()[rnd(3)];
+}
+
+initRnd();
+gczeal(10, 3);
+
+for (var count = 0; count < 20; count++) {
+ print(count);
+ testOne()
+}
+
+// ==========================================================================================
+
+// this program is a JavaScript version of Mersenne Twister, with concealment and encapsulation in class,
+// an almost straight conversion from the original program, mt19937ar.c,
+// translated by y. okada on July 17, 2006.
+// Changes by Jesse Ruderman: added "var" keyword in a few spots; added export_mta etc; pasted into fuzz.js.
+// in this program, procedure descriptions and comments of original source code were not removed.
+// lines commented with //c// were originally descriptions of c procedure. and a few following lines are appropriate JavaScript descriptions.
+// lines commented with /* and */ are original comments.
+// lines commented with // are additional comments in this JavaScript version.
+// before using this version, create at least one instance of MersenneTwister19937 class, and initialize the each state, given below in c comments, of all the instances.
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+function MersenneTwister19937()
+{
+ /* Period parameters */
+ //c//#define N 624
+ //c//#define M 397
+ //c//#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+ //c//#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+ //c//#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+ var N = 624;
+ var M = 397;
+ var MATRIX_A = 0x9908b0df; /* constant vector a */
+ var UPPER_MASK = 0x80000000; /* most significant w-r bits */
+ var LOWER_MASK = 0x7fffffff; /* least significant r bits */
+ //c//static unsigned long mt[N]; /* the array for the state vector */
+ //c//static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+ var mt = new Array(N); /* the array for the state vector */
+ var mti = N+1; /* mti==N+1 means mt[N] is not initialized */
+
+ function unsigned32 (n1) // returns a 32-bits unsiged integer from an operand to which applied a bit operator.
+ {
+ return n1 < 0 ? (n1 ^ UPPER_MASK) + UPPER_MASK : n1;
+ }
+
+ function subtraction32 (n1, n2) // emulates lowerflow of a c 32-bits unsiged integer variable, instead of the operator -. these both arguments must be non-negative integers expressible using unsigned 32 bits.
+ {
+ return n1 < n2 ? unsigned32((0x100000000 - (n2 - n1)) & 0xffffffff) : n1 - n2;
+ }
+
+ function addition32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator +. these both arguments must be non-negative integers expressible using unsigned 32 bits.
+ {
+ return unsigned32((n1 + n2) & 0xffffffff)
+ }
+
+ function multiplication32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator *. these both arguments must be non-negative integers expressible using unsigned 32 bits.
+ {
+ var sum = 0;
+ for (var i = 0; i < 32; ++i){
+ if ((n1 >>> i) & 0x1){
+ sum = addition32(sum, unsigned32(n2 << i));
+ }
+ }
+ return sum;
+ }
+
+ /* initializes mt[N] with a seed */
+ //c//void init_genrand(unsigned long s)
+ this.init_genrand = function (s)
+ {
+ //c//mt[0]= s & 0xffffffff;
+ mt[0]= unsigned32(s & 0xffffffff);
+ for (mti=1; mti<N; mti++) {
+ mt[mti] =
+ //c//(1812433253 * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+ addition32(multiplication32(1812433253, unsigned32(mt[mti-1] ^ (mt[mti-1] >>> 30))), mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ //c//mt[mti] &= 0xffffffff;
+ mt[mti] = unsigned32(mt[mti] & 0xffffffff);
+ /* for >32 bit machines */
+ }
+ }
+
+ /* initialize by an array with array-length */
+ /* init_key is the array for initializing keys */
+ /* key_length is its length */
+ /* slight change for C++, 2004/2/26 */
+ //c//void init_by_array(unsigned long init_key[], int key_length)
+ this.init_by_array = function (init_key, key_length)
+ {
+ //c//int i, j, k;
+ var i, j, k;
+ //c//init_genrand(19650218);
+ this.init_genrand(19650218);
+ i=1; j=0;
+ k = (N>key_length ? N : key_length);
+ for (; k; k--) {
+ //c//mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525))
+ //c// + init_key[j] + j; /* non linear */
+ mt[i] = addition32(addition32(unsigned32(mt[i] ^ multiplication32(unsigned32(mt[i-1] ^ (mt[i-1] >>> 30)), 1664525)), init_key[j]), j);
+ mt[i] =
+ //c//mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */
+ unsigned32(mt[i] & 0xffffffff);
+ i++; j++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ if (j>=key_length) j=0;
+ }
+ for (k=N-1; k; k--) {
+ //c//mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941))
+ //c//- i; /* non linear */
+ mt[i] = subtraction32(unsigned32((dbg=mt[i]) ^ multiplication32(unsigned32(mt[i-1] ^ (mt[i-1] >>> 30)), 1566083941)), i);
+ //c//mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */
+ mt[i] = unsigned32(mt[i] & 0xffffffff);
+ i++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ }
+ mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */
+ }
+
+ this.export_state = function() { return [mt, mti]; };
+ this.import_state = function(s) { mt = s[0]; mti = s[1]; };
+ this.export_mta = function() { return mt; };
+ this.import_mta = function(_mta) { mt = _mta };
+ this.export_mti = function() { return mti; };
+ this.import_mti = function(_mti) { mti = _mti; }
+
+ /* generates a random number on [0,0xffffffff]-interval */
+ //c//unsigned long genrand_int32(void)
+ this.genrand_int32 = function ()
+ {
+ //c//unsigned long y;
+ //c//static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ var y;
+ var mag01 = new Array(0x0, MATRIX_A);
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ //c//int kk;
+ var kk;
+
+ if (mti == N+1) /* if init_genrand() has not been called, */
+ //c//init_genrand(5489); /* a default initial seed is used */
+ this.init_genrand(5489); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ //c//y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ //c//mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
+ y = unsigned32((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK));
+ mt[kk] = unsigned32(mt[kk+M] ^ (y >>> 1) ^ mag01[y & 0x1]);
+ }
+ for (;kk<N-1;kk++) {
+ //c//y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ //c//mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
+ y = unsigned32((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK));
+ mt[kk] = unsigned32(mt[kk+(M-N)] ^ (y >>> 1) ^ mag01[y & 0x1]);
+ }
+ //c//y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ //c//mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
+ y = unsigned32((mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK));
+ mt[N-1] = unsigned32(mt[M-1] ^ (y >>> 1) ^ mag01[y & 0x1]);
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ //c//y ^= (y >> 11);
+ //c//y ^= (y << 7) & 0x9d2c5680;
+ //c//y ^= (y << 15) & 0xefc60000;
+ //c//y ^= (y >> 18);
+ y = unsigned32(y ^ (y >>> 11));
+ y = unsigned32(y ^ ((y << 7) & 0x9d2c5680));
+ y = unsigned32(y ^ ((y << 15) & 0xefc60000));
+ y = unsigned32(y ^ (y >>> 18));
+
+ return y;
+ }
+
+ /* generates a random number on [0,0x7fffffff]-interval */
+ //c//long genrand_int31(void)
+ this.genrand_int31 = function ()
+ {
+ //c//return (genrand_int32()>>1);
+ return (this.genrand_int32()>>>1);
+ }
+
+ /* generates a random number on [0,1]-real-interval */
+ //c//double genrand_real1(void)
+ this.genrand_real1 = function ()
+ {
+ //c//return genrand_int32()*(1.0/4294967295.0);
+ return this.genrand_int32()*(1.0/4294967295.0);
+ /* divided by 2^32-1 */
+ }
+
+ /* generates a random number on [0,1)-real-interval */
+ //c//double genrand_real2(void)
+ this.genrand_real2 = function ()
+ {
+ //c//return genrand_int32()*(1.0/4294967296.0);
+ return this.genrand_int32()*(1.0/4294967296.0);
+ /* divided by 2^32 */
+ }
+
+ /* generates a random number on (0,1)-real-interval */
+ //c//double genrand_real3(void)
+ this.genrand_real3 = function ()
+ {
+ //c//return ((genrand_int32()) + 0.5)*(1.0/4294967296.0);
+ return ((this.genrand_int32()) + 0.5)*(1.0/4294967296.0);
+ /* divided by 2^32 */
+ }
+
+ /* generates a random number on [0,1) with 53-bit resolution*/
+ //c//double genrand_res53(void)
+ this.genrand_res53 = function ()
+ {
+ //c//unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+ var a=this.genrand_int32()>>>5, b=this.genrand_int32()>>>6;
+ return(a*67108864.0+b)*(1.0/9007199254740992.0);
+ }
+ /* These real versions are due to Isaku Wada, 2002/01/09 added */
+}
+
+function initRnd() {
+ var fuzzMT = new MersenneTwister19937;
+ var fuzzSeed = 53;
+ fuzzMT.init_genrand(fuzzSeed);
+ rnd = function (n) { var v = Math.floor(fuzzMT.genrand_real2() * n); return v; };
+ rnd.rndReal = function() { return fuzzMT.genrand_real2(); };
+ rnd.fuzzMT = fuzzMT;
+}
diff --git a/js/src/jit-test/tests/gc/bug-821551.js b/js/src/jit-test/tests/gc/bug-821551.js
new file mode 100644
index 0000000000..8d13799bfe
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-821551.js
@@ -0,0 +1,16 @@
+function g() {
+ z = newGlobal('');
+ return function(code) {
+ evalcx(code, z)
+ }
+}
+f = g();
+f("\
+ options('strict_mode');\
+ for (var x = 0; x < 1; ++x) {\
+ a = x;\
+ }\
+ options('strict_mode');\
+");
+f("a in eval");
+
diff --git a/js/src/jit-test/tests/gc/bug-824321.js b/js/src/jit-test/tests/gc/bug-824321.js
new file mode 100644
index 0000000000..424e2db0ce
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-824321.js
@@ -0,0 +1,3 @@
+x = "\udada\udada";
+gc();
+
diff --git a/js/src/jit-test/tests/gc/bug-825326.js b/js/src/jit-test/tests/gc/bug-825326.js
new file mode 100644
index 0000000000..1579f1d291
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-825326.js
@@ -0,0 +1,18 @@
+// |jit-test|
+
+try {
+ a = []
+ r = /x/
+ gczeal(10, 2)()
+} catch (e) {}
+try {
+ (function() {
+ r(function() {
+ eval()
+ })
+ })()
+} catch (e) {}
+try {
+ s
+} catch (e) {}
+a.every(function() {})
diff --git a/js/src/jit-test/tests/gc/bug-832103.js b/js/src/jit-test/tests/gc/bug-832103.js
new file mode 100644
index 0000000000..9ada62e40f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-832103.js
@@ -0,0 +1,27 @@
+//|jit-test| error:TypeError
+RegExp("").exec()
+Object.defineProperty(this, "x", {
+ get: function() {
+ return new Array
+ }
+})
+Object.defineProperty(this, "y", {
+ get: function() {
+ return [function() {}, 0, 0, 0, 0, 0, 0]
+ }
+})
+r = RegExp("");
+String(undefined)
+with({
+ b: gczeal(9, 2)
+});
+r = /()/;
+y.sort(function(j) {
+ if (j) {
+ a =
+ new
+ Array
+ } else {
+ x.v()
+ }
+})
diff --git a/js/src/jit-test/tests/gc/bug-880816.js b/js/src/jit-test/tests/gc/bug-880816.js
new file mode 100644
index 0000000000..7d7e9e622d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-880816.js
@@ -0,0 +1,30 @@
+var lfcode = new Array();
+lfcode.push("const baz = 'bar';");
+lfcode.push("2");
+lfcode.push("{ function foo() {} }");
+lfcode.push("evaluate('\
+var INVALIDATE_MODES = INVALIDATE_MODE_STRINGS.map(s => ({mode: s}));\
+function range(n, m) {}\
+function seq_scan(array, f) {}\
+function assertStructuralEq(e1, e2) {}\
+for (var i = 0, l = a.length; i < l; i++) {}\
+');");
+lfcode.push("for (var x of new Set(Object.getOwnPropertyNames(this))) {}");
+var lfRunTypeId = -1;
+while (true) {
+ var file = lfcode.shift(); if (file == undefined) { break; }
+ loadFile(file)
+}
+function loadFile(lfVarx) {
+ try {
+ if (lfVarx.substr(-3) == ".js") {}
+ if (!isNaN(lfVarx)) {
+ lfRunTypeId = parseInt(lfVarx);
+ } else {
+ switch (lfRunTypeId) {
+ case 2: new Function(lfVarx)(); break;
+ default: evaluate(lfVarx); break;
+ }
+ }
+ } catch (lfVare) {}
+}
diff --git a/js/src/jit-test/tests/gc/bug-880886.js b/js/src/jit-test/tests/gc/bug-880886.js
new file mode 100644
index 0000000000..2f83a2c637
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-880886.js
@@ -0,0 +1,10 @@
+// |jit-test| error: too much recursion
+
+function testUniqueness(asmJSModule) {
+ var f = asmJSModule();
+}
+function lambda() {
+ var x = function inner() { "use asm"; function g() {} return g };
+ return lambda();
+}
+testUniqueness(lambda);
diff --git a/js/src/jit-test/tests/gc/bug-886551-1.js b/js/src/jit-test/tests/gc/bug-886551-1.js
new file mode 100644
index 0000000000..b3f2bcd45f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-886551-1.js
@@ -0,0 +1,8 @@
+if (this.hasOwnProperty('Intl')) {
+ gc();
+ gcslice(1);
+ var thisValues = [ "x" ];
+ thisValues.forEach(function (value) {
+ var format = Intl.DateTimeFormat.call(value);
+ });
+}
diff --git a/js/src/jit-test/tests/gc/bug-886551-2.js b/js/src/jit-test/tests/gc/bug-886551-2.js
new file mode 100644
index 0000000000..9381c867d5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-886551-2.js
@@ -0,0 +1,7 @@
+gc();
+gcslice(1);
+function isClone(a, b) {
+ var rmemory = new WeakMap();
+ rmemory.set(a,b);
+}
+isClone([]);
diff --git a/js/src/jit-test/tests/gc/bug-886560.js b/js/src/jit-test/tests/gc/bug-886560.js
new file mode 100644
index 0000000000..c506b05d04
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-886560.js
@@ -0,0 +1,11 @@
+// |jit-test| error: x is not defined
+
+// enableShellAllocationMetadataBuilder ignores its argument, because we don't
+// permit metadata callbacks to run JS any more, so this test may be
+// unnecessary. We'll preserve its structure just in case.
+enableShellAllocationMetadataBuilder(function(obj) {
+ var res = {};
+ return res;
+ });
+gczeal(4);
+x();
diff --git a/js/src/jit-test/tests/gc/bug-886630.js b/js/src/jit-test/tests/gc/bug-886630.js
new file mode 100644
index 0000000000..d794cfac17
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-886630.js
@@ -0,0 +1,109 @@
+function errorToString(e) {
+ try {} catch (e2) {}
+}
+Object.getOwnPropertyNames(this);
+if (false) {
+ for (let x of constructors)
+ print(x);
+}
+var tryRunning = tryRunningDirectly;
+function unlikelyToHang(code) {
+ var codeL = code.replace(/\s/g, " ");
+ return true && code.indexOf("infloop") == -1 && !(codeL.match(/const.*for/)) // can be an infinite loop: function() { const x = 1; for(x in ({a1:1})) dumpln(3); }
+ && !(codeL.match(/for.*const/)) // can be an infinite loop: for (x in ...); const x;
+ && !(codeL.match(/for.*in.*uneval/)) // can be slow to loop through the huge string uneval(this), for example
+ && !(codeL.match(/for.*for.*for/)) // nested for loops (including for..in, etc) can take a while
+ && !(codeL.match(/for.*for.*gc/))
+}
+function whatToTestSpidermonkeyTrunk(code) {
+ var codeL = code.replace(/\s/g, " ");
+ return {
+ allowParse: true,
+ allowExec: unlikelyToHang(code),
+ allowIter: true,
+ expectConsistentOutput: true && code.indexOf("Date") == -1 // time marches on
+ && code.indexOf("random") == -1 && code.indexOf("dumpObject") == -1 // shows heap addresses
+ && code.indexOf("oomAfterAllocations") == -1 && code.indexOf("ParallelArray") == -1,
+ expectConsistentOutputAcrossIter: true && code.indexOf("options") == -1 // options() is per-cx, and the js shell doesn't create a new cx for each sandbox/compartment
+ ,
+ expectConsistentOutputAcrossJITs: true && code.indexOf("'strict") == -1 // bug 743425
+ && code.indexOf("preventExtensions") == -1 // bug 887521
+ && !(codeL.match(/\/.*[\u0000\u0080-\uffff]/)) // doesn't stay valid utf-8 after going through python (?)
+ };
+}
+function tryRunningDirectly(f, code, wtt) {
+ try {
+ eval(code);
+ } catch (e) {}
+ try {
+ var rv = f();
+ tryIteration(rv);
+ } catch (runError) {
+ var err = errorToString(runError);
+ }
+ tryEnsureSanity();
+}
+var realEval = eval;
+var realMath = Math;
+var realFunction = Function;
+var realGC = gc;
+function tryEnsureSanity() {
+ try {
+ delete this.Math;
+ delete this.Function;
+ delete this.gc;
+ this.Math = realMath;
+ this.eval = realEval;
+ this.Function = realFunction;
+ this.gc = realGC;
+ } catch (e) {}
+}
+function tryIteration(rv) {
+ try {
+ var iterCount = 0;
+ for /* each */
+ ( /* let */ iterValue in rv)
+ print("Iterating succeeded, iterCount == " + iterCount);
+ } catch (iterError) {}
+}
+function failsToCompileInTry(code) {
+ try {
+ new Function(" try { " + code + " } catch(e) { }");
+ } catch (e) {}
+}
+function tryItOut(code) {
+ if (count % 1000 == 0) {
+ gc();
+ }
+ var wtt = whatToTestSpidermonkeyTrunk(code);
+ code = code.replace(/\/\*DUPTRY\d+\*\//, function(k) {
+ var n = parseInt(k.substr(8), 10);
+ print(n);
+ return strTimes("try{}catch(e){}", n);
+ })
+ try {
+ f = new Function(code);
+ } catch (compileError) {}
+ if (code.indexOf("\n") == -1 && code.indexOf("\r") == -1 && code.indexOf("\f") == -1 && code.indexOf("\0") == -1 && code.indexOf("\u2028") == -1 && code.indexOf("\u2029") == -1 && code.indexOf("<--") == -1 && code.indexOf("-->") == -1 && code.indexOf("//") == -1) {
+ var nCode = code;
+ if (nCode.indexOf("return") != -1 || nCode.indexOf("yield") != -1 || nCode.indexOf("const") != -1 || failsToCompileInTry(nCode)) nCode = "(function(){" + nCode + "})()"
+ }
+ tryRunning(f, code, false);
+}
+var count = 0;
+tryItOut("");
+count = 2
+tryItOut("");
+tryItOut("");
+tryItOut("o")
+tryItOut("")
+tryItOut("")
+tryItOut("\
+ with((/ /-7))\
+ {\
+ for(let mjcpxc=0;mjcpxc<9;++mjcpxc)\
+ {\
+ e=mjcpxc;\
+ yield/x/\
+ }}")
+
diff --git a/js/src/jit-test/tests/gc/bug-889682-1.js b/js/src/jit-test/tests/gc/bug-889682-1.js
new file mode 100644
index 0000000000..75b3f4089b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-889682-1.js
@@ -0,0 +1,13 @@
+// |jit-test| error:TypeError
+gc();
+var recursiveFunctions = [{
+ text: "(function(){if(a){}g()})"
+}];
+(function testAllRecursiveFunctions() {
+ for (var i = 0; i < recursiveFunctions.length; ++i) {
+ var a = recursiveFunctions[i];
+ eval(a.text.replace(/@/g, ""))
+ }
+})();
+gcslice(2869);
+Function("v={c:[{x:[[]],N:{x:[{}[d]]}}]}=minorgc(true)")()
diff --git a/js/src/jit-test/tests/gc/bug-889682-2.js b/js/src/jit-test/tests/gc/bug-889682-2.js
new file mode 100644
index 0000000000..efc47337a2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-889682-2.js
@@ -0,0 +1,14 @@
+// |jit-test| error:TypeError
+(function(){})
+gc();
+var recursiveFunctions = [{
+ text: "(function(){if(a){}g()})"
+}];
+(function testAllRecursiveFunctions() {
+ for (var i = 0; i < recursiveFunctions.length; ++i) {
+ var a = recursiveFunctions[i];
+ eval(a.text.replace(/@/g, ""))
+ }
+})();
+gcslice(2869);
+Function("v={c:[{x:[[]],N:{x:[{}[d]]}}]}=minorgc(true)")()
diff --git a/js/src/jit-test/tests/gc/bug-889682-3.js b/js/src/jit-test/tests/gc/bug-889682-3.js
new file mode 100644
index 0000000000..601c8300a4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-889682-3.js
@@ -0,0 +1,14 @@
+// |jit-test| error:TypeError
+function f(){}
+gc();
+var recursiveFunctions = [{
+ text: "(function(){if(a){}g()})"
+}];
+(function testAllRecursiveFunctions() {
+ for (var i = 0; i < recursiveFunctions.length; ++i) {
+ var a = recursiveFunctions[i];
+ eval(a.text.replace(/@/g, ""))
+ }
+})();
+gcslice(2869);
+Function("v={c:[{x:[[]],N:{x:[{}[d]]}}]}=minorgc(true)")()
diff --git a/js/src/jit-test/tests/gc/bug-891773.js b/js/src/jit-test/tests/gc/bug-891773.js
new file mode 100644
index 0000000000..c5cb237782
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-891773.js
@@ -0,0 +1,14 @@
+x = newGlobal()
+Int32Array = x.Int32Array
+x.p = new ArrayBuffer()
+schedulegc(29);
+(function(stdlib, n, heap) {
+ "use asm"
+ var Int32ArrayView = new stdlib.Int32Array(heap)
+ function f() {
+ Int32ArrayView[1]
+ }
+ return f
+})(this, {
+ f: new Function
+}, new ArrayBuffer())
diff --git a/js/src/jit-test/tests/gc/bug-906236.js b/js/src/jit-test/tests/gc/bug-906236.js
new file mode 100644
index 0000000000..7566bda428
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-906236.js
@@ -0,0 +1,9 @@
+// |jit-test| error: too much recursion
+(function() {
+ (function f(x) {
+ return x * f(x - 1);
+ with({})
+ var r = ""
+ })()
+})()
+
diff --git a/js/src/jit-test/tests/gc/bug-906241.js b/js/src/jit-test/tests/gc/bug-906241.js
new file mode 100644
index 0000000000..ccbc73e594
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-906241.js
@@ -0,0 +1,9 @@
+// |jit-test| error: InternalError: too much recursion
+for (let y in []);
+(function f(x) {
+ new Float64Array(new ArrayBuffer());
+ {
+ f(x)
+ function t() {}
+ }
+})();
diff --git a/js/src/jit-test/tests/gc/bug-912813.js b/js/src/jit-test/tests/gc/bug-912813.js
new file mode 100644
index 0000000000..1babaac28e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-912813.js
@@ -0,0 +1,7 @@
+// |jit-test| slow
+gczeal(9, 1)
+for (var a = 0; a < 1; a++) {
+ newGlobal({
+ sameZoneAs: {}
+ })
+}
diff --git a/js/src/jit-test/tests/gc/bug-913224.js b/js/src/jit-test/tests/gc/bug-913224.js
new file mode 100644
index 0000000000..815164d764
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-913224.js
@@ -0,0 +1 @@
+dumpHeap();
diff --git a/js/src/jit-test/tests/gc/bug-913715.js b/js/src/jit-test/tests/gc/bug-913715.js
new file mode 100644
index 0000000000..3a05cf5881
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-913715.js
@@ -0,0 +1,31 @@
+try {
+ (function() {
+ Object.defineProperty(this, "x", {
+ get: function() {
+ Object.defineProperty(this, "y", {
+ configurable: true,
+ get: function() {
+ return Proxy(this.y)
+ }
+ });
+ x;
+ }
+ })
+ })()
+ x
+} catch (e) {}
+try {
+ x
+} catch (e) {}
+try {
+ x
+} catch (e) {}
+try {
+ y
+} catch (e) {}
+try {
+ y
+} catch (e) {}
+try {
+ y
+} catch (e) {}
diff --git a/js/src/jit-test/tests/gc/bug-919536.js b/js/src/jit-test/tests/gc/bug-919536.js
new file mode 100644
index 0000000000..0c07b4b598
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-919536.js
@@ -0,0 +1,17 @@
+if ("gczeal" in this) {
+ gczeal(2, 1000);
+
+ var a = new Array(10 * 1000);
+
+ var i = a.length;
+ while (i-- != 0) {
+ switch (i % 3) {
+ case 0:
+ a[i] = { };
+ break;
+ }
+ }
+
+ gc();
+}
+
diff --git a/js/src/jit-test/tests/gc/bug-924690.js b/js/src/jit-test/tests/gc/bug-924690.js
new file mode 100644
index 0000000000..e7fdc981ba
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-924690.js
@@ -0,0 +1,25 @@
+x = []
+try {
+ (function() {
+ schedulegc(1);
+ ((function() {
+ return {
+ y: function() {
+ u() = []
+ }
+ }
+ })())
+ })()
+ watch.call(x, "valueOf", function() {})
+ gc()
+} catch (e) { print(e); }
+try {
+ (function() {
+ x.valueOf =
+ (function() {
+ y();
+ })
+ })()
+ x + 2
+ print('foo')
+} catch (e) { print(e); }
diff --git a/js/src/jit-test/tests/gc/bug-935022.js b/js/src/jit-test/tests/gc/bug-935022.js
new file mode 100644
index 0000000000..2ebe148911
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-935022.js
@@ -0,0 +1,4 @@
+function callback(obj) {}
+enableShellAllocationMetadataBuilder();
+gczeal(7);
+var statusitems = [];
diff --git a/js/src/jit-test/tests/gc/bug-939499.js b/js/src/jit-test/tests/gc/bug-939499.js
new file mode 100644
index 0000000000..a54d78cab9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-939499.js
@@ -0,0 +1,4 @@
+gczeal(0);
+gc();
+verifyprebarriers();
+setMarkStackLimit(5);
diff --git a/js/src/jit-test/tests/gc/bug-945275.js b/js/src/jit-test/tests/gc/bug-945275.js
new file mode 100644
index 0000000000..26cdb23d3c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-945275.js
@@ -0,0 +1,11 @@
+function TestCase(n) {
+ this.name = undefined;
+ this.description = undefined;
+}
+gczeal(7,1);
+eval("\
+function reportCompare() { return new TestCase; };\
+reportCompare();\
+Object.defineProperty(Object.prototype, 'name', {});\
+reportCompare();\
+");
diff --git a/js/src/jit-test/tests/gc/bug-945280.js b/js/src/jit-test/tests/gc/bug-945280.js
new file mode 100644
index 0000000000..3546c864d9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-945280.js
@@ -0,0 +1,4 @@
+gczeal(7,1);
+enableShellAllocationMetadataBuilder();
+gczeal(false);
+var statusitems = [];
diff --git a/js/src/jit-test/tests/gc/bug-945285.js b/js/src/jit-test/tests/gc/bug-945285.js
new file mode 100644
index 0000000000..7544d86c04
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-945285.js
@@ -0,0 +1,3 @@
+gczeal(11);
+function callback(obj) {}
+enableShellAllocationMetadataBuilder();
diff --git a/js/src/jit-test/tests/gc/bug-950927.js b/js/src/jit-test/tests/gc/bug-950927.js
new file mode 100644
index 0000000000..23096c483b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-950927.js
@@ -0,0 +1,31 @@
+var lfcode = new Array();
+lfcode.push("\
+var optionNames = options().split(',');\
+ for (var i = 0; i < optionNames.length; i++) {}\
+");
+lfcode.push("gczeal(7,5);");
+lfcode.push("4");
+lfcode.push("\
+var S = new Array();\
+var x = 1;\
+for ( var i = 8; i >= 0; i-- ) {\
+ S[0] += ' ';\
+ S[0] += ',';\
+}\
+eval(S);\
+");
+var lfRunTypeId = -1;
+while (true) {
+ var file = lfcode.shift(); if (file == undefined) { break; }
+ loadFile(file)
+}
+function loadFile(lfVarx) {
+ if (lfVarx.substr(-3) != ".js" && lfVarx.length != 1) {
+ switch (lfRunTypeId) {
+ case 4: eval("(function() { " + lfVarx + " })();"); break;
+ default: evaluate(lfVarx, { noScriptRval : true }); break;
+ }
+ } else if (!isNaN(lfVarx)) {
+ lfRunTypeId = parseInt(lfVarx);
+ }
+}
diff --git a/js/src/jit-test/tests/gc/bug-952819.js b/js/src/jit-test/tests/gc/bug-952819.js
new file mode 100644
index 0000000000..3b118a2dc3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-952819.js
@@ -0,0 +1,3 @@
+verifypostbarriers()
+verifyprebarriers()
+verifypostbarriers()
diff --git a/js/src/jit-test/tests/gc/bug-956324.js b/js/src/jit-test/tests/gc/bug-956324.js
new file mode 100644
index 0000000000..711fff9516
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-956324.js
@@ -0,0 +1,28 @@
+var g = newGlobal({newCompartment: true});
+g.eval("function f() {\n" +
+ " debugger;\n" +
+ "}\n")
+
+var dbg = new Debugger(g);
+var handler = {};
+dbg.onDebuggerStatement = function (frame) {
+ frame.script.setBreakpoint(0, {});
+};
+
+// create breakpoint
+g.f()
+
+// drop our references to things
+handler = undefined;
+dbg.onDebuggerStatement = undefined;
+
+dbg.removeAllDebuggees();
+
+gc();
+
+//create garbage to trigger a minor GC
+var x;
+for (var i = 0; i < 100; ++i)
+ x = {};
+
+gc();
diff --git a/js/src/jit-test/tests/gc/bug-957110.js b/js/src/jit-test/tests/gc/bug-957110.js
new file mode 100644
index 0000000000..372e15915f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-957110.js
@@ -0,0 +1,7 @@
+// |jit-test| allow-unhandlable-oom
+gczeal(7,1);
+try {
+gcparam("maxBytes", gcparam("gcBytes") + 4*1024);
+newGlobal("same-compartment");
+} catch(exc1) {}
+gczeal(1);
diff --git a/js/src/jit-test/tests/gc/bug-957114.js b/js/src/jit-test/tests/gc/bug-957114.js
new file mode 100644
index 0000000000..5c61c8210b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-957114.js
@@ -0,0 +1,13 @@
+gczeal(7,1);
+function TestCase(n) {
+ this.name = '';
+ this.description = '';
+ this.expect = '';
+ this.actual = '';
+ this.reason = '';
+ this.passed = '';
+}
+function test() { return new TestCase; }
+test();
+Object.defineProperty(Object.prototype, "name", {});
+test();
diff --git a/js/src/jit-test/tests/gc/bug-961741.js b/js/src/jit-test/tests/gc/bug-961741.js
new file mode 100644
index 0000000000..c4c7823636
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-961741.js
@@ -0,0 +1,5 @@
+function r() {
+ for (var x in undefined) {}
+}
+enableShellAllocationMetadataBuilder();
+r();
diff --git a/js/src/jit-test/tests/gc/bug-961877.js b/js/src/jit-test/tests/gc/bug-961877.js
new file mode 100644
index 0000000000..37b146feaf
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-961877.js
@@ -0,0 +1,14 @@
+g = Function("", "for (var i = 0; i < 0; ++i) { eval('this.arg'+0 +'=arg'+0); }");
+Math.abs(undefined);
+gczeal(2,300);
+evaluate("\
+var toFloat32 = (function() {\
+ var f32 = new Float32Array(1);\
+ function f(x) { return f32[0] = x; }\
+ return f;\
+})();\
+for (var i = 0; i < 64; ++i) {\
+ var p = Math.pow(2, i) + 1;\
+ g(toFloat32(p));\
+ toFloat32(-p);\
+}");
diff --git a/js/src/jit-test/tests/gc/bug-969012.js b/js/src/jit-test/tests/gc/bug-969012.js
new file mode 100644
index 0000000000..ed03f66a73
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-969012.js
@@ -0,0 +1,60 @@
+function testClosureCreationAndInvocation() {
+ var a = 'foobar';
+ function makeaddv(vvvv) {
+ var z = -4 * vvvv;
+ var y = -3 * vvvv;
+ var x = -2 * vvvv;
+ var w = -1 * vvvv;
+ var v = 0 * vvvv;
+ var u = 1 * vvvv;
+ var t = 2 * vvvv;
+ var s = 3 * vvvv;
+ var r = 4 * vvvv;
+ var q = 5 * vvvv;
+ var p = 6 * vvvv;
+ var o = 7 * vvvv;
+ var n = 8 * vvvv;
+ var m = 9 * vvvv;
+ var l = 10 * vvvv;
+ var k = 11 * vvvv;
+ var j = 12 * vvvv;
+ var i = 13 * vvvv;
+ var h = 14 * vvvv;
+ var g = 15 * vvvv;
+ var f = 16 * vvvv;
+ var e = 17 * vvvv;
+ var d = 18 * vvvv;
+ var c = 19 * vvvv;
+ var b = 20 * vvvv;
+ var a = 21 * vvvv;
+ return function (x) {
+ switch (x) {
+ case 0: return a; case 1: return b;
+ case 2: return c; case 3: return d;
+ case 4: return e; case 5: return f;
+ case 6: return g; case 7: return h;
+ case 8: return i; case 9: return j;
+ case 10: return k; case 11: return l;
+ case 12: return m; case 13: return n;
+ case 14: return o; case 15: return p;
+ case 16: return q; case 17: return r;
+ case 18: return s; case 19: return t;
+ case 20: return u; case 21: return v;
+ case 22: return w; case 23: return x;
+ case 24: return y; case 25: return z;
+ }
+ };
+ }
+ var a = Array();
+ for (var i = 0; i < 26; ++i) {
+ a.push(makeaddv(Math.random()));
+ }
+ return a;
+}
+
+var a = testClosureCreationAndInvocation();
+for (var i = 0; i < 26; ++i) {
+ print(a[i](i));
+}
+
+
diff --git a/js/src/jit-test/tests/gc/bug-978353.js b/js/src/jit-test/tests/gc/bug-978353.js
new file mode 100644
index 0000000000..44635209fe
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-978353.js
@@ -0,0 +1,6 @@
+var arr = new Float64Array(2);
+function test(m) {
+ arr[1] = m;
+}
+for(var i=0; i<20000; ++i, Array('x'))
+ test(0);
diff --git a/js/src/jit-test/tests/gc/bug-978802.js b/js/src/jit-test/tests/gc/bug-978802.js
new file mode 100644
index 0000000000..1e13b76e0e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-978802.js
@@ -0,0 +1,17 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => {
+ try {
+ var max = 400;
+ function f(b) {
+ if (b) {
+ f(b - 1);
+ } else {
+ g = {};
+ }
+ g.apply(null, arguments);
+ }
+ f(max - 1);
+ } catch(exc0) {}
+ f();
+});
diff --git a/js/src/jit-test/tests/gc/bug-981289.js b/js/src/jit-test/tests/gc/bug-981289.js
new file mode 100644
index 0000000000..edba3fc974
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-981289.js
@@ -0,0 +1,8 @@
+gcPreserveCode();
+function test() {
+ for (var i=0; i<20; i++) {
+ arguments.x = {};
+ gc();
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/gc/bug-981295.js b/js/src/jit-test/tests/gc/bug-981295.js
new file mode 100644
index 0000000000..0f570bf73d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-981295.js
@@ -0,0 +1,9 @@
+var NotEarlyErrorString = "NotEarlyError";
+var NotEarlyError = new Error(NotEarlyErrorString);
+var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0);
+for (var i = 0; i < function(x) { return myObj(Date.prototype.toString.apply(x)); }; void i) {
+ eval(a.text.replace(/@/g, ""))
+}
+gcslice(2601);
+function testcase() {}
+new Uint16Array(testcase);
diff --git a/js/src/jit-test/tests/gc/bug-985732.js b/js/src/jit-test/tests/gc/bug-985732.js
new file mode 100644
index 0000000000..eb560f1878
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-985732.js
@@ -0,0 +1,84 @@
+// |jit-test| error: expected is not defined
+function testx() {
+function compareArray(aExpected, aActual) {}
+ for (var i = 0; i < expected.length; i++) {}
+var supportsArrayIndexGettersOnArrays = undefined;
+function fnSupportsArrayIndexGettersOnArrays() {}
+var supportsArrayIndexGettersOnObjects = undefined;
+function fnSupportsArrayIndexGettersOnObjects() {}
+function ConvertToFileUrl(pathStr) {
+}
+function fnExists() {}
+var __globalObject = Function("return this;")();
+function fnGlobalObject() {}
+function fnSupportsStrict() {
+ eval('with ({}) {}');
+}
+function dataPropertyAttributesAreCorrect(obj, configurable) {}
+function accessorPropertyAttributesAreCorrect(obj, configurable) {}
+var NotEarlyErrorString = "NotEarlyError";
+var EarlyErrorRePat = "^((?!" + NotEarlyErrorString + ").)*$";
+var NotEarlyError = new Error(NotEarlyErrorString);
+function Test262Error(message) {};
+function testFailed(message) {}
+function testPrint(message) {}
+function $PRINT(message) {}
+function $INCLUDE(message) { }
+function $ERROR(message) {}
+function $FAIL(message) {}
+function getPrecision(num) {}
+var prec;
+function isEqual(num1, num2) {}
+function ToInteger(p) {}
+var HoursPerDay = 24;
+var MinutesPerHour = 60;
+var SecondsPerMinute = 60;
+var msPerDay = 86400000;
+var msPerSecond = 1000;
+var msPerMinute = 60000;
+var msPerHour = 3600000;
+var date_1899_end = -2208988800001;
+var date_1900_start = -2208988800000;
+var date_1969_end = -1;
+var date_1970_start = 0;
+var date_1999_end = 946684799999;
+var date_2000_start = 946684800000;
+var date_2099_end = 4102444799999;
+var date_2100_start = 4102444800000;
+var $LocalTZ,
+ $DST_start_month,
+ $DST_start_sunday,
+ $DST_start_hour,
+ $DST_start_minutes,
+ $DST_end_month,
+ $DST_end_sunday,
+ $DST_end_hour,
+ $DST_end_minutes;
+function Day(t) {}
+function TimeWithinDay(t) {}
+function DaysInYear(y){}
+function DayFromYear(y) {}
+function TimeFromYear(y){}
+function YearFromTime(t) {}
+function InLeapYear(t){}
+function DayWithinYear(t) {}
+function MonthFromTime(t){}
+function DateFromTime(t) {}
+function WeekDay(t) {}
+var LocalTZA = $LocalTZ*msPerHour;
+function DaysInMonth(m, leap) {}
+function GetSundayInMonth(t, m, count){}
+function DaylightSavingTA(t) {}
+function LocalTime(t){}
+function UTC(t) {}
+function HourFromTime(t){}
+function MinFromTime(t){}
+function SecFromTime(t){}
+function msFromTime(t){}
+function MakeTime(hour, min, sec, ms){}
+function MakeDay(year, month, date) {}
+function MakeDate( day, time ) {}
+function TimeClip(time) {}
+function ConstructDate(year, month, date, hours, minutes, seconds, ms){}
+function runTestCase(testcase) {}
+} testx();
diff --git a/js/src/jit-test/tests/gc/bug-993768.js b/js/src/jit-test/tests/gc/bug-993768.js
new file mode 100644
index 0000000000..2fc05fb5b8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-993768.js
@@ -0,0 +1,13 @@
+var SECTION = "";
+gcPreserveCode()
+gczeal(9, 1000);
+function test() {
+ var f32 = new Float32Array(10);
+ f32[0] = 5;
+ var i = 0;
+ for (var j = 0; j < 10000; ++j) {
+ f32[i + 1] = f32[i] - 1;
+ SECTION += 1;
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/gc/bug1116306.js b/js/src/jit-test/tests/gc/bug1116306.js
new file mode 100644
index 0000000000..5a9718f19c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1116306.js
@@ -0,0 +1,8 @@
+const dbg = new Debugger();
+const g = newGlobal({newCompartment: true});
+dbg.addDebuggee(g);
+dbg.memory.trackingAllocationSites = true;
+g.eval("this.alloc = {}");
+verifyprebarriers();
+schedulegc(3);
+dbg.memory.drainAllocationsLog();
diff --git a/js/src/jit-test/tests/gc/bug1146213.js b/js/src/jit-test/tests/gc/bug1146213.js
new file mode 100644
index 0000000000..7e4b246e07
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1146213.js
@@ -0,0 +1,10 @@
+// |jit-test| skip-if: !(getBuildConfiguration()['has-gczeal']) || helperThreadCount() === 0
+setGCCallback({
+ action: "majorGC",
+});
+schedulezone(this)
+gcslice(3)
+var lfGlobal = newGlobal();
+lfGlobal.offThreadCompileToStencil("");
+var stencil = lfGlobal.finishOffThreadStencil();
+lfGlobal.evalStencil(stencil);
diff --git a/js/src/jit-test/tests/gc/bug1191756.js b/js/src/jit-test/tests/gc/bug1191756.js
new file mode 100644
index 0000000000..ebc82341e9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1191756.js
@@ -0,0 +1,18 @@
+// |jit-test| skip-if: typeof 'oomAtAllocation' === 'undefined'
+
+function fn(i) {
+ if (i == 3)
+ return ["isFinite"].map(function (i) {});
+ return [];
+}
+
+try {
+ fn(0);
+ fn(1);
+ fn(2);
+ oomAtAllocation(50);
+ fn(3);
+} catch(e) {
+ // Ignore oom
+}
+
diff --git a/js/src/jit-test/tests/gc/bug1246607.js b/js/src/jit-test/tests/gc/bug1246607.js
new file mode 100644
index 0000000000..1fbe9e5208
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1246607.js
@@ -0,0 +1,15 @@
+// |jit-test| skip-if: typeof oomTest !== 'function' || typeof Intl !== 'object'
+
+oomTest(() => {
+ try {
+ new Intl.DateTimeFormat;
+ x1 = 0;
+ } catch (e) {
+ switch (1) {
+ case 0:
+ let s;
+ case 1:
+ x;
+ }
+ }
+})
diff --git a/js/src/jit-test/tests/gc/bug1282113.js b/js/src/jit-test/tests/gc/bug1282113.js
new file mode 100644
index 0000000000..88d128a0ff
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1282113.js
@@ -0,0 +1,6 @@
+Object.getOwnPropertyNames(this);
+setGCCallback({
+ action: "majorGC",
+ phases: "begin"
+});
+selectforgc(this);
diff --git a/js/src/jit-test/tests/gc/bug1283169.js b/js/src/jit-test/tests/gc/bug1283169.js
new file mode 100644
index 0000000000..721293dbd4
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1283169.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+gczeal(0);
+startgc(45);
+offThreadCompileToStencil("print(1)");
diff --git a/js/src/jit-test/tests/gc/bug1285186.js b/js/src/jit-test/tests/gc/bug1285186.js
new file mode 100644
index 0000000000..f0974c6de9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1285186.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+gczeal(10);
+newGlobal();
+offThreadCompileToStencil("let x = 1;");
+abortgc();
diff --git a/js/src/jit-test/tests/gc/bug1285490.js b/js/src/jit-test/tests/gc/bug1285490.js
new file mode 100644
index 0000000000..7cd457d30c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1285490.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+gczeal(4);
+offThreadCompileToStencil("let x = 1;");
diff --git a/js/src/jit-test/tests/gc/bug1287063.js b/js/src/jit-test/tests/gc/bug1287063.js
new file mode 100644
index 0000000000..66c2f69390
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1287063.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+schedulezone("");
+offThreadCompileToStencil("");
diff --git a/js/src/jit-test/tests/gc/bug1326343-gcstats.js b/js/src/jit-test/tests/gc/bug1326343-gcstats.js
new file mode 100644
index 0000000000..f29306af4c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1326343-gcstats.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+setJitCompilerOption('baseline.warmup.trigger', 4);
+oomTest((function () {
+ gcslice(0);
+}))
diff --git a/js/src/jit-test/tests/gc/bug1335642.js b/js/src/jit-test/tests/gc/bug1335642.js
new file mode 100644
index 0000000000..6e7779ae04
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1335642.js
@@ -0,0 +1,6 @@
+var g = newGlobal();
+var b = g.eval(`
+var b = /foo2/;
+Object.defineProperty(b, "source", { get: () => {}});
+`);
+new RegExp(b).source;
diff --git a/js/src/jit-test/tests/gc/bug1335643.js b/js/src/jit-test/tests/gc/bug1335643.js
new file mode 100644
index 0000000000..5ee61b0603
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1335643.js
@@ -0,0 +1,5 @@
+low = high = newGlobal({})
+high.low = low
+high.eval("function a() { return saveStack(1, low) }")
+set = eval("high.a()")
+serialize(set)
diff --git a/js/src/jit-test/tests/gc/bug1336866.js b/js/src/jit-test/tests/gc/bug1336866.js
new file mode 100644
index 0000000000..5a665e55fd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1336866.js
@@ -0,0 +1 @@
+JSON.stringify(this);
diff --git a/js/src/jit-test/tests/gc/bug1337324.js b/js/src/jit-test/tests/gc/bug1337324.js
new file mode 100644
index 0000000000..eaf4c080f0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1337324.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('oomTest' in this)
+oomTest(function () {
+ offThreadCompileModuleToStencil('');
+ var stencil = finishOffThreadStencil();
+ instantiateModuleStencil(stencil);
+});
diff --git a/js/src/jit-test/tests/gc/bug1471949.js b/js/src/jit-test/tests/gc/bug1471949.js
new file mode 100644
index 0000000000..5f0f10f4df
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1471949.js
@@ -0,0 +1,5 @@
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
+
+gczeal(15);
+oomAfterAllocations(5);
+gcslice(11);
diff --git a/js/src/jit-test/tests/gc/bug1511412.js b/js/src/jit-test/tests/gc/bug1511412.js
new file mode 100644
index 0000000000..0a2d6893cd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1511412.js
@@ -0,0 +1,17 @@
+Object.defineProperty(this, "fuzzutils", {
+ value: {
+ orig_evaluate: evaluate,
+ evaluate: function(c, o) {
+ if (!o) {
+ o = {};
+ }
+ o.catchTermination = true;
+ return fuzzutils.orig_evaluate(c, o);
+ },
+ }
+ });
+ gczeal(21, 10);
+ fuzzutils.evaluate(`
+enableShellAllocationMetadataBuilder();
+function test() {}
+`);
diff --git a/js/src/jit-test/tests/gc/bug1532289.js b/js/src/jit-test/tests/gc/bug1532289.js
new file mode 100644
index 0000000000..131867c415
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1532289.js
@@ -0,0 +1,11 @@
+// |jit-test| --ion-warmup-threshold=0; --ion-offthread-compile=off; skip-if: !getJitCompilerOptions()['baseline.enable']
+
+// gczeal mode causes test to run too slowly with --no-baseline
+gczeal(4,40);
+
+var x;
+var y = false;
+
+function f(v) { x = v; while (y) {} }
+
+for (var z=1; z < 1e5; z++) { f(BigInt(z)); }
diff --git a/js/src/jit-test/tests/gc/bug1600017.js b/js/src/jit-test/tests/gc/bug1600017.js
new file mode 100644
index 0000000000..c7a748009f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1600017.js
@@ -0,0 +1,21 @@
+var registry = new FinalizationRegistry(x => {
+ if (target1 === null) {
+ return;
+ }
+
+ target1 = null;
+
+ gc();
+
+ print("targets:", [...x]); // consume
+});
+
+var target1 = {};
+registry.register(target1, "target1");
+
+var target2 = {};
+registry.register(target2, "target2");
+
+target2 = null;
+
+gc();
diff --git a/js/src/jit-test/tests/gc/bug1600488-1.js b/js/src/jit-test/tests/gc/bug1600488-1.js
new file mode 100644
index 0000000000..a0fb8e5ee5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1600488-1.js
@@ -0,0 +1,14 @@
+const token = {};
+let cleanedUpValue;
+const finalizationRegistry = new FinalizationRegistry(value => {
+ cleanedUpValue = value;
+});
+{
+ let object = {};
+ finalizationRegistry.register(object, token, token);
+ object = undefined;
+}
+gc();
+finalizationRegistry.cleanupSome();
+assertEq(cleanedUpValue, token);
+assertEq(finalizationRegistry.unregister(token), false);
diff --git a/js/src/jit-test/tests/gc/bug1600488-2.js b/js/src/jit-test/tests/gc/bug1600488-2.js
new file mode 100644
index 0000000000..c7de44f1a0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1600488-2.js
@@ -0,0 +1,14 @@
+const token = {};
+let iterated;
+const finalizationRegistry = new FinalizationRegistry(items => {
+ iterated = items.next().value;
+});
+{
+ let object = {};
+ finalizationRegistry.register(object, token, token);
+ object = undefined;
+}
+gc();
+assertEq(finalizationRegistry.unregister(token), true);
+finalizationRegistry.cleanupSome();
+assertEq(iterated, undefined);
diff --git a/js/src/jit-test/tests/gc/bug1698557.js b/js/src/jit-test/tests/gc/bug1698557.js
new file mode 100644
index 0000000000..2fa2302fe3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1698557.js
@@ -0,0 +1,6 @@
+fullcompartmentchecks(true);
+let g = newGlobal({sameZoneAs: this});
+for (let i = 0; i < 20; i++) {
+ g.Object.prototype.toString;
+}
+gc();
diff --git a/js/src/jit-test/tests/gc/bug1704451.js b/js/src/jit-test/tests/gc/bug1704451.js
new file mode 100644
index 0000000000..d4b4d14995
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1704451.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('gczeal' in this)
+
+enableShellAllocationMetadataBuilder();
+gczeal(9,1);
+var o86 = {x76: 1, y86: 2};
+var snapshot = createShapeSnapshot(o86);
diff --git a/js/src/jit-test/tests/gc/bug1709537.js b/js/src/jit-test/tests/gc/bug1709537.js
new file mode 100644
index 0000000000..77f3fd1d5c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1709537.js
@@ -0,0 +1,9 @@
+function f() {
+ var obj = [];
+ for (var count = 20000; count > 15900; count--) {
+ obj[count] = 2;
+ }
+ assertEq(Object.getOwnPropertyNames(obj).length, 4101);
+}
+gczeal(4);
+f();
diff --git a/js/src/jit-test/tests/gc/bug888463.js b/js/src/jit-test/tests/gc/bug888463.js
new file mode 100644
index 0000000000..cd7e170d28
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug888463.js
@@ -0,0 +1,66 @@
+var sjcl = {
+ hash: {},
+};
+sjcl.bitArray = {
+ concat: function (a, b) {
+ var c = a[a.length - 1],
+ d = sjcl.bitArray.getPartial(c);
+ return d === 32 ? a.concat(b) : sjcl.bitArray.P(b, d, c | 0, a.slice(0, a.length - 1))
+ },
+ getPartial: function (a) {
+ return Math.round(a / 0x10000000000) || 32
+ }
+};
+sjcl.hash.sha256 = function (a) {
+ this.a[0] || this.w();
+ this.reset()
+};
+sjcl.hash.sha256.prototype = {
+ reset: function () {
+ this.n = this.N.slice(0);
+ this.i = [];
+ },
+ update: function (a) {
+ var b, c = this.i = sjcl.bitArray.concat(this.i, a);
+ return this
+ },
+ finalize: function () {
+ var a, b = this.i,
+ c = this.n;
+ this.C(b.splice(0, 16));
+ return c
+ },
+ N: [],
+ a: [],
+ w: function () {
+ function a(e) {
+ return (e - Math.floor(e)) * 0x100000000 | 0
+ }
+ var b = 0,
+ c = 2,
+ d;
+ a: for (; b < 64; c++) {
+ if (b < 8)
+ this.N[b] = a(Math.pow(c, 0.5));
+ b++
+ }
+ },
+ C: function (a) {
+ var b, c, d = a.slice(0),
+ e = this.n,
+ h = e[1],
+ i = e[2],
+ k = e[3],
+ n = e[7];
+ for (a = 0; a < 64; a++) {
+ b = d[a + 1 & 15];
+ g = b + (h & i ^ k & (h ^ i)) + (h >>> 2 ^ h >>> 13 ^ h >>> 22 ^ h << 30 ^ h << 19 ^ h << 10) | 0
+ }
+ e[0] = e[0] + g | 0;
+ }
+};
+var ax1 = [-1862726214, -1544935945, -1650904951, -1523200565, 1783959997, -1422527763, -1915825893, 67249414];
+var ax2 = ax1;
+for (var aix = 0; aix < 200; aix++) ax1 = (new sjcl.hash.sha256(undefined)).update(ax1, undefined).finalize();
+eval("for (var aix = 0; aix < 200; aix++) ax2 = (new sjcl.hash.sha256(undefined)).update(ax2, undefined).finalize();" +
+ "assertEq(ax2.toString(), ax1.toString());");
diff --git a/js/src/jit-test/tests/gc/compartment-revived-gc.js b/js/src/jit-test/tests/gc/compartment-revived-gc.js
new file mode 100644
index 0000000000..881f4a622a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/compartment-revived-gc.js
@@ -0,0 +1,133 @@
+// Test 'compartment revived' GCs, where we do an extra GC if there are
+// compartments which we expected to die but were kept alive.
+
+// A global used as the destination for transplants.
+let transplantTargetGlobal = newGlobal();
+
+function didCompartmentRevivedGC() {
+ return performance.mozMemory.gc.lastStartReason === "COMPARTMENT_REVIVED";
+}
+
+function compartmentCount() {
+ let r = performance.mozMemory.gc.compartmentCount;
+ return r;
+}
+
+function startIncrementalGC() {
+ startgc(1);
+ while (gcstate() === "Prepare") {
+ gcslice(100, {dontStart: true});
+ }
+ assertEq(gcstate(), "Mark");
+}
+
+function finishIncrementalGC() {
+ while (gcstate() !== "NotActive") {
+ gcslice(100, {dontStart: true});
+ }
+ assertEq(gcstate(), "NotActive");
+}
+
+// Create a new compartment and global and return the global.
+function createCompartment() {
+ return newGlobal({newCompartment: true});
+}
+
+// Create a transplantable object and create a wrapper to it from a new
+// compartment. Return a function to transplant the target object.
+function createTransplantableWrapperTarget(wrapperGlobal) {
+ let {object: target, transplant} = transplantableObject();
+ wrapperGlobal.wrapper = target;
+ return transplant;
+}
+
+// Transplant an object to a new global by calling the transplant
+// function. This remaps all wrappers pointing to the target object,
+// potentially keeping dead compartments alive.
+function transplantTargetAndRemapWrappers(transplant) {
+ transplant(transplantTargetGlobal);
+}
+
+// Test no compartment revived GC triggered in normal cases.
+function testNormal() {
+ gc();
+ assertEq(didCompartmentRevivedGC(), false);
+
+ startIncrementalGC();
+ finishIncrementalGC();
+ assertEq(didCompartmentRevivedGC(), false);
+
+ let initialCount = compartmentCount();
+ createCompartment();
+ startIncrementalGC();
+ finishIncrementalGC();
+ assertEq(compartmentCount(), initialCount);
+}
+
+// Test compartment revived GC is triggered by wrapper remapping.
+function testCompartmentRevived1() {
+ let initialCount = compartmentCount();
+ let compartment = createCompartment();
+ let transplant = createTransplantableWrapperTarget(compartment);
+ compartment = null;
+
+ startIncrementalGC();
+ transplantTargetAndRemapWrappers(transplant);
+ finishIncrementalGC();
+
+ assertEq(didCompartmentRevivedGC(), true);
+ assertEq(compartmentCount(), initialCount);
+}
+
+// Test no compartment revived GC is triggered for compartments transitively
+// kept alive by black roots.
+function testCompartmentRevived2() {
+ let initialCount = compartmentCount();
+ let compartment = createCompartment();
+ let transplant = createTransplantableWrapperTarget(compartment);
+ let liveCompartment = createCompartment();
+ liveCompartment.wrapper = compartment;
+ compartment = null;
+
+ startIncrementalGC();
+ transplantTargetAndRemapWrappers(transplant);
+ finishIncrementalGC();
+
+ assertEq(didCompartmentRevivedGC(), false);
+ assertEq(compartmentCount(), initialCount + 2);
+
+ liveCompartment = null;
+ gc();
+
+ assertEq(compartmentCount(), initialCount);
+}
+
+// Test no compartment revived GC is triggered for compartments transitively
+// kept alive by gray roots.
+function testCompartmentRevived3() {
+ let initialCount = compartmentCount();
+ let compartment = createCompartment();
+ let transplant = createTransplantableWrapperTarget(compartment);
+ let liveCompartment = createCompartment();
+ liveCompartment.wrapper = compartment;
+ liveCompartment.eval('grayRoot()[0] = this');
+ liveCompartment = null;
+ gc();
+
+ startIncrementalGC();
+ transplantTargetAndRemapWrappers(transplant);
+ finishIncrementalGC();
+
+ assertEq(didCompartmentRevivedGC(), false);
+ assertEq(compartmentCount(), initialCount + 2);
+
+ // There's no easy way to clear gray roots for a compartment we don't have
+ // any reference to.
+}
+
+gczeal(0);
+
+testNormal();
+testCompartmentRevived1();
+testCompartmentRevived2();
+testCompartmentRevived3();
diff --git a/js/src/jit-test/tests/gc/dedupe-02.js b/js/src/jit-test/tests/gc/dedupe-02.js
new file mode 100644
index 0000000000..72b2f97f9c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/dedupe-02.js
@@ -0,0 +1,39 @@
+// AutoStableStringChars needs to prevent the owner of its chars from being
+// deduplicated, even if they are held by a different string.
+
+gczeal(0);
+
+function makeExtensibleStrFrom(str) {
+ var left = str.substr(0, str.length/2);
+ var right = str.substr(str.length/2, str.length);
+ var ropeStr = left + right;
+ return ensureLinearString(ropeStr);
+}
+
+// Make a string to deduplicate to.
+var original = makeExtensibleStrFrom('{ "phbbbbbbbbbbbbbbttt!!!!??": [1] }\n\n');
+
+// Construct D2 -> D1 -> base
+var D2 = makeExtensibleStrFrom('{ "phbbbbbbbbbbbbbbttt!!!!??": [1] }');
+var D1 = newRope(D2, '\n', {nursery: true});
+ensureLinearString(D1);
+var base = newRope(D1, '\n', {nursery: true});
+ensureLinearString(base);
+
+// Make an AutoStableStringChars(D2) and do a minor GC within it. (This will do
+// a major GC, but it'll start out with a minor GC.) `base` would get
+// deduplicated to `original`, if it weren't for AutoStableStringChars marking
+// all of D2, D1, and base non-deduplicatable.
+
+// The first time JSON.parse runs, it will create several (14 in my test) GC
+// things before getting to the point where it does an allocation while holding
+// the chars pointer. Get them out of the way now.
+JSON.parse(D2);
+
+// Cause a minor GC to happen during JSON.parse after AutoStableStringChars
+// gives up its pointer.
+schedulegc(1);
+JSON.parse(D2);
+
+// Access `D2` to verify that it is not using the deduplicated chars.
+print(D2);
diff --git a/js/src/jit-test/tests/gc/dedupe.js b/js/src/jit-test/tests/gc/dedupe.js
new file mode 100644
index 0000000000..2e2c578abf
--- /dev/null
+++ b/js/src/jit-test/tests/gc/dedupe.js
@@ -0,0 +1,44 @@
+function str(c) {
+ let s = c;
+ for (let i = 0; i < 30; i++)
+ s += c;
+ ensureLinearString(s);
+ return s;
+}
+
+function f() {
+ // Create some slots to write into.
+ const o = {owner: 'short1', s: 'short2'};
+
+ // Make a tenured rope.
+ const r1 = str("a") + str("b");
+ gc();
+
+ // Write the first instance of our duplicate string into one of the slots
+ // (`owner`). This will be scanned first, and entered into deDupSet when
+ // tenured.
+ o.owner = ensureLinearString(str("a") + str("b") + str("c"));
+
+ // Make another rope with identical contents, with a tenured subtree.
+ const r2 = r1 + str("c");
+
+ // Linearize the new rope, creating a new extensible string and a bunch of
+ // dependent strings replacing the rest of the rope nodes.
+ ensureLinearString(r2);
+
+ // Write the new rope into a slot, so that it will be scanned next during the
+ // minor GC during traceSlots().
+ o.s = r2;
+
+ // Do a nursery collection. o.owner will be tenured and inserted into
+ // deDupSet. Then o.s aka r2 will be tenured. If things work correctly, r2
+ // will be marked non-deduplicatable because it is the base of a tenured
+ // string r1. If not, it will be deduplicated to o.owner.
+ minorgc();
+
+ // Extract out that r1 child node. If its base was deduplicated, this will
+ // assert because its chars have been freed.
+ const s1 = r1.substr(0, 31);
+}
+
+f();
diff --git a/js/src/jit-test/tests/gc/dedupeTenuredBase.js b/js/src/jit-test/tests/gc/dedupeTenuredBase.js
new file mode 100644
index 0000000000..5dbb25df45
--- /dev/null
+++ b/js/src/jit-test/tests/gc/dedupeTenuredBase.js
@@ -0,0 +1,45 @@
+function str(c) {
+ let s = c;
+ for (let i = 0; i < 30; i++) {
+ s += c;
+ }
+ ensureLinearString(s);
+ return s;
+}
+
+function f() {
+ // Create some slots to write into.
+ const o = { owner: "short1", s: "short2" };
+
+ // Make a tenured rope.
+ const r1 = str("a") + str("b");
+ gc();
+
+ // Write the first instance of our duplicate string into one of the slots
+ // (`owner`). This will be scanned first, and entered into deDupSet when
+ // tenured.
+ o.owner = ensureLinearString(str("a") + str("b") + str("c"));
+
+ // Make another rope with identical contents, with a tenured subtree.
+ const r2 = r1 + str("c");
+
+ // Linearize the new rope, creating a new extensible string and a bunch of
+ // dependent strings replacing the rest of the rope nodes.
+ ensureLinearString(r2);
+
+ // Write the new rope into a slot, so that it will be scanned next during the
+ // minor GC during traceSlots().
+ o.s = r2;
+
+ // Do a nursery collection. o.owner will be tenured and inserted into
+ // deDupSet. Then o.s aka r2 will be tenured. If things work correctly, r2
+ // will be marked non-deduplicatable because it is the base of a tenured
+ // string r1. If not, it will be deduplicated to o.owner.
+ minorgc();
+
+ // Extract out that r1 child node. If its base was deduplicated, this will
+ // assert because its chars have been freed.
+ const s1 = r1.substr(0, 31);
+}
+
+f();
diff --git a/js/src/jit-test/tests/gc/deduplicateTenuringStrings.js b/js/src/jit-test/tests/gc/deduplicateTenuringStrings.js
new file mode 100644
index 0000000000..1b8259cc15
--- /dev/null
+++ b/js/src/jit-test/tests/gc/deduplicateTenuringStrings.js
@@ -0,0 +1,205 @@
+// |jit-test| skip-if: !('stringRepresentation' in this)
+
+// This is to test the correctness of the string deduplication algorithm during
+// the tenuring phase. Same strings below refer to the same character encoding
+// (either latin1 or twobyte) and the same characters.
+
+// Tests:
+// 1. Same strings with same flags and zones should be deduplicated for
+// all linear strings except atoms and external strings.
+// 2. Same strings, but from different zones should not be deduplicated.
+// 3. Same strings, but with different flags should not be deduplicated.
+
+// We require predictable GC timing to make sure the correct
+// strings are tenured together.
+gczeal(0);
+
+var helperCode = `
+function makeInlineStr(isLatin1) {
+ var s = isLatin1 ? "123456789*1" : "一二三";
+ return s + s;
+}
+
+// Generic linear strings are non-atom, non-extensible, non-inline
+// linear strings.
+// Generic linear strings can only have latin1 characters.
+function makeGenericLinearStr() {
+ return notes(() => 1);
+}
+
+function makeRopeStr(isLatin1) {
+ var left = isLatin1 ? "1" : "一";
+ var right = isLatin1 ? "123456789*123456789*123456" :
+ "一二三四五六七八九*一二三四五六七八";
+ return left + right;
+}
+
+function makeExtensibleStr(isLatin1) {
+ var r = makeRopeStr(isLatin1);
+ ensureLinearString(r);
+ return r;
+}
+
+function makeExtensibleStrFrom(str) {
+ var left = str.substr(0, str.length/2);
+ var right = str.substr(str.length/2, str.length);
+ var ropeStr = left + right;
+ return ensureLinearString(ropeStr);
+}
+
+function makeDependentStr(isLatin1) {
+ var e = makeExtensibleStr(isLatin1);
+ var r1 = e + "!";
+ var r2 = e + r1;
+ ensureLinearString(r2);
+ return r1;
+}
+
+function makeDependentStrFrom(str) {
+ var e = makeExtensibleStrFrom(str);
+ var r1 = e.substr(0, e.length/2) + e.substr(e.length/2, e.length);
+ var r2 = e + r1;
+ ensureLinearString(r2);
+ return r1;
+}
+
+function makeExternalStr(isLatin1) {
+ return isLatin1 ? newString("12345678", {external: true}) :
+ newString("一二三", {external: true});
+}
+
+function tenureStringsWithSameChars(str1, str2, isDeduplicatable) {
+ minorgc();
+ assertEq(stringRepresentation(str1) == stringRepresentation(str2),
+ isDeduplicatable);
+}
+
+function assertDiffStrRepAfterMinorGC(g1, g2) {
+ minorgc();
+ g1.eval(\`strRep = stringRepresentation(str);\`);
+ g2.eval(\`strRep = stringRepresentation(str);\`);
+ assertEq(g1.strRep == g2.strRep, false);
+}
+`;
+
+eval(helperCode);
+
+// test1:
+// Same strings with same flags and zones should be deduplicated for all linear
+// strings except atoms, external strings.
+function test1(isLatin1) {
+ const isDeduplicatable = true;
+
+ // Deduplicatable:
+ // --> Inline Strings
+ var str1 = makeInlineStr(isLatin1);
+ var str2 = makeInlineStr(isLatin1);
+ tenureStringsWithSameChars(str1, str2, isDeduplicatable);
+
+ // --> Extensible Strings
+ str1 = makeExtensibleStr(isLatin1);
+ str2 = makeExtensibleStr(isLatin1);
+ tenureStringsWithSameChars(str1, str2, isDeduplicatable);
+
+ // --> Dependent Strings
+ str1 = makeDependentStr(isLatin1);
+ str2 = makeDependentStr(isLatin1);
+ tenureStringsWithSameChars(str1, str2, isDeduplicatable);
+
+ // --> Generic Linear Strings
+ if (isLatin1) {
+ var str1 = makeGenericLinearStr();
+ var str2 = makeGenericLinearStr();
+ tenureStringsWithSameChars(str1, str2, isDeduplicatable);
+ }
+
+ // Non-Deduplicatable:
+ // --> Rope Strings
+ str1 = makeRopeStr(isLatin1);
+ str2 = makeRopeStr(isLatin1);
+ tenureStringsWithSameChars(str1, str2, !isDeduplicatable);
+
+ // --> Atom strings are deduplicated already but not through string
+ // deduplication during tenuring.
+
+ // --> External strings are not nursery allocated.
+}
+
+// test2:
+// Same strings, but from different zones should not be deduplicated.
+function test2(isLatin1) {
+ var g1 = newGlobal({ newCompartment: true });
+ var g2 = newGlobal({ newCompartment: true });
+
+ g1.eval(helperCode);
+ g2.eval(helperCode);
+
+ // --> Inline Strings
+ g1.eval(`var str = makeInlineStr(${isLatin1}); `);
+ g2.eval(`var str = makeInlineStr(${isLatin1}); `);
+ assertDiffStrRepAfterMinorGC(g1, g2);
+
+ // --> Extensible Strings
+ g1.eval(`str = makeExtensibleStr(${isLatin1}); `);
+ g2.eval(`str = makeExtensibleStr(${isLatin1}); `);
+ assertDiffStrRepAfterMinorGC(g1, g2);
+
+ // --> Dependent Strings
+ g1.eval(`str = makeDependentStr(${isLatin1}); `);
+ g2.eval(`str = makeDependentStr(${isLatin1}); `);
+ assertDiffStrRepAfterMinorGC(g1, g2);
+
+ // --> Generic Linear Strings
+ if (isLatin1) {
+ g1.eval(`str = makeGenericLinearStr();`);
+ g2.eval(`str = makeGenericLinearStr();`);
+ assertDiffStrRepAfterMinorGC(g1, g2);
+ }
+}
+
+// test3:
+// Same strings, but with different flags should not be deduplicated.
+function test3(isLatin1) {
+ const isDeduplicatable = true;
+
+ // --> Dependent String and Extensible String
+ var dependentStr = makeDependentStr(isLatin1);
+ var extensibleStr = makeExtensibleStrFrom(dependentStr);
+ tenureStringsWithSameChars(dependentStr, extensibleStr, !isDeduplicatable);
+
+ if (isLatin1) {
+ // --> Generic Linear String and Extensible String
+ var genericLinearStr = makeGenericLinearStr();
+ var extensibleStr = makeExtensibleStrFrom(genericLinearStr);
+ tenureStringsWithSameChars(
+ genericLinearStr,
+ extensibleStr,
+ !isDeduplicatable
+ );
+
+ // --> Generic Linear String and Dependent String
+ var dependentStr = makeDependentStrFrom(genericLinearStr);
+ tenureStringsWithSameChars(
+ dependentStr,
+ genericLinearStr,
+ !isDeduplicatable
+ );
+ }
+
+ // --> Inline strings are too short to have the same chars as the extensible
+ // strings, generic linear strings and dependent strings
+}
+
+function runTests() {
+ var charEncoding = { TWOBYTE: 0, LATIN1: 1 };
+
+ test1(charEncoding.TWOBYTE);
+ test2(charEncoding.TWOBYTE);
+ test3(charEncoding.TWOBYTE);
+
+ test1(charEncoding.LATIN1);
+ test2(charEncoding.LATIN1);
+ test3(charEncoding.LATIN1);
+}
+
+runTests();
diff --git a/js/src/jit-test/tests/gc/elements-post-write-barrier.js b/js/src/jit-test/tests/gc/elements-post-write-barrier.js
new file mode 100644
index 0000000000..04249eb727
--- /dev/null
+++ b/js/src/jit-test/tests/gc/elements-post-write-barrier.js
@@ -0,0 +1,27 @@
+gczeal(12);
+
+var length = 10000;
+var array = new Array(length);
+array.fill(null);
+
+// Promote the array to the tenured heap, if it isn't already there.
+minorgc();
+
+for (var i = 0; i < length; i++) {
+ // Exercise that barrier with some fresh nursery object references!
+ array[i] = {};
+}
+
+minorgc();
+
+for (var i = length; i > 0; i--) {
+ array[i - 1] = {};
+}
+
+minorgc();
+
+for (var i = 0; i < length; i++) {
+ array[Math.floor(Math.random() * length)] = {};
+}
+
+gc();
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-ccw.js b/js/src/jit-test/tests/gc/finalizationRegistry-ccw.js
new file mode 100644
index 0000000000..88c98519ca
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-ccw.js
@@ -0,0 +1,81 @@
+// Test combinations of arguments in different compartments.
+
+gczeal(0);
+
+let heldValues = [];
+
+function ccwToObject() {
+ return evalcx('({})', newGlobal({newCompartment: true}));
+}
+
+function newRegistry() {
+ return new FinalizationRegistry(value => {
+ heldValues.push(value);
+ });
+}
+
+function ccwToRegistry() {
+ let global = newGlobal({newCompartment: true});
+ global.heldValues = heldValues;
+ return global.eval(
+ `new FinalizationRegistry(value => heldValues.push(value))`);
+}
+
+function incrementalGC() {
+ startgc(1);
+ while (gcstate() !== "NotActive") {
+ gcslice(1000);
+ }
+}
+
+// Test the case when the registry remains live.
+for (let w of [false, true]) {
+ for (let x of [false, true]) {
+ for (let y of [false, true]) {
+ for (let z of [false, true]) {
+ let registry = w ? ccwToRegistry(w) : newRegistry();
+ let target = x ? ccwToObject() : {};
+ let heldValue = y ? ccwToObject() : {};
+ let token = z ? ccwToObject() : {};
+ registry.register(target, heldValue, token);
+ registry.unregister(token);
+ registry.register(target, heldValue, token);
+ target = undefined;
+ token = undefined;
+ heldValue = undefined;
+ incrementalGC();
+ heldValues.length = 0; // Clear, don't replace.
+ drainJobQueue();
+ assertEq(heldValues.length, 1);
+ }
+ }
+ }
+}
+
+// Test the case when registry has no more references.
+for (let w of [false, true]) {
+ for (let x of [false, true]) {
+ for (let y of [false, true]) {
+ for (let z of [false, true]) {
+ let registry = w ? ccwToRegistry(w) : newRegistry();
+ let target = x ? ccwToObject() : {};
+ let heldValue = y ? ccwToObject() : {};
+ let token = z ? ccwToObject() : {};
+ registry.register(target, heldValue, token);
+ registry.unregister(token);
+ registry.register(target, heldValue, token);
+ target = undefined;
+ token = undefined;
+ heldValue = undefined;
+ registry = undefined; // Remove last reference to registry.
+ incrementalGC();
+ heldValues.length = 0;
+ drainJobQueue();
+ // The cleanup callback may or may not be run depending on
+ // which order the zones are swept in, which itself depends on
+ // the arrangement of CCWs.
+ assertEq(heldValues.length <= 1, true);
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-cleanupSome-recursive.js b/js/src/jit-test/tests/gc/finalizationRegistry-cleanupSome-recursive.js
new file mode 100644
index 0000000000..3245c1cff3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-cleanupSome-recursive.js
@@ -0,0 +1,51 @@
+// Test trying to call cleanupSome recursively in callback.
+
+// 0: Initial state.
+// 1: Attempt recursive calls.
+// 2: After recursive calls.
+let state = 0;
+
+let registry = new FinalizationRegistry(x => {
+ if (state === 0) {
+ state = 1;
+ try {
+ registry.cleanupSome();
+ } catch (e) {
+ // Pass the test if any error was thrown.
+ return;
+ } finally {
+ state = 2;
+ }
+ throw new Error("expected stack overflow error");
+ }
+
+ if (state === 1) {
+ registry.cleanupSome();
+ }
+});
+
+// Attempt to find the maximum supported stack depth.
+var stackSize = 0;
+function findStackSize(i) {
+ try {
+ stackSize = i;
+ findStackSize(i + 1);
+ } catch (e) {
+ return;
+ }
+}
+findStackSize(0);
+
+// Multiply the calculated stack size by some factor just to be on the safe side.
+const exceedStackDepthLimit = stackSize * 5;
+
+let values = [];
+for (let i = 0; i < exceedStackDepthLimit; ++i) {
+ let v = {};
+ registry.register(v, i);
+ values.push(v);
+}
+values.length = 0;
+
+gc();
+drainJobQueue();
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-gray.js b/js/src/jit-test/tests/gc/finalizationRegistry-gray.js
new file mode 100644
index 0000000000..5411a1a9ad
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-gray.js
@@ -0,0 +1,10 @@
+// Test gray finalization registry is correctly barrired.
+target = {};
+registry = new FinalizationRegistry(value => undefined);
+registry.register(target, 1);
+grayRoot()[0] = registry;
+registry = undefined;
+gc(); // Registry is now marked gray.
+target = undefined;
+gc(); // Target dies, registry is queued.
+drainJobQueue();
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js
new file mode 100644
index 0000000000..753448a650
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js
@@ -0,0 +1,8 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+// Don't test prototype initialization etc.
+new FinalizationRegistry(x => 0);
+
+oomTest(() => {
+ new FinalizationRegistry(x => 0);
+});
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js
new file mode 100644
index 0000000000..9d9b2a7db8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomTest' in this)
+let registry = new FinalizationRegistry(x => 0);
+let token = {};
+oomTest(() => registry.register({}, 1, token));
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js
new file mode 100644
index 0000000000..d606ad8ba8
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: !('oomTest' in this)
+let registry = new FinalizationRegistry(x => 0);
+registry.register({}, 1, {});
+let token = {};
+oomTest(() => registry.unregister(token));
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js
new file mode 100644
index 0000000000..4b7ef66ba0
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: !('oomTest' in this)
+let registry = new FinalizationRegistry(x => 0);
+let target = {};
+let token = {};
+oomTest(() => registry.register(target, 1, token));
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-records-not-initialized.js b/js/src/jit-test/tests/gc/finalizationRegistry-records-not-initialized.js
new file mode 100644
index 0000000000..327988ddc7
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry-records-not-initialized.js
@@ -0,0 +1,6 @@
+enableShellAllocationMetadataBuilder();
+evaluate(`
+ var registry = new FinalizationRegistry(x => 0);
+ gczeal(9,3);
+ registry.register({}, 1, {});
+`);
diff --git a/js/src/jit-test/tests/gc/finalizationRegistry.js b/js/src/jit-test/tests/gc/finalizationRegistry.js
new file mode 100644
index 0000000000..0450df8457
--- /dev/null
+++ b/js/src/jit-test/tests/gc/finalizationRegistry.js
@@ -0,0 +1,247 @@
+function checkPropertyDescriptor(obj, property, writable, enumerable,
+ configurable) {
+ let desc = Object.getOwnPropertyDescriptor(obj, property);
+ assertEq(typeof desc, "object");
+ assertEq(desc.writable, writable);
+ assertEq(desc.enumerable, enumerable);
+ assertEq(desc.configurable, configurable);
+}
+
+function assertThrowsTypeError(thunk) {
+ let error;
+ try {
+ thunk();
+ } catch (e) {
+ error = e;
+ }
+ assertEq(error instanceof TypeError, true);
+}
+
+// 3.1 The FinalizationRegistry Constructor
+assertEq(typeof this.FinalizationRegistry, "function");
+
+// 3.1.1 FinalizationRegistry ( cleanupCallback )
+assertThrowsTypeError(() => new FinalizationRegistry());
+assertThrowsTypeError(() => new FinalizationRegistry(1));
+new FinalizationRegistry(x => 0);
+
+// 3.2 Properties of the FinalizationRegistry Constructor
+assertEq(Object.getPrototypeOf(FinalizationRegistry), Function.prototype);
+
+// 3.2.1 FinalizationRegistry.prototype
+checkPropertyDescriptor(FinalizationRegistry, 'prototype', false, false, false);
+
+// 3.3 Properties of the FinalizationRegistry Prototype Object
+let proto = FinalizationRegistry.prototype;
+assertEq(Object.getPrototypeOf(proto), Object.prototype);
+
+// 3.3.1 FinalizationRegistry.prototype.constructor
+assertEq(proto.constructor, FinalizationRegistry);
+
+// 3.3.2 FinalizationRegistry.prototype.register ( target , holdings [, unregisterToken ] )
+assertEq(proto.hasOwnProperty('register'), true);
+assertEq(typeof proto.register, 'function');
+
+// 3.3.3 FinalizationRegistry.prototype.unregister ( unregisterToken )
+assertEq(proto.hasOwnProperty('unregister'), true);
+assertEq(typeof proto.unregister, 'function');
+
+// 3.3.4 FinalizationRegistry.prototype.cleanupSome ( [ callback ] )
+assertEq(proto.hasOwnProperty('cleanupSome'), true);
+assertEq(typeof proto.cleanupSome, 'function');
+
+// 3.3.5 FinalizationRegistry.prototype [ @@toStringTag ]
+assertEq(proto[Symbol.toStringTag], "FinalizationRegistry");
+checkPropertyDescriptor(proto, Symbol.toStringTag, false, false, true);
+
+// 3.4 Properties of FinalizationRegistry Instances
+let registry = new FinalizationRegistry(x => 0);
+assertEq(Object.getPrototypeOf(registry), proto);
+assertEq(Object.getOwnPropertyNames(registry).length, 0);
+
+let heldValues = [];
+registry = new FinalizationRegistry(value => {
+ heldValues.push(value);
+});
+
+// Test a single target.
+heldValues = [];
+registry.register({}, 42);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 1);
+assertEq(heldValues[0], 42);
+
+// Test multiple targets.
+heldValues = [];
+for (let i = 0; i < 100; i++) {
+ registry.register({}, i);
+}
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 100);
+heldValues = heldValues.sort((a, b) => a - b);
+for (let i = 0; i < 100; i++) {
+ assertEq(heldValues[i], i);
+}
+
+// Test a single object in multiple registries
+heldValues = [];
+let heldValues2 = [];
+let registry2 = new FinalizationRegistry(value => {
+ heldValues2.push(value);
+});
+{
+ let object = {};
+ registry.register(object, 1);
+ registry2.register(object, 2);
+ object = null;
+}
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 1);
+assertEq(heldValues[0], 1);
+assertEq(heldValues2.length, 1);
+assertEq(heldValues2[0], 2);
+
+// Unregister a single target.
+heldValues = [];
+let token = {};
+registry.register({}, 1, token);
+registry.unregister(token);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 0);
+
+// Unregister multiple targets.
+heldValues = [];
+let token2 = {};
+registry.register({}, 1, token);
+registry.register({}, 2, token2);
+registry.register({}, 3, token);
+registry.register({}, 4, token2);
+registry.unregister(token);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 2);
+heldValues = heldValues.sort((a, b) => a - b);
+assertEq(heldValues[0], 2);
+assertEq(heldValues[1], 4);
+
+// Watch object in another global.
+let other = newGlobal({newCompartment: true});
+heldValues = [];
+registry.register(evalcx('({})', other), 1);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 1);
+assertEq(heldValues[0], 1);
+
+// Pass heldValues from another global.
+let heldValue = evalcx('{}', other);
+heldValues = [];
+registry.register({}, heldValue);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 1);
+assertEq(heldValues[0], heldValue);
+
+// Pass unregister token from another global.
+token = evalcx('({})', other);
+heldValues = [];
+registry.register({}, 1, token);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 1);
+assertEq(heldValues[0], 1);
+heldValues = [];
+registry.register({}, 1, token);
+registry.unregister(token);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 0);
+
+// FinalizationRegistry is designed to be subclassable.
+class MyRegistry extends FinalizationRegistry {
+ constructor(callback) {
+ super(callback);
+ }
+}
+let r2 = new MyRegistry(value => {
+ heldValues.push(value);
+});
+heldValues = [];
+r2.register({}, 42);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 1);
+assertEq(heldValues[0], 42);
+
+// Test cleanupSome.
+heldValues = [];
+let r5 = new FinalizationRegistry(v => heldValues.push(v));
+r5.register({}, 1);
+r5.register({}, 2);
+r5.register({}, 3);
+gc();
+r5.cleanupSome();
+assertEq(heldValues.length, 3);
+heldValues = heldValues.sort((a, b) => a - b);
+assertEq(heldValues[0], 1);
+assertEq(heldValues[1], 2);
+assertEq(heldValues[2], 3);
+
+// Test trying to call cleanupSome in callback.
+let r6 = new FinalizationRegistry(x => {
+ r6.cleanupSome();
+});
+r6.register({}, 1);
+gc();
+drainJobQueue();
+
+// Test trying to call cleanupSome in callback with multiple values.
+let callbackCounter7 = 0;
+let r7 = new FinalizationRegistry(x => {
+ callbackCounter7++;
+ r7.cleanupSome();
+});
+r7.register({}, 1);
+r7.register({}, 2);
+r7.register({}, 3);
+r7.register({}, 4);
+gc();
+drainJobQueue();
+assertEq(callbackCounter7, 4);
+
+// Test that targets don't keep the finalization registry alive.
+let target = {};
+registry = new FinalizationRegistry(value => undefined);
+registry.register(target, 1);
+let weakRef = new WeakRef(registry);
+registry = undefined;
+assertEq(typeof weakRef.deref(), 'object');
+drainJobQueue();
+gc();
+assertEq(weakRef.deref(), undefined);
+assertEq(typeof target, 'object');
+
+// Test that targets don't keep the finalization registry alive when also
+// used as the unregister token.
+registry = new FinalizationRegistry(value => undefined);
+registry.register(target, 1, target);
+weakRef = new WeakRef(registry);
+registry = undefined;
+assertEq(typeof weakRef.deref(), 'object');
+drainJobQueue();
+gc();
+assertEq(weakRef.deref(), undefined);
+assertEq(typeof target, 'object');
+
+// Test that cleanup doesn't happen if the finalization registry dies.
+heldValues = [];
+new FinalizationRegistry(value => {
+ heldValues.push(value);
+}).register({}, 1);
+gc();
+drainJobQueue();
+assertEq(heldValues.length, 0);
diff --git a/js/src/jit-test/tests/gc/gcparam.js b/js/src/jit-test/tests/gc/gcparam.js
new file mode 100644
index 0000000000..a1584fc39b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/gcparam.js
@@ -0,0 +1,64 @@
+gczeal(0);
+
+function testGetParam(key) {
+ gcparam(key);
+}
+
+function testChangeParam(key, diff) {
+ if (!diff) {
+ diff = 1;
+ }
+
+ let prev = gcparam(key);
+
+ let newValue = prev > 0 ? prev - diff : prev + diff;
+ gcparam(key, newValue);
+ assertEq(gcparam(key), newValue);
+
+ gcparam(key, prev);
+ assertEq(gcparam(key), prev);
+}
+
+testGetParam("gcBytes");
+testGetParam("gcNumber");
+testGetParam("unusedChunks");
+testGetParam("totalChunks");
+testGetParam("nurseryBytes");
+testGetParam("majorGCNumber");
+testGetParam("minorGCNumber");
+testGetParam("chunkBytes");
+testGetParam("helperThreadCount");
+
+testChangeParam("maxBytes");
+testChangeParam("minNurseryBytes", 16 * 1024);
+testChangeParam("maxNurseryBytes", 1024 * 1024);
+testChangeParam("incrementalGCEnabled");
+testChangeParam("perZoneGCEnabled");
+testChangeParam("sliceTimeBudgetMS");
+testChangeParam("highFrequencyTimeLimit");
+testChangeParam("smallHeapSizeMax");
+testChangeParam("largeHeapSizeMin");
+testChangeParam("highFrequencySmallHeapGrowth");
+testChangeParam("highFrequencyLargeHeapGrowth");
+testChangeParam("lowFrequencyHeapGrowth");
+testChangeParam("balancedHeapLimitsEnabled");
+testChangeParam("heapGrowthFactor");
+testChangeParam("allocationThreshold");
+testChangeParam("smallHeapIncrementalLimit");
+testChangeParam("largeHeapIncrementalLimit");
+testChangeParam("minEmptyChunkCount");
+testChangeParam("maxEmptyChunkCount");
+testChangeParam("compactingEnabled");
+testChangeParam("parallelMarkingEnabled");
+testChangeParam("parallelMarkingThresholdKB");
+testChangeParam("minLastDitchGCPeriod");
+testChangeParam("nurseryFreeThresholdForIdleCollection");
+testChangeParam("nurseryFreeThresholdForIdleCollectionPercent");
+testChangeParam("nurseryTimeoutForIdleCollectionMS");
+testChangeParam("pretenureThreshold");
+testChangeParam("zoneAllocDelayKB");
+testChangeParam("mallocThresholdBase");
+testChangeParam("urgentThreshold");
+testChangeParam("nurseryTimeoutForIdleCollectionMS");
+testChangeParam("helperThreadRatio");
+testChangeParam("maxHelperThreads");
diff --git a/js/src/jit-test/tests/gc/gczeal-range.js b/js/src/jit-test/tests/gc/gczeal-range.js
new file mode 100644
index 0000000000..1589a54af5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/gczeal-range.js
@@ -0,0 +1,5 @@
+try {
+ gczeal(123);
+} catch(e) {
+ assertEq(e.toString().includes("out of range"), true);
+}
diff --git a/js/src/jit-test/tests/gc/helper-thread-params.js b/js/src/jit-test/tests/gc/helper-thread-params.js
new file mode 100644
index 0000000000..282ea82a54
--- /dev/null
+++ b/js/src/jit-test/tests/gc/helper-thread-params.js
@@ -0,0 +1,34 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+function assertError(thunk) {
+ let threw = false;
+ try {
+ thunk();
+ } catch (e) {
+ threw = true;
+ }
+ assertEq(threw, true);
+}
+
+let initialHelperThreads = helperThreadCount();
+
+// Test that setting maxHelperThreads limits the number of threads.
+gcparam("helperThreadRatio", 100);
+for (let i = 1; i <= initialHelperThreads; i++) {
+ gcparam("maxHelperThreads", i);
+ assertEq(gcparam("helperThreadCount"), i);
+}
+
+// Test that setting helperThreadRatio works as expected.
+gcparam("maxHelperThreads", 1000);
+for (let i = 25; i <= 400; i *= 2) {
+ gcparam("helperThreadRatio", i);
+ let ratio = i / 100;
+ let expected = Math.max(Math.floor(initialHelperThreads * ratio), 1);
+ assertEq(gcparam("helperThreadCount"), expected);
+ assertEq(helperThreadCount(), Math.max(initialHelperThreads, expected));
+}
+
+// Test that illegal settings are checked.
+assertError(() => gcparam("helperThreadRatio", 0));
+assertError(() => gcparam("maxHelperThreads", 0));
diff --git a/js/src/jit-test/tests/gc/incremental-01.js b/js/src/jit-test/tests/gc/incremental-01.js
new file mode 100644
index 0000000000..d4646c1656
--- /dev/null
+++ b/js/src/jit-test/tests/gc/incremental-01.js
@@ -0,0 +1,31 @@
+var objs;
+
+function init()
+{
+ objs = new Object();
+ var x = new Object();
+ objs.root1 = x;
+ objs.root2 = new Object();
+ x.ptr = new Object();
+ x = null;
+
+ /*
+ * Clears out the arena lists. Otherwise all the objects above
+ * would be considered to be created during the incremental GC.
+ */
+ gc();
+}
+
+/*
+ * Use eval here so that the interpreter frames end up higher on the
+ * stack, which avoids them being seen later on by the conservative
+ * scanner.
+ */
+eval("init()");
+
+gcslice(0); // Start IGC, but don't mark anything.
+selectforgc(objs.root2);
+gcslice(1);
+objs.root2.ptr = objs.root1.ptr;
+objs.root1.ptr = null;
+gcslice();
diff --git a/js/src/jit-test/tests/gc/incremental-02.js b/js/src/jit-test/tests/gc/incremental-02.js
new file mode 100644
index 0000000000..a2a2c8de85
--- /dev/null
+++ b/js/src/jit-test/tests/gc/incremental-02.js
@@ -0,0 +1,30 @@
+var objs;
+
+function init()
+{
+ objs = new Object();
+ var x = new Object();
+ objs.root = x;
+ x.a = new Object();
+ x.b = new Object();
+
+ /*
+ * Clears out the arena lists. Otherwise all the objects above
+ * would be considered to be created during the incremental GC.
+ */
+ gc();
+}
+
+/*
+ * Use eval here so that the interpreter frames end up higher on the
+ * stack, which avoids them being seen later on by the conservative
+ * scanner.
+ */
+eval("init()");
+
+gcslice(0); // Start IGC, but don't mark anything.
+selectforgc(objs.root);
+gcslice(1);
+delete objs.root.b;
+delete objs.root.a;
+gcslice();
diff --git a/js/src/jit-test/tests/gc/incremental-AccessorShape-barrier.js b/js/src/jit-test/tests/gc/incremental-AccessorShape-barrier.js
new file mode 100644
index 0000000000..0055e2c51f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/incremental-AccessorShape-barrier.js
@@ -0,0 +1,18 @@
+gczeal(0);
+gc();
+
+var o = {};
+function foo() {
+ var i = 0;
+ startgc(0);
+ Object.defineProperty(o, 'foo', {configurable: true, get: function g() { return i; },
+ set: function s() { return i; }});
+ Object.defineProperty(o, 'foo', {configurable: true, get: function g() { return i; },
+ set: function s() { return i; }});
+ Object.defineProperty(o, 'foo', {configurable: true, get: function g() { return i; },
+ set: function s() { return i; }});
+ Object.defineProperty(o, 'foo', {configurable: true, get: function g() { return i; },
+ set: function s() { return i; }});
+ abortgc();
+}
+foo();
diff --git a/js/src/jit-test/tests/gc/incremental-abort.js b/js/src/jit-test/tests/gc/incremental-abort.js
new file mode 100644
index 0000000000..ee128d1290
--- /dev/null
+++ b/js/src/jit-test/tests/gc/incremental-abort.js
@@ -0,0 +1,57 @@
+// |jit-test| skip-if: !getBuildConfiguration()['has-gczeal'] || (getBuildConfiguration()['osx'] && getBuildConfiguration()['arm64'])
+
+// Test aborting an incremental GC in all possible states
+
+gczeal(0);
+gc();
+
+// Allocate objectCount objects in zoneCount zones and run a incremental
+// shrinking GC with slices with a work budget of sliceBudget until we reach
+// GC state abortState at which point, abort the GC.
+function testAbort(zoneCount, objectCount, sliceBudget, abortState)
+{
+
+ var zones = [];
+ for (var i = 0; i < zoneCount; i++) {
+ var zone = newGlobal({newCompartment: true});
+ evaluate("var objects; " +
+ "function makeObjectGraph(objectCount) { " +
+ " objects = []; " +
+ " for (var i = 0; i < objectCount; i++) " +
+ " objects.push({i: i}); " +
+ "}",
+ { global: zone });
+ zone.makeObjectGraph(objectCount);
+ zones.push(zone);
+ }
+
+ gc();
+
+ var didAbort = false;
+ startgc(sliceBudget, "shrinking");
+ assertEq(currentgc().isShrinking, true);
+ while (gcstate() !== "NotActive") {
+ if (gcstate() == abortState) {
+ abortgc();
+ didAbort = true;
+ break;
+ }
+
+ gcslice(sliceBudget);
+ }
+
+ assertEq(gcstate(), "NotActive");
+ if (abortState)
+ assertEq(didAbort, true);
+
+ return zones;
+}
+
+gczeal(0);
+testAbort(10, 10000, 10000);
+testAbort(10, 10000, 10000, "Mark");
+testAbort(10, 10000, 1000, "Sweep");
+testAbort(10, 10000, 10000, "Compact");
+// Note: we do not yield automatically before Finalize or Decommit, as they
+// yield internally. Thus, we may not witness an incremental state in this
+// phase and cannot test it explicitly.
diff --git a/js/src/jit-test/tests/gc/incremental-compacting.js b/js/src/jit-test/tests/gc/incremental-compacting.js
new file mode 100644
index 0000000000..f051cf60ea
--- /dev/null
+++ b/js/src/jit-test/tests/gc/incremental-compacting.js
@@ -0,0 +1,43 @@
+// |jit-test| skip-if: !("gcstate" in this && "gczeal" in this)
+
+// Exercise incremental compacting GC
+// Run with MOZ_GCTIMER to see the timings
+
+gczeal(0);
+
+function testCompacting(zoneCount, objectCount, sliceCount)
+{
+ // Allocate objectCount objects in zoneCount zones
+ // On linux64 debug builds we will move them all
+ // Run compacting GC with multiple slices
+
+ var zones = [];
+ for (var i = 0; i < zoneCount; i++) {
+ var zone = newGlobal();
+ evaluate("var objects; " +
+ "function makeObjectGraph(objectCount) { " +
+ " objects = []; " +
+ " for (var i = 0; i < objectCount; i++) " +
+ " objects.push({ serial: i }); " +
+ "}",
+ { global: zone });
+ zone.makeObjectGraph(objectCount);
+ zones.push(zone);
+ }
+
+ // Finish any alloc-triggered incremental GC
+ if (gcstate() !== "NotActive")
+ gc();
+
+ startgc(sliceCount, "shrinking");
+ while (gcstate() !== "NotActive") {
+ gcslice(sliceCount);
+ }
+
+ return zones;
+}
+
+testCompacting(1, 100000, 100000);
+testCompacting(2, 100000, 100000);
+testCompacting(4, 50000, 100000);
+testCompacting(2, 100000, 50000);
diff --git a/js/src/jit-test/tests/gc/incremental-state.js b/js/src/jit-test/tests/gc/incremental-state.js
new file mode 100644
index 0000000000..f20b722234
--- /dev/null
+++ b/js/src/jit-test/tests/gc/incremental-state.js
@@ -0,0 +1,96 @@
+// |jit-test| skip-if: !hasFunction["gczeal"]
+
+function assert(x) {
+ assertEq(true, x);
+}
+
+function waitForState(state) {
+ while (gcstate() !== state && gcstate() !== "NotActive") {
+ gcslice(100);
+ }
+}
+
+// Test expected state changes during collection.
+gczeal(0);
+
+// Non-incremental GC.
+gc();
+assertEq(gcstate(), "NotActive");
+
+// Incremental GC in minimal slice. Note that finalization always uses zero-
+// sized slices while background finalization is on-going, so we need to loop.
+gcslice(1000000);
+assert(gcstate() !== "Mark");
+finishgc();
+assertEq(gcstate(), "NotActive");
+
+// Incremental GC in multiple slices: if marking takes more than one slice,
+// we yield before we start sweeping.
+gczeal(0);
+gcslice(1);
+waitForState("Mark");
+assertEq(gcstate(), "Mark");
+gcslice(1000000);
+assertEq(gcstate(), "Mark");
+gcslice(1000000);
+assert(gcstate() !== "Mark");
+finishgc();
+
+// Zeal mode 6: Incremental GC in two slices:
+// 1) prepare
+// 2) mark roots, mark and sweep
+gczeal(6, 0);
+gcslice(1);
+assertEq(gcstate(), "Prepare");
+gcslice(1);
+assertEq(gcstate(), "NotActive");
+
+// Zeal mode 8: Incremental GC in two slices:
+// 1) prepare and mark roots
+// 2) mark and sweep
+gczeal(8, 0);
+gcslice(1);
+assertEq(gcstate(), "Mark");
+gcslice(1);
+assertEq(gcstate(), "NotActive");
+
+// Zeal mode 9: Incremental GC in two slices:
+// 1) prepare, mark roots and marking
+// 2) new marking and sweeping
+gczeal(9, 0);
+gcslice(1);
+assertEq(gcstate(), "Mark");
+gcslice(1);
+assertEq(gcstate(), "NotActive");
+
+// Zeal mode 10: Incremental GC in multiple slices (always yeilds before
+// sweeping). This test uses long slices to prove that this zeal mode yields
+// in sweeping, where normal IGC (above) does not.
+gczeal(10, 0);
+gcslice(1000000);
+while (gcstate() === "Prepare") {
+ gcslice(1000000);
+}
+assertEq(gcstate(), "Sweep");
+gcslice(1000000);
+assert(gcstate() !== "Sweep");
+finishgc();
+
+// Two-slice zeal modes that yield once during sweeping.
+for (let mode of [ 17, 19 ]) {
+ gczeal(mode, 0);
+ gcslice(1);
+ assertEq(gcstate(), "Sweep");
+ gcslice(1);
+ assertEq(gcstate(), "NotActive");
+}
+
+// Two-slice zeal modes that yield per-zone during sweeping.
+const sweepingZealModes = [ 20, 21, 22, 23 ];
+for (let mode of sweepingZealModes) {
+ gczeal(mode, 0);
+ gcslice(1);
+ while (gcstate() === "Sweep")
+ gcslice(1);
+ assertEq(gcstate(), "NotActive");
+}
diff --git a/js/src/jit-test/tests/gc/jsscript-mark-children.js b/js/src/jit-test/tests/gc/jsscript-mark-children.js
new file mode 100644
index 0000000000..c74a19c0f3
--- /dev/null
+++ b/js/src/jit-test/tests/gc/jsscript-mark-children.js
@@ -0,0 +1,24 @@
+// Bug 758509 changed things so that a JSScript is partially initialized when
+// it is created, which is prior to bytecode generation; full initialization
+// only occurs after bytecode generation. This means that
+// JSScript::markChildren() must deal with partially-initialized JSScripts.
+// This test forces that to happen, because each let block allocates a
+// StaticBlockObject. All that should happen is that we don't crash.
+
+let t = 0;
+gczeal(2,1);
+eval("\
+let x0 = 3, y = 4;\
+{ let x = x0+0, y = 12; t += (x + y); } \
+{ let x = x0+1, y = 12; t += (x + y); } \
+{ let x = x0+2, y = 12; t += (x + y); } \
+{ let x = x0+3, y = 12; t += (x + y); } \
+{ let x = x0+4, y = 12; t += (x + y); } \
+{ let x = x0+5, y = 12; t += (x + y); } \
+{ let x = x0+6, y = 12; t += (x + y); } \
+{ let x = x0+7, y = 12; t += (x + y); } \
+{ let x = x0+8, y = 12; t += (x + y); } \
+{ let x = x0+9, y = 12; t += (x + y); } \
+t += (x0 + y);\
+assertEq(t, 202);\
+");
diff --git a/js/src/jit-test/tests/gc/marking-thread-count.js b/js/src/jit-test/tests/gc/marking-thread-count.js
new file mode 100644
index 0000000000..5b90e14186
--- /dev/null
+++ b/js/src/jit-test/tests/gc/marking-thread-count.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+let initialGCHelperThreadCount = gcparam('helperThreadCount');
+
+let prevHelperThreadCount = helperThreadCount();
+for (let i of [0, 1, 4, 8, 4, 0]) {
+ gcparam('markingThreadCount', i);
+ assertEq(gcparam('markingThreadCount'), i);
+ assertEq(gcparam('helperThreadCount'), initialGCHelperThreadCount);
+ assertEq(true, helperThreadCount() >= Math.max(prevHelperThreadCount, i));
+ prevHelperThreadCount = helperThreadCount();
+}
diff --git a/js/src/jit-test/tests/gc/multi-01.js b/js/src/jit-test/tests/gc/multi-01.js
new file mode 100644
index 0000000000..18aa40aaea
--- /dev/null
+++ b/js/src/jit-test/tests/gc/multi-01.js
@@ -0,0 +1,9 @@
+/* Make sure we don't collect the atoms compartment unless every compartment is marked. */
+
+var g = newGlobal();
+g.eval("var x = 'some-atom';");
+
+schedulezone(this);
+schedulezone('atoms');
+gc('zone');
+print(g.x);
diff --git a/js/src/jit-test/tests/gc/multi-02.js b/js/src/jit-test/tests/gc/multi-02.js
new file mode 100644
index 0000000000..fedb51ce92
--- /dev/null
+++ b/js/src/jit-test/tests/gc/multi-02.js
@@ -0,0 +1,10 @@
+/* Exercise the path where we want to collect a new compartment in the middle of incremental GC. */
+
+var g1 = newGlobal();
+var g2 = newGlobal();
+
+schedulezone(g1);
+gcslice(0); // Start IGC, but don't mark anything.
+schedulezone(g2);
+gcslice(1);
+gcslice();
diff --git a/js/src/jit-test/tests/gc/multi-03.js b/js/src/jit-test/tests/gc/multi-03.js
new file mode 100644
index 0000000000..0212838dce
--- /dev/null
+++ b/js/src/jit-test/tests/gc/multi-03.js
@@ -0,0 +1,11 @@
+/* Exercise the path where we want to collect a new compartment in the middle of incremental GC. */
+
+var g1 = newGlobal();
+var g2 = newGlobal();
+
+schedulezone(g1);
+schedulezone(g2);
+gcslice(0); // Start IGC, but don't mark anything.
+schedulezone(g1);
+gcslice(1);
+gcslice();
diff --git a/js/src/jit-test/tests/gc/oomInArrayProtoTest.js b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js
new file mode 100644
index 0000000000..b99669d92d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js
@@ -0,0 +1,22 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function arrayProtoOutOfRange() {
+ function f(obj) {
+ return typeof obj[15];
+ }
+
+ function test() {
+ var a = [1, 2];
+ a.__proto__ = {15: 1337};
+ var b = [1, 2, 3, 4];
+
+ for (var i = 0; i < 200; i++) {
+ var r = f(i % 2 ? a : b);
+ assertEq(r, i % 2 ? "number" : "undefined");
+ }
+ }
+
+ test();
+}
+
+oomTest(arrayProtoOutOfRange);
diff --git a/js/src/jit-test/tests/gc/oomInByteSize.js b/js/src/jit-test/tests/gc/oomInByteSize.js
new file mode 100644
index 0000000000..9566b9cb49
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInByteSize.js
@@ -0,0 +1,18 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => byteSize({}));
+oomTest(() => byteSize({ w: 1, x: 2, y: 3 }));
+oomTest(() => byteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2 }));
+oomTest(() => byteSize([1, 2, 3]));
+oomTest(() => byteSize(function () {}));
+
+function f1() {
+ return 42;
+}
+oomTest(() => byteSizeOfScript(f1));
+
+oomTest(() => byteSize("1234567"));
+oomTest(() => byteSize("千早ぶる神代"));
+
+let s = Symbol();
+oomTest(() => byteSize(s));
diff --git a/js/src/jit-test/tests/gc/oomInDebugger.js b/js/src/jit-test/tests/gc/oomInDebugger.js
new file mode 100644
index 0000000000..c1904f573a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInDebugger.js
@@ -0,0 +1,4 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var g = newGlobal();
+oomTest(() => Debugger(g));
diff --git a/js/src/jit-test/tests/gc/oomInDtoa.js b/js/src/jit-test/tests/gc/oomInDtoa.js
new file mode 100644
index 0000000000..83ded51cbb
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInDtoa.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(function() { return 1e300; })
diff --git a/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js b/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js
new file mode 100644
index 0000000000..d5c8f29b27
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js
@@ -0,0 +1,14 @@
+// |jit-test| skip-if: !('oomTest' in this)
+oomTest(() => {
+ let x = 0;
+ try {
+ for (let i = 0; i < 100; i++) {
+ if (i == 99)
+ throw "foo";
+ x += i;
+ }
+ } catch (e) {
+ x = 0;
+ }
+ return x;
+});
diff --git a/js/src/jit-test/tests/gc/oomInFindPath.js b/js/src/jit-test/tests/gc/oomInFindPath.js
new file mode 100644
index 0000000000..4b3d95688c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInFindPath.js
@@ -0,0 +1,18 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var o = { w: { x: { y: { z: {} } } } };
+oomTest(() => findPath(o, o.w.x.y.z));
+
+var a = [ , o ];
+oomTest(() => findPath(a, o));
+
+function C() {}
+C.prototype.obj = {};
+var c = new C;
+
+oomTest(() => findPath(c, c.obj));
+
+function f(x) { return function g(y) { return x+y; }; }
+var o = {}
+var gc = f(o);
+oomTest(() => findPath(gc, o));
diff --git a/js/src/jit-test/tests/gc/oomInFormatStackDump.js b/js/src/jit-test/tests/gc/oomInFormatStackDump.js
new file mode 100644
index 0000000000..ce68e47f9e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInFormatStackDump.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => getBacktrace({args: true, locals: true, thisprops: true}));
diff --git a/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js b/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js
new file mode 100644
index 0000000000..a568fc592f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => getBacktrace({thisprops: gc() && delete addDebuggee.enabled}));
diff --git a/js/src/jit-test/tests/gc/oomInNewGlobal.js b/js/src/jit-test/tests/gc/oomInNewGlobal.js
new file mode 100644
index 0000000000..c45737e143
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInNewGlobal.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(newGlobal);
diff --git a/js/src/jit-test/tests/gc/oomInOffTheadCompile.js b/js/src/jit-test/tests/gc/oomInOffTheadCompile.js
new file mode 100644
index 0000000000..d4e0d4135e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile.js
@@ -0,0 +1,15 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+oomTest(() => {
+ offThreadCompileToStencil(
+ `
+ function f(x) {
+ if (x == 0)
+ return "foobar";
+ return 1 + f(x - 1);
+ }
+ f(5);
+ `);
+ var stencil = finishOffThreadStencil();
+ evalStencil(stencil);
+});
diff --git a/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js b/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js
new file mode 100644
index 0000000000..1cac5ee859
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+oomTest(() => {
+ offThreadCompileToStencil("function a(x) {");
+ var stencil = finishOffThreadStencil();
+ evalStencil(stencil);
+});
diff --git a/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js b/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js
new file mode 100644
index 0000000000..6535676b72
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js
@@ -0,0 +1,17 @@
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
+
+oomTest(() => {
+ offThreadCompileToStencil(`
+ function f(x) {
+ class of extends ("ABCDEFGHIJK") {
+ test() { return true; };
+ static get() {};
+ static get() {};
+ }
+ return 1 + f(x - 1);
+ }
+ return g("try{}catch(e){}", n);
+ `);
+ var stencil = finishOffThreadStencil();
+ evalStencil(stencil);
+});
diff --git a/js/src/jit-test/tests/gc/oomInParseAsmJS.js b/js/src/jit-test/tests/gc/oomInParseAsmJS.js
new file mode 100644
index 0000000000..72216e1c2c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInParseAsmJS.js
@@ -0,0 +1,16 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+function parseAsmJS() {
+ eval(`function m(stdlib)
+ {
+ "use asm";
+ var abs = stdlib.Math.abs;
+ function f(d)
+ {
+ d = +d;
+ return (~~(5.0 - +abs(d)))|0;
+ }
+ return f;
+ }`);
+}
+oomTest(parseAsmJS);
diff --git a/js/src/jit-test/tests/gc/oomInParseFunction.js b/js/src/jit-test/tests/gc/oomInParseFunction.js
new file mode 100644
index 0000000000..b1c1bd6297
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInParseFunction.js
@@ -0,0 +1,3 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => eval("function f() {}"));
diff --git a/js/src/jit-test/tests/gc/oomInRegExp.js b/js/src/jit-test/tests/gc/oomInRegExp.js
new file mode 100644
index 0000000000..b58f0ac50d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInRegExp.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3));
+oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));
+oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true));
diff --git a/js/src/jit-test/tests/gc/oomInRegExp2.js b/js/src/jit-test/tests/gc/oomInRegExp2.js
new file mode 100644
index 0000000000..c35075b375
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInRegExp2.js
@@ -0,0 +1,5 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3), {keepFailing: true});
+oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false), {keepFailing: true});
+oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true), {keepFailing: true});
diff --git a/js/src/jit-test/tests/gc/oomInRegExpAlternativeGeneration.js b/js/src/jit-test/tests/gc/oomInRegExpAlternativeGeneration.js
new file mode 100644
index 0000000000..b2a8ea7588
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInRegExpAlternativeGeneration.js
@@ -0,0 +1,16 @@
+// |jit-test| allow-oom; allow-unhandlable-oom
+// Bug 1234402
+// Unhandlable OOM in AlternativeGeneration::AlternativeGeneration.
+
+if (typeof oomAfterAllocations == "function" && helperThreadCount() > 0) {
+ offThreadCompileToStencil(`
+[null, "", ""].forEach(function(locales) {
+try {
+Intl.NumberFormat(locales)
+} catch (e) {}
+oomAfterAllocations(100);
+})
+`);
+ var stencil = finishOffThreadStencil();
+ evalStencil(stencil);
+}
diff --git a/js/src/jit-test/tests/gc/oomInWeakMap.js b/js/src/jit-test/tests/gc/oomInWeakMap.js
new file mode 100644
index 0000000000..522dc24738
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInWeakMap.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+oomTest(function () {
+ eval(`var wm = new WeakMap();
+ wm.set({}, 'FOO').get(false);`);
+});
diff --git a/js/src/jit-test/tests/gc/pretenure-array-long-lived.js b/js/src/jit-test/tests/gc/pretenure-array-long-lived.js
new file mode 100644
index 0000000000..77ea7c0efc
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-array-long-lived.js
@@ -0,0 +1,17 @@
+// Allocate many long-lived objects and check that we pretenure them.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+allocateArrays(nurseryCount, true); // Warm up.
+
+let { minor, major } = runTestAndCountCollections(
+ () => allocateArrays(tenuredCount, true)
+);
+
+// Check that after the warm up period we 'only' do major GCs (we allow one
+// nursery collection).
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor <= 1, true);
+assertEq(major >= 5, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-array-long-then-short-lived.js b/js/src/jit-test/tests/gc/pretenure-array-long-then-short-lived.js
new file mode 100644
index 0000000000..3f7b30507b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-array-long-then-short-lived.js
@@ -0,0 +1,28 @@
+// Allocate many objects, changing the lifetime from long-lived to short lived
+// and check that we recover.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+// Phase 1: long lived.
+
+allocateArrays(nurseryCount, true);
+let { minor, major } = runTestAndCountCollections(
+ () => allocateArrays(tenuredCount, true)
+);
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor <= 1, true);
+assertEq(major >= 5, true);
+
+// Phase 2: short lived.
+
+allocateArrays(tenuredCount, false);
+({ minor, major } = runTestAndCountCollections(
+ () => allocateArrays(nurseryCount * 5, false)
+));
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor >= 5, true);
+assertEq(major == 0, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-array-short-lived.js b/js/src/jit-test/tests/gc/pretenure-array-short-lived.js
new file mode 100644
index 0000000000..6a7c814c02
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-array-short-lived.js
@@ -0,0 +1,16 @@
+// Allocate many short-lived objects and check that we don't pretenure them.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+allocateArrays(nurseryCount, false); // Warm up.
+
+let { minor, major } = runTestAndCountCollections(
+ () => allocateArrays(nurseryCount * 5, false)
+);
+
+// Check that after the warm up period we only do minor GCs.
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor >= 5, true);
+assertEq(major == 0, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-array-short-then-long-lived.js b/js/src/jit-test/tests/gc/pretenure-array-short-then-long-lived.js
new file mode 100644
index 0000000000..1cef28c89c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-array-short-then-long-lived.js
@@ -0,0 +1,28 @@
+// Allocate many objects, changing the lifetime from short-lived to long-lived
+// and check that we recover.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+// Phase 1: short lived.
+
+allocateArrays(nurseryCount, false);
+let { minor, major } = runTestAndCountCollections(
+ () => allocateArrays(tenuredCount, false)
+);
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor >= 5, true);
+assertEq(major == 0, true);
+
+// Phase 2: long lived.
+
+allocateArrays(tenuredCount, true);
+({ minor, major } = runTestAndCountCollections(
+ () => allocateArrays(tenuredCount * 5, true)
+));
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor <= 1, true);
+assertEq(major >= 5, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-object-long-lived.js b/js/src/jit-test/tests/gc/pretenure-object-long-lived.js
new file mode 100644
index 0000000000..4b741e3ebe
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-object-long-lived.js
@@ -0,0 +1,17 @@
+// Allocate many long-lived objects and check that we pretenure them.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+allocateObjects(nurseryCount, true); // Warm up.
+
+let { minor, major } = runTestAndCountCollections(
+ () => allocateObjects(tenuredCount, true)
+);
+
+// Check that after the warm up period we 'only' do major GCs (we allow one
+// nursery collection).
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor <= 1, true);
+assertEq(major >= 5, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-object-long-then-short-lived.js b/js/src/jit-test/tests/gc/pretenure-object-long-then-short-lived.js
new file mode 100644
index 0000000000..280ce3a5bd
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-object-long-then-short-lived.js
@@ -0,0 +1,28 @@
+// Allocate many objects, changing the lifetime from long-lived to short lived
+// and check that we recover.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+// Phase 1: long lived.
+
+allocateObjects(nurseryCount, true);
+let { minor, major } = runTestAndCountCollections(
+ () => allocateObjects(tenuredCount, true)
+);
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor <= 1, true);
+assertEq(major >= 5, true);
+
+// Phase 2: short lived.
+
+allocateObjects(tenuredCount, false);
+({ minor, major } = runTestAndCountCollections(
+ () => allocateObjects(nurseryCount * 5, false)
+));
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor >= 5, true);
+assertEq(major == 0, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-object-short-lived.js b/js/src/jit-test/tests/gc/pretenure-object-short-lived.js
new file mode 100644
index 0000000000..0e7dc3b65b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-object-short-lived.js
@@ -0,0 +1,16 @@
+// Allocate many short-lived objects and check that we don't pretenure them.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+allocateObjects(nurseryCount, false); // Warm up.
+
+let { minor, major } = runTestAndCountCollections(
+ () => allocateObjects(nurseryCount * 5, false)
+);
+
+// Check that after the warm up period we only do minor GCs.
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor >= 5, true);
+assertEq(major == 0, true);
diff --git a/js/src/jit-test/tests/gc/pretenure-object-short-then-long-lived.js b/js/src/jit-test/tests/gc/pretenure-object-short-then-long-lived.js
new file mode 100644
index 0000000000..0de2440594
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenure-object-short-then-long-lived.js
@@ -0,0 +1,28 @@
+// Allocate many objects, changing the lifetime from short-lived to long-lived
+// and check that we recover.
+
+load(libdir + "pretenure.js");
+
+setupPretenureTest();
+
+// Phase 1: short lived.
+
+allocateObjects(nurseryCount, false);
+let { minor, major } = runTestAndCountCollections(
+ () => allocateObjects(tenuredCount, false)
+);
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor >= 5, true);
+assertEq(major == 0, true);
+
+// Phase 2: long lived.
+
+allocateObjects(tenuredCount, true);
+({ minor, major } = runTestAndCountCollections(
+ () => allocateObjects(tenuredCount * 5, true)
+));
+
+print(`${minor} minor GCs, ${major} major GCs`);
+assertEq(minor <= 1, true);
+assertEq(major >= 5, true);
diff --git a/js/src/jit-test/tests/gc/pretenuring.js b/js/src/jit-test/tests/gc/pretenuring.js
new file mode 100644
index 0000000000..9c13954d3d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/pretenuring.js
@@ -0,0 +1,42 @@
+// Test nursery string allocation and pretenuring.
+
+gczeal(0);
+
+gcparam("minNurseryBytes", 4096 * 1024);
+gcparam("maxNurseryBytes", 4096 * 1024);
+gc();
+
+// String allocation in the nursery is initially enabled.
+assertEq(nurseryStringsEnabled(), true);
+
+// Literal strings are atoms (which are always tenured).
+assertEq(isNurseryAllocated("foo"), false);
+
+// The result of Number.toString is nursery allocated.
+assertEq(isNurseryAllocated((1234).toString()), true);
+
+// Ropes are nursery allocated.
+let s = "bar";
+assertEq(isNurseryAllocated("foo" + s), true);
+
+// Dependent strings are nursery allocated.
+assertEq(isNurseryAllocated("foobar".substr(1)), true);
+
+// The testing function 'newString' allows control over which heap is used.
+assertEq(isNurseryAllocated(newString("foobar", { tenured: true })), false);
+assertEq(isNurseryAllocated(newString("foobar", { tenured: false })), true);
+
+// Allocating lots of strings which survive nursery collection disables
+// allocating strings in the nursery.
+let a = [];
+for (let i = 1; i < 500000; i++) {
+ a.push(i.toString());
+}
+gc();
+assertEq(nurseryStringsEnabled(), false);
+
+// When a large number of strings are collected by major GC nursery allocation
+// is enabled again.
+a = undefined;
+gc();
+assertEq(nurseryStringsEnabled(), true);
diff --git a/js/src/jit-test/tests/gc/regress-1711413.js b/js/src/jit-test/tests/gc/regress-1711413.js
new file mode 100644
index 0000000000..8261c47f7f
--- /dev/null
+++ b/js/src/jit-test/tests/gc/regress-1711413.js
@@ -0,0 +1,6 @@
+// Required --no-threads to crash.
+
+enableShellAllocationMetadataBuilder();
+newGlobal({newCompartment:true});
+gcslice(1);
+nukeAllCCWs();
diff --git a/js/src/jit-test/tests/gc/str-atom-dedupe.js b/js/src/jit-test/tests/gc/str-atom-dedupe.js
new file mode 100644
index 0000000000..587f879db5
--- /dev/null
+++ b/js/src/jit-test/tests/gc/str-atom-dedupe.js
@@ -0,0 +1,16 @@
+// Create Latin1 string + atom.
+var latin1S = "foo".repeat(50);
+var obj = {};
+obj[latin1S] = 3;
+assertEq(obj[latin1S], 3);
+
+// Create a TwoByte version, ensure it's in the StringToAtomCache.
+var twoByteS = newString(latin1S, {twoByte: true});
+assertEq(obj[twoByteS], 3);
+
+// Create a dependent TwoByte string.
+var depTwoByteS = twoByteS.slice(1);
+
+// Deduplication shouldn't get confused about Latin1 atom vs TwoByte strings.
+minorgc();
+assertEq(obj["f" + depTwoByteS], 3);
diff --git a/js/src/jit-test/tests/gc/test-root-arrays.js b/js/src/jit-test/tests/gc/test-root-arrays.js
new file mode 100644
index 0000000000..2c6015f19c
--- /dev/null
+++ b/js/src/jit-test/tests/gc/test-root-arrays.js
@@ -0,0 +1,10 @@
+gczeal(0);
+let objects = [{}, {}, {}];
+blackRoot()[0] = objects[0];
+grayRoot()[0] = objects[1];
+addMarkObservers(objects);
+objects = undefined;
+gc();
+assertEq(getMarks()[0], "black");
+assertEq(getMarks()[1], "gray");
+assertEq(getMarks()[2], "dead");
diff --git a/js/src/jit-test/tests/gc/weak-marking-01.js b/js/src/jit-test/tests/gc/weak-marking-01.js
new file mode 100644
index 0000000000..a132398510
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weak-marking-01.js
@@ -0,0 +1,220 @@
+// These tests will be using object literals as keys, and we want some of them
+// to be dead after being inserted into a WeakMap. That means we must wrap
+// everything in functions because it seems like the toplevel script hangs onto
+// its object literals.
+
+gczeal(0);
+gcparam('minNurseryBytes', 1024 * 1024);
+gcparam('maxNurseryBytes', 1024 * 1024);
+
+function startIncrementalGC() {
+ startgc(1, 'shrinking');
+ while (gcstate() === "Prepare") {
+ gcslice(1);
+ }
+}
+
+function syncIncrementalGC() {
+ startIncrementalGC();
+ finishgc();
+}
+
+// All reachable keys should be found, and the rest should be swept.
+function basicSweeping() {
+ gc();
+
+ var wm1 = new WeakMap();
+ wm1.set({'name': 'obj1'}, {'name': 'val1'});
+ var hold = {'name': 'obj2'};
+ wm1.set(hold, {'name': 'val2'});
+ wm1.set({'name': 'obj3'}, {'name': 'val3'});
+
+ syncIncrementalGC();
+
+ assertEq(wm1.get(hold).name, 'val2');
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
+}
+
+basicSweeping();
+
+// Keep values alive even when they are only referenced by (live) WeakMap values.
+function weakGraph() {
+ gc();
+
+ var wm1 = new WeakMap();
+ var obj1 = {'name': 'obj1'};
+ var obj2 = {'name': 'obj2'};
+ var obj3 = {'name': 'obj3'};
+ var obj4 = {'name': 'obj4'};
+ var clear = {'name': ''}; // Make the interpreter forget about the last obj created
+
+ wm1.set(obj2, obj3);
+ wm1.set(obj3, obj1);
+ wm1.set(obj4, obj1); // This edge will be cleared
+ obj1 = obj3 = obj4 = undefined;
+
+ syncIncrementalGC();
+
+ assertEq(obj2.name, "obj2");
+ assertEq(wm1.get(obj2).name, "obj3");
+ assertEq(wm1.get(wm1.get(obj2)).name, "obj1");
+ print(nondeterministicGetWeakMapKeys(wm1).map(o => o.name).join(","));
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 2);
+}
+
+weakGraph();
+
+// ...but the weakmap itself has to stay alive, too.
+function deadWeakMap() {
+ gc();
+
+ var wm1 = new WeakMap();
+ var obj1 = makeFinalizeObserver();
+ var obj2 = {'name': 'obj2'};
+ var obj3 = {'name': 'obj3'};
+ var obj4 = {'name': 'obj4'};
+ var clear = {'name': ''}; // Make the interpreter forget about the last obj created
+
+ wm1.set(obj2, obj3);
+ wm1.set(obj3, obj1);
+ wm1.set(obj4, obj1); // This edge will be cleared
+ var initialCount = finalizeCount();
+ obj1 = obj3 = obj4 = undefined;
+ wm1 = undefined;
+
+ syncIncrementalGC();
+
+ assertEq(obj2.name, "obj2");
+ assertEq(finalizeCount(), initialCount + 1);
+}
+
+deadWeakMap();
+
+// WeakMaps do not strongly reference their keys or values. (WeakMaps hold a
+// collection of (strong) references to *edges* from keys to values. If the
+// WeakMap is not live, then its edges are of course not live either. An edge
+// holds neither its key nor its value live; it just holds a strong ref from
+// the key to the value. So if the key is live, the value is live too, but the
+// edge itself has no references to anything.)
+function deadKeys() {
+ gc();
+
+ var wm1 = new WeakMap();
+ var obj1 = makeFinalizeObserver();
+ var obj2 = {'name': 'obj2'};
+ var obj3 = makeFinalizeObserver();
+ var clear = {}; // Make the interpreter forget about the last obj created
+
+ wm1.set(obj1, obj1);
+ wm1.set(obj3, obj2);
+ obj1 = obj3 = undefined;
+ var initialCount = finalizeCount();
+
+ syncIncrementalGC();
+
+ assertEq(finalizeCount(), initialCount + 2);
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0);
+}
+
+deadKeys();
+
+// The weakKeys table has to grow if it encounters enough new unmarked weakmap
+// keys. Trigger this to happen during weakmap marking.
+//
+// There's some trickiness involved in getting it to test the right thing,
+// because if a key is marked before the weakmap, then it won't get entered
+// into the weakKeys table. This chains through multiple weakmap layers to
+// ensure that the objects can't get marked before the weakmaps.
+function weakKeysRealloc() {
+ gc();
+
+ var wm1 = new WeakMap;
+ var wm2 = new WeakMap;
+ var wm3 = new WeakMap;
+ var obj1 = {'name': 'obj1'};
+ var obj2 = {'name': 'obj2'};
+ wm1.set(obj1, wm2);
+ wm2.set(obj2, wm3);
+ for (var i = 0; i < 10000; i++) {
+ wm3.set(Object.create(null), wm2);
+ }
+ wm3.set(Object.create(null), makeFinalizeObserver());
+
+ wm2 = undefined;
+ wm3 = undefined;
+ obj2 = undefined;
+
+ var initialCount = finalizeCount();
+ syncIncrementalGC();
+ assertEq(finalizeCount(), initialCount + 1);
+}
+
+weakKeysRealloc();
+
+// The weakKeys table is populated during regular marking. When a key is later
+// deleted, both it and its delegate should be removed from weakKeys.
+// Otherwise, it will hold its value live if it gets marked, and our table
+// traversals will include non-keys, etc.
+function deletedKeys() {
+ gc();
+
+ var wm = new WeakMap;
+ var g = newGlobal();
+
+ for (var i = 0; i < 1000; i++)
+ wm.set(g.Object.create(null), i);
+
+ startIncrementalGC();
+
+ for (var key of nondeterministicGetWeakMapKeys(wm)) {
+ if (wm.get(key) % 2)
+ wm.delete(key);
+ }
+
+ gc();
+}
+
+deletedKeys();
+
+// Test adding keys during incremental GC.
+function incrementalAdds() {
+ gc();
+
+ var initialCount = finalizeCount();
+
+ var wm1 = new WeakMap;
+ var wm2 = new WeakMap;
+ var wm3 = new WeakMap;
+ var obj1 = {'name': 'obj1'};
+ var obj2 = {'name': 'obj2'};
+ wm1.set(obj1, wm2);
+ wm2.set(obj2, wm3);
+ for (var i = 0; i < 10000; i++) {
+ wm3.set(Object.create(null), wm2);
+ }
+ wm3.set(Object.create(null), makeFinalizeObserver());
+ obj2 = undefined;
+
+ var obj3 = [];
+ startIncrementalGC();
+ var M = 10;
+ var N = 800;
+ for (var j = 0; j < M; j++) {
+ for (var i = 0; i < N; i++)
+ wm3.set(Object.create(null), makeFinalizeObserver()); // Should be swept
+ for (var i = 0; i < N; i++) {
+ obj3.push({'name': 'obj3'});
+ wm1.set(obj3[obj3.length - 1], makeFinalizeObserver()); // Should not be swept
+ }
+ gcslice();
+ }
+
+ wm2 = undefined;
+ wm3 = undefined;
+
+ gc();
+ print("initialCount = " + initialCount);
+ assertEq(finalizeCount(), initialCount + 1 + M * N);
+}
+
+incrementalAdds();
diff --git a/js/src/jit-test/tests/gc/weak-marking-02.js b/js/src/jit-test/tests/gc/weak-marking-02.js
new file mode 100644
index 0000000000..b29da74496
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weak-marking-02.js
@@ -0,0 +1,338 @@
+// |jit-test| allow-unhandlable-oom
+
+// These tests will be using object literals as keys, and we want some of them
+// to be dead after being inserted into a WeakMap. That means we must wrap
+// everything in functions because it seems like the toplevel script hangs onto
+// its object literals.
+
+// Cross-compartment WeakMap keys work by storing a cross-compartment wrapper
+// in the WeakMap, and the actual "delegate" object in the target compartment
+// is the thing whose liveness is checked.
+
+gczeal(0);
+
+var g2 = newGlobal({newCompartment: true});
+g2.eval('function genObj(name) { return {"name": name} }');
+
+function basicSweeping() {
+ var wm1 = new WeakMap();
+ wm1.set({'name': 'obj1'}, {'name': 'val1'});
+ var hold = g2.genObj('obj2');
+ wm1.set(hold, {'name': 'val2'});
+ wm1.set({'name': 'obj3'}, {'name': 'val3'});
+ var obj4 = g2.genObj('obj4');
+ wm1.set(obj4, {'name': 'val3'});
+ obj4 = undefined;
+
+ startgc(100000, 'shrinking');
+ gcslice();
+ assertEq(wm1.get(hold).name, 'val2');
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
+}
+
+basicSweeping();
+
+// Same, but behind an additional WM layer, to avoid ordering problems (not
+// that I've checked that basicSweeping even has any problems.)
+
+function basicSweeping2() {
+ var wm1 = new WeakMap();
+ wm1.set({'name': 'obj1'}, {'name': 'val1'});
+ var hold = g2.genObj('obj2');
+ wm1.set(hold, {'name': 'val2'});
+ wm1.set({'name': 'obj3'}, {'name': 'val3'});
+ var obj4 = g2.genObj('obj4');
+ wm1.set(obj4, {'name': 'val3'});
+ obj4 = undefined;
+
+ var base1 = {'name': 'base1'};
+ var base2 = {'name': 'base2'};
+ var wm_base1 = new WeakMap();
+ var wm_base2 = new WeakMap();
+ wm_base1.set(base1, wm_base2);
+ wm_base2.set(base2, wm1);
+ wm1 = wm_base2 = undefined;
+
+ startgc(100000, 'shrinking');
+ gcslice();
+
+ assertEq(nondeterministicGetWeakMapKeys(wm_base1).length, 1);
+ wm_base2 = wm_base1.get(base1);
+ assertEq(nondeterministicGetWeakMapKeys(wm_base2).length, 1);
+ assertEq(nondeterministicGetWeakMapKeys(wm_base1)[0], base1);
+ assertEq(nondeterministicGetWeakMapKeys(wm_base2)[0], base2);
+ wm_base2 = wm_base1.get(base1);
+ wm1 = wm_base2.get(base2);
+ assertEq(wm1.get(hold).name, 'val2');
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
+}
+
+basicSweeping2();
+
+// Scatter the weakmap, the keys, and the values among different compartments.
+
+function tripleZoneMarking() {
+ var g1 = newGlobal({newCompartment: true});
+ var g2 = newGlobal({newCompartment: true});
+ var g3 = newGlobal({newCompartment: true});
+
+ var wm = g1.eval("new WeakMap()");
+ var key = g2.eval("({'name': 'obj1'})");
+ var value = g3.eval("({'name': 'val1'})");
+ g1 = g2 = g3 = undefined;
+ wm.set(key, value);
+
+ // Make all of it only reachable via a weakmap in the main test compartment,
+ // so that all of this happens during weak marking mode. Use the weakmap as
+ // its own key, so we know that the weakmap will get traced before the key
+ // and therefore will populate the weakKeys table and all of that jazz.
+ var base_wm = new WeakMap();
+ base_wm.set(base_wm, [ wm, key ]);
+
+ wm = key = value = undefined;
+
+ startgc(100000, 'shrinking');
+ gcslice();
+
+ var keys = nondeterministicGetWeakMapKeys(base_wm);
+ assertEq(keys.length, 1);
+ var [ wm, key ] = base_wm.get(keys[0]);
+ assertEq(key.name, "obj1");
+ value = wm.get(key);
+ assertEq(value.name, "val1");
+}
+
+tripleZoneMarking();
+
+// Same as above, but this time use enqueueMark to enforce ordering.
+
+function tripleZoneMarking2() {
+ var g1 = newGlobal();
+ var g2 = newGlobal();
+ var g3 = newGlobal();
+
+ var wm = g1.eval("wm = new WeakMap()");
+ var key = g2.eval("key = ({'name': 'obj1'})");
+ var value = g3.eval("({'name': 'val1'})");
+ wm.set(key, value);
+
+ enqueueMark("enter-weak-marking-mode");
+ g1.eval("enqueueMark(wm)"); // weakmap
+ g2.eval("enqueueMark(key)"); // delegate
+ g1.wm = g2.key = undefined;
+ g1 = g2 = g3 = undefined;
+ wm = key = value = undefined;
+
+ gc();
+
+ var [ dummy, weakmap, keywrapper ] = getMarkQueue();
+ assertEq(keywrapper.name, "obj1");
+ value = weakmap.get(keywrapper);
+ assertEq(value.name, "val1");
+
+ clearMarkQueue();
+}
+
+if (this.enqueueMark)
+ tripleZoneMarking2();
+
+function enbugger() {
+ var g = newGlobal({newCompartment: true});
+ var dbg = new Debugger;
+ g.eval("function debuggee_f() { return 1; }");
+ g.eval("function debuggee_g() { return 1; }");
+ dbg.addDebuggee(g);
+ var [ s ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_f");
+ var [ s2 ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_g");
+ g.eval("debuggee_f = null");
+ gc();
+ dbg.removeAllDebuggees();
+ gc();
+ assertEq(s.displayName, "debuggee_f");
+
+ var wm = new WeakMap;
+ var obj = Object.create(null);
+ var obj2 = Object.create(null);
+ wm.set(obj, s);
+ wm.set(obj2, obj);
+ wm.set(s2, obj2);
+ s = s2 = obj = obj2 = null;
+
+ gc();
+}
+
+enbugger();
+
+// Want to test: zone edges
+// Make map with cross-zone delegate. Collect the zones one at a time.
+function zone_edges() {
+ var g3 = newGlobal();
+ g3.eval('function genObj(name) { return {"name": name} }');
+
+ var wm1 = new WeakMap();
+ var hold = g2.genObj('obj1');
+ var others = [g2.genObj('key2'), g2.genObj('key3')];
+ wm1.set(hold, {'name': g3.genObj('val1'), 'others': others});
+ others = null;
+
+ var m = new Map;
+ m.set(m, hold);
+ hold = null;
+
+ const zones = [ this, g2, g3 ];
+ for (let zonebits = 0; zonebits < 2 ** zones.length; zonebits++) {
+ for (let z in zones) {
+ if (zonebits & (1 << z))
+ schedulezone(zones[z]);
+ }
+ startgc(1);
+ wm1.set(wm1.get(m.get(m)).others[0], g2.genObj('val2'));
+ gcslice(1000000);
+ wm1.set(wm1.get(m.get(m)).others[1], g2.genObj('val3'));
+ gc();
+ assertEq(wm1.get(m.get(m)).name.name, 'val1');
+ assertEq(wm1.get(m.get(m)).others[0].name, 'key2');
+ assertEq(wm1.get(wm1.get(m.get(m)).others[0]).name, 'val2');
+ assertEq(wm1.get(m.get(m)).others[1].name, 'key3');
+ assertEq(wm1.get(wm1.get(m.get(m)).others[1]).name, 'val3');
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 3);
+ }
+
+ // Do it again, with nuking.
+ const wm2 = g2.eval("new WeakMap");
+ wm2.set(wm1.get(m.get(m)).others[0], Object.create(null));
+ for (let zonebits = 0; zonebits < 2 ** zones.length; zonebits++) {
+ for (let z in zones) {
+ if (zonebits & (1 << z))
+ schedulezone(zones[z]);
+ }
+ startgc(1);
+ wm1.set(wm1.get(m.get(m)).others[0], g2.genObj('val2'));
+ gcslice(1000000);
+ wm1.set(wm1.get(m.get(m)).others[1], g2.genObj('val3'));
+ nukeCCW(wm1.get(wm1.get(m.get(m)).others[0]));
+ nukeCCW(wm1.get(wm1.get(m.get(m)).others[1]));
+ gc();
+ assertEq(wm1.get(m.get(m)).name.name, 'val1');
+ assertEq(wm1.get(m.get(m)).others[0].name, 'key2');
+ assertEq(wm1.get(m.get(m)).others[1].name, 'key3');
+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 3);
+ }
+}
+
+zone_edges();
+
+// Stress test: lots of cross-zone and same-zone cross-compartment edges, and
+// exercise the barriers.
+function stress(opt) {
+ printErr(JSON.stringify(opt));
+
+ var g1 = this;
+ var g2 = newGlobal({sameZoneAs: g1});
+ var g3 = newGlobal();
+
+ var globals = g1.globals = g2.globals = g3.globals = [ g1, g2, g3 ];
+ g1.name = 'main';
+ g2.name = 'same-zone';
+ g3.name = 'other-zone';
+ g1.names = g2.names = g3.names = [ g1.name, g2.name, g3.name ];
+
+ // Basic setup:
+ //
+ // Three different globals, each with a weakmap and an object. Each global's
+ // weakmap contains all 3 objects (1 from each global) as keys. Internally,
+ // that means that each weakmap will contain one local object and two
+ // cross-compartment wrappers.
+ //
+ // Now duplicate that 3 times. The first weakmap will be unmodified. The
+ // second weakmap will have its keys updated to different values. The third
+ // weakmap will have its keys deleted.
+
+ for (const i in globals) {
+ const g = globals[i];
+ g.eval('function genObj(name) { return {"name": name} }');
+ g.eval("weakmap0 = new WeakMap()");
+ g.eval("weakmap1 = new WeakMap()");
+ g.eval("weakmap2 = new WeakMap()");
+ g.eval(`obj = genObj('global-${names[i]}-object')`);
+ for (const j in [0, 1, 2]) {
+ g.eval(`weakmap${j}.set(genObj('global-${names[i]}-key}'), genObj("value"))`);
+ }
+ }
+
+ for (const i in globals) {
+ const g = globals[i];
+ for (const j in globals) {
+ for (const k in [0, 1, 2]) {
+ g.eval(`weakmap${k}.set(globals[${j}].obj, genObj('value-${i}-${j}'))`);
+ }
+ }
+ }
+
+ // Construct object keys to retrieve the weakmaps with.
+ for (const g of globals) {
+ g.eval(`plain = genObj("plain")`);
+ g.eval(`update = genObj("update")`);
+ g.eval(`remove = genObj("remove")`);
+ }
+
+ // Put the weakmaps in another WeakMap.
+ for (const g of globals) {
+ g.eval(`weakmaps = new WeakMap();
+ weakmaps.set(plain, weakmap0);
+ weakmaps.set(update, weakmap1);
+ weakmaps.set(remove, weakmap2);`);
+ }
+
+ // Eliminate the edges from the global to the object being used as a key. But
+ // assuming we want the key to be live (nothing else points to it), hide it
+ // behind another weakmap layer.
+ for (const g of globals) {
+ if (opt.live) {
+ g.eval("keyholder = genObj('key-holder')");
+ g.eval("weakmaps.set(keyholder, obj)");
+ }
+ g.eval("obj = null");
+ }
+
+ // If we want a layer of indirection, remove the edges from the globals to
+ // their weakmaps. But note that the original purpose of this test *wants*
+ // the weakmaps themselves to be visited early, so that gcWeakKeys will be
+ // populated with not-yet-marked keys and the barriers will need to update
+ // entries there.
+ if (opt.indirect) {
+ for (const g of globals) {
+ g.eval("weakmap0 = weakmap1 = weakmap2 = null");
+ }
+ }
+
+ // Start an incremental GC. TODO: need a zeal mode to yield before entering
+ // weak marking mode.
+ startgc(1);
+
+ // Do the mutations.
+ if (opt.live) {
+ for (const g of globals) {
+ g.eval("weakmaps.get(update).set(weakmaps.get(keyholder), genObj('val'))");
+ g.eval("weakmaps.get(remove).delete(weakmaps.get(keyholder))");
+ }
+ }
+
+ if (opt.nuke) {
+ for (const g of globals) {
+ if (g.name != 'main')
+ g.eval("nukeAllCCWs()");
+ }
+ }
+
+ // Finish the GC.
+ gc();
+}
+
+for (const live of [true, false]) {
+ for (const indirect of [true, false]) {
+ for (const nuke of [true, false]) {
+ stress({live, indirect, nuke});
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/gc/weak-marking-03.js b/js/src/jit-test/tests/gc/weak-marking-03.js
new file mode 100644
index 0000000000..f1f7c85c1b
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weak-marking-03.js
@@ -0,0 +1,696 @@
+// |jit-test| allow-unhandlable-oom
+
+// weakmap marking tests that use the testing mark queue to force an ordering
+// of marking.
+
+// We are carefully controlling the sequence of GC events.
+gczeal(0);
+
+// If a command-line parameter is given, use it as a substring restriction on
+// the tests to run.
+var testRestriction = scriptArgs[0];
+printErr(`testRestriction is ${testRestriction || '(run all tests)'}`);
+
+function runtest(func) {
+ if (testRestriction && ! func.name.includes(testRestriction)) {
+ print("\Skipping " + func.name);
+ } else {
+ print("\nRunning " + func.name);
+ func();
+ }
+}
+
+function reportMarks(prefix = "") {
+ const marks = getMarks();
+ const current = currentgc();
+ const markstr = marks.join("/");
+ print(`${prefix}[${current.incrementalState}/${current.sweepGroup}@${current.queuePos}] ${markstr}`);
+ return markstr;
+}
+
+function startGCMarking() {
+ startgc(100000);
+ while (gcstate() === "Prepare") {
+ gcslice(100000);
+ }
+}
+
+function purgeKey() {
+ const m = new WeakMap();
+ const vals = {};
+ vals.key = Object.create(null);
+ vals.val = Object.create(null);
+ m.set(vals.key, vals.val);
+
+ minorgc();
+
+ addMarkObservers([m, vals.key, vals.val]);
+
+ enqueueMark(m);
+ enqueueMark("yield");
+
+ enqueueMark(vals.key);
+ enqueueMark("yield");
+
+ vals.key = vals.val = null;
+
+ startGCMarking();
+ // getMarks() returns map/key/value
+ assertEq(getMarks().join("/"), "black/unmarked/unmarked",
+ "marked the map black");
+
+ gcslice(100000);
+ assertEq(getMarks().join("/"), "black/black/unmarked",
+ "key is now marked");
+
+ // Trigger purgeWeakKey: the key is in weakkeys (because it was unmarked when
+ // the map was marked), and now we're removing it.
+ m.delete(nondeterministicGetWeakMapKeys(m)[0]);
+
+ finishgc(); // Finish the GC
+ assertEq(getMarks().join("/"), "black/black/black",
+ "at end, value is marked too");
+
+ clearMarkQueue();
+ clearMarkObservers();
+}
+
+if (this.enqueueMark)
+ runtest(purgeKey);
+
+function removeKey() {
+ reportMarks("removeKey start: ");
+
+ const m = new WeakMap();
+ const vals = {};
+ vals.key = Object.create(null);
+ vals.val = Object.create(null);
+ m.set(vals.key, vals.val);
+
+ minorgc();
+
+ addMarkObservers([m, vals.key, vals.val]);
+
+ enqueueMark(m);
+ enqueueMark("yield");
+
+ startGCMarking();
+ reportMarks("first: ");
+ var marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "unmarked", "key not marked yet");
+ assertEq(marks[2], "unmarked", "value not marked yet");
+ m.delete(vals.key);
+
+ finishgc(); // Finish the GC
+ reportMarks("done: ");
+ marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "black", "key is black");
+ assertEq(marks[2], "black", "value is black");
+
+ // Do it again, but this time, remove all other roots.
+ m.set(vals.key, vals.val);
+ vals.key = vals.val = null;
+ startgc(10000);
+ while (gcstate() !== "Mark") {
+ gcslice(100000);
+ }
+ marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "unmarked", "key not marked yet");
+ assertEq(marks[2], "unmarked", "value not marked yet");
+
+ // This was meant to test the weakmap deletion barrier, which would remove
+ // the key from weakkeys. Unfortunately, JS-exposed WeakMaps now have a read
+ // barrier on lookup that marks the key, and deletion requires a lookup.
+ m.delete(nondeterministicGetWeakMapKeys(m)[0]);
+
+ finishgc();
+ marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "black", "key was blackened by lookup read barrier during deletion");
+ assertEq(marks[2], "black", "value is black because map and key are black");
+
+ clearMarkQueue();
+ clearMarkObservers();
+}
+
+if (this.enqueueMark)
+ runtest(removeKey);
+
+// Test:
+// 1. mark the map
+// - that inserts the delegate into weakKeys
+// 2. nuke the CCW key
+// - removes the delegate from weakKeys
+// 3. mark the key
+// 4. enter weak marking mode
+//
+// The problem it's attempting to recreate is that entering weak marking mode
+// will no longer mark the value, because there's no delegate to trigger it,
+// and the key was not added to weakKeys (because at the time the map was
+// scanned, the key had a delegate, so it was added to weakKeys instead.)
+function nukeMarking() {
+ const g1 = newGlobal({newCompartment: true});
+
+ const vals = {};
+ vals.map = new WeakMap();
+ vals.key = g1.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+ vals.val = null;
+ gc();
+
+ // Set up the sequence of marking events.
+ enqueueMark(vals.map);
+ enqueueMark("yield");
+ // We will nuke the key's delegate here.
+ enqueueMark(vals.key);
+ enqueueMark("enter-weak-marking-mode");
+
+ // Okay, run through the GC now.
+ startgc(1000000);
+ while (gcstate() !== "Mark") {
+ gcslice(100000);
+ }
+ assertEq(gcstate(), "Mark", "expected to yield after marking map");
+ // We should have marked the map and then yielded back here.
+ nukeCCW(vals.key);
+ // Finish up the GC.
+ gcslice();
+
+ clearMarkQueue();
+}
+
+if (this.enqueueMark)
+ runtest(nukeMarking);
+
+// Similar to the above, but trying to get a different failure:
+// - start marking
+// - find a map, add its key to ephemeronEdges
+// - nuke the key (and all other CCWs between the key -> delegate zones)
+// - when sweeping, we will no longer have any edges between the key
+// and delegate zones. So they will be placed in separate sweep groups.
+// - for this test, the delegate zone must be swept after the key zone
+// - make sure we don't try to mark back in the key zone (due to an
+// ephemeron edge) while sweeping the delegate zone. In a DEBUG build,
+// this would assert.
+function nukeMarkingSweepGroups() {
+ // Create g1 before host, because that will result in the right zone
+ // ordering to trigger the bug.
+ const g1 = newGlobal({newCompartment: true});
+ const host = newGlobal({newCompartment: true});
+ host.g1 = g1;
+ host.eval(`
+ const vals = {};
+ vals.map = new WeakMap();
+ vals.key = g1.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+ vals.val = null;
+ gc();
+
+ // Set up the sequence of marking events.
+ enqueueMark(vals.map);
+ enqueueMark("yield");
+ // We will nuke the key's delegate here.
+ enqueueMark(vals.key);
+ enqueueMark("enter-weak-marking-mode");
+
+ // Okay, run through the GC now.
+ startgc(1);
+ while (gcstate() === "Prepare") {
+ gcslice(100000);
+ }
+ assertEq(gcstate(), "Mark", "expected to yield after marking map");
+ // We should have marked the map and then yielded back here.
+ nukeAllCCWs();
+ // Finish up the GC.
+ while (gcstate() === "Mark") {
+ gcslice(1000);
+ }
+ gcslice();
+
+ clearMarkQueue();
+ `);
+}
+
+if (this.enqueueMark)
+ runtest(nukeMarkingSweepGroups);
+
+function transplantMarking() {
+ const g1 = newGlobal({newCompartment: true});
+
+ const vals = {};
+ vals.map = new WeakMap();
+ let {object, transplant} = transplantableObject();
+ vals.key = object;
+ object = null;
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+ vals.val = null;
+ gc();
+
+ // Set up the sequence of marking events.
+ enqueueMark(vals.map);
+ enqueueMark("yield");
+ // We will transplant the key here.
+ enqueueMark(vals.key);
+ enqueueMark("enter-weak-marking-mode");
+
+ // Okay, run through the GC now.
+ startgc(1000000);
+ while (gcstate() !== "Mark") {
+ gcslice(100000);
+ }
+ assertEq(gcstate(), "Mark", "expected to yield after marking map");
+ // We should have marked the map and then yielded back here.
+ transplant(g1);
+ // Finish up the GC.
+ gcslice();
+
+ clearMarkQueue();
+}
+
+if (this.enqueueMark)
+ runtest(transplantMarking);
+
+// 1. Mark the map
+// => add delegate to weakKeys
+// 2. Mark the delegate black
+// => do nothing because we are not in weak marking mode
+// 3. Mark the key gray
+// => mark value gray, not that we really care
+// 4. Enter weak marking mode
+// => black delegate darkens the key from gray to black
+function grayMarkingMapFirst() {
+ const g = newGlobal({newCompartment: true});
+ const vals = {};
+ vals.map = new WeakMap();
+ vals.key = g.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+
+ g.delegate = vals.key;
+ g.eval("dummy = Object.create(null)");
+ g.eval("grayRoot().push(delegate, dummy)");
+ addMarkObservers([vals.map, vals.key]);
+ g.addMarkObservers([vals.key, g.dummy]);
+ addMarkObservers([vals.val]);
+
+ gc();
+
+ enqueueMark(vals.map);
+ enqueueMark("yield"); // checkpoint 1
+
+ g.enqueueMark(vals.key);
+ enqueueMark("yield"); // checkpoint 2
+
+ vals.val = null;
+ vals.key = null;
+ g.delegate = null;
+ g.dummy = null;
+
+ const showmarks = () => {
+ print("[map,key,delegate,graydummy,value] marked " + JSON.stringify(getMarks()));
+ };
+
+ print("Starting incremental GC");
+ startGCMarking();
+ // Checkpoint 1, after marking map
+ showmarks();
+ var marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "unmarked", "key is not marked yet");
+ assertEq(marks[2], "unmarked", "delegate is not marked yet");
+
+ gcslice(100000);
+ // Checkpoint 2, after marking delegate
+ showmarks();
+ marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "unmarked", "key is not marked yet");
+ assertEq(marks[2], "black", "delegate is black");
+
+ gcslice();
+ // GC complete. Key was marked black (b/c of delegate), then gray marking saw
+ // it was already black and skipped it.
+ showmarks();
+ marks = getMarks();
+ assertEq(marks[0], "black", "map is black");
+ assertEq(marks[1], "black", "delegate marked key black because of weakmap");
+ assertEq(marks[2], "black", "delegate is still black");
+ assertEq(marks[3], "gray", "basic gray marking is working");
+ assertEq(marks[4], "black", "black map + black delegate => black value");
+
+ clearMarkQueue();
+ clearMarkObservers();
+ grayRoot().length = 0;
+ g.eval("grayRoot().length = 0");
+}
+
+if (this.enqueueMark)
+ runtest(grayMarkingMapFirst);
+
+function grayMarkingMapLast() {
+ const g = newGlobal({newCompartment: true});
+ const vals = {};
+ vals.map = new WeakMap();
+ vals.key = g.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+
+ vals.map2 = new WeakMap();
+ vals.key2 = g.eval("Object.create(null)");
+ vals.val2 = Object.create(null);
+ vals.map2.set(vals.key2, vals.val2);
+
+ g.delegate = vals.key;
+ g.eval("grayRoot().push(delegate)");
+ addMarkObservers([vals.map, vals.key]);
+ g.addMarkObservers([vals.key]);
+ addMarkObservers([vals.val]);
+
+ grayRoot().push(vals.key2);
+ addMarkObservers([vals.map2, vals.key2]);
+ g.addMarkObservers([vals.key2]);
+ addMarkObservers([vals.val2]);
+
+ const labels = ["map", "key", "delegate", "value", "map2", "key2", "delegate2", "value2"];
+
+ gc();
+
+ g.enqueueMark(vals.key);
+ g.enqueueMark(vals.key2);
+ enqueueMark("yield"); // checkpoint 1
+
+ vals.val = null;
+ vals.key = null;
+ g.delegate = null;
+
+ vals.map2 = null; // Important! Second map is never marked, keeps nothing alive.
+ vals.key2 = null;
+ vals.val2 = null;
+ g.delegate2 = null;
+
+ const labeledMarks = () => {
+ const info = {};
+ const marks = getMarks();
+ for (let i = 0; i < labels.length; i++)
+ info[labels[i]] = marks[i];
+ return info;
+ };
+
+ const showmarks = () => {
+ print("Marks:");
+ for (const [label, mark] of Object.entries(labeledMarks()))
+ print(` ${label}: ${mark}`);
+ };
+
+ print("Starting incremental GC");
+ startGCMarking();
+ // Checkpoint 1, after marking key
+ showmarks();
+ var marks = labeledMarks();
+ assertEq(marks.map, "unmarked", "map is unmarked");
+ assertEq(marks.key, "unmarked", "key is not marked yet");
+ assertEq(marks.delegate, "black", "delegate is black");
+ assertEq(marks.map2, "unmarked", "map2 is unmarked");
+ assertEq(marks.key2, "unmarked", "key2 is not marked yet");
+ assertEq(marks.delegate2, "black", "delegate2 is black");
+
+ gcslice();
+ // GC complete. When entering weak marking mode, black delegate propagated to
+ // key.
+ showmarks();
+ marks = labeledMarks();
+ assertEq(marks.map, "black", "map is black");
+ assertEq(marks.key, "black", "delegate marked key black because of weakmap");
+ assertEq(marks.delegate, "black", "delegate is still black");
+ assertEq(marks.value, "black", "black map + black delegate => black value");
+ assertEq(marks.map2, "dead", "map2 is dead");
+ assertEq(marks.key2, "gray", "key2 marked gray, map2 had no effect");
+ assertEq(marks.delegate2, "black", "delegate artificially marked black via mark queue");
+ assertEq(marks.value2, "dead", "dead map + black delegate => dead value");
+
+ clearMarkQueue();
+ clearMarkObservers();
+ grayRoot().length = 0;
+ g.eval("grayRoot().length = 0");
+
+ return vals; // To prevent the JIT from optimizing out vals.
+}
+
+if (this.enqueueMark)
+ runtest(grayMarkingMapLast);
+
+function grayMapKey() {
+ const vals = {};
+ vals.m = new WeakMap();
+ vals.key = Object.create(null);
+ vals.val = Object.create(null);
+ vals.m.set(vals.key, vals.val);
+
+ // Maps are allocated black, so we won't be able to mark it gray during the
+ // first GC.
+ gc();
+
+ addMarkObservers([vals.m, vals.key, vals.val]);
+
+ // Wait until we can mark gray (ie, sweeping). Mark the map gray and yield.
+ // This should happen all in one slice.
+ enqueueMark("set-color-gray");
+ enqueueMark(vals.m);
+ enqueueMark("unset-color");
+ enqueueMark("yield");
+
+ // Make the weakmap no longer reachable from the roots, so we can mark it
+ // gray.
+ vals.m = null;
+
+ enqueueMark(vals.key);
+ enqueueMark("yield");
+
+ vals.key = vals.val = null;
+
+ startGCMarking();
+ assertEq(getMarks().join("/"), "gray/unmarked/unmarked",
+ "marked the map gray");
+
+ gcslice(100000);
+ assertEq(getMarks().join("/"), "gray/black/unmarked",
+ "key is now marked black");
+
+ finishgc(); // Finish the GC
+
+ assertEq(getMarks().join("/"), "gray/black/gray",
+ "at end: black/gray => gray");
+
+ clearMarkQueue();
+ clearMarkObservers();
+}
+
+if (this.enqueueMark)
+ runtest(grayMapKey);
+
+function grayKeyMap() {
+ const vals = {};
+ vals.m = new WeakMap();
+ vals.key = Object.create(null);
+ vals.val = Object.create(null);
+ vals.m.set(vals.key, vals.val);
+
+ addMarkObservers([vals.m, vals.key, vals.val]);
+
+ enqueueMark(vals.key);
+ enqueueMark("yield");
+
+ // Wait until we are gray marking.
+ enqueueMark("set-color-gray");
+ enqueueMark(vals.m);
+ enqueueMark("unset-color");
+ enqueueMark("yield");
+
+ enqueueMark("set-color-black");
+ enqueueMark(vals.m);
+ enqueueMark("unset-color");
+
+ // Make the weakmap no longer reachable from the roots, so we can mark it
+ // gray.
+ vals.m = null;
+
+ vals.key = vals.val = null;
+
+ // Only mark this zone, to avoid interference from other tests that may have
+ // created additional zones.
+ schedulezone(vals);
+
+ startGCMarking();
+ // getMarks() returns map/key/value
+ reportMarks("1: ");
+ assertEq(getMarks().join("/"), "unmarked/black/unmarked",
+ "marked key black");
+
+ // We always yield before sweeping (in the absence of zeal), so we will see
+ // the unmarked state another time.
+ gcslice(100000);
+ reportMarks("2: ");
+ assertEq(getMarks().join("/"), "unmarked/black/unmarked",
+ "marked key black, yield before sweeping");
+
+ gcslice(100000);
+ reportMarks("3: ");
+ assertEq(getMarks().join("/"), "gray/black/gray",
+ "marked the map gray, which marked the value when map scanned");
+
+ finishgc(); // Finish the GC
+ reportMarks("4: ");
+ assertEq(getMarks().join("/"), "black/black/black",
+ "further marked the map black, so value should also be blackened");
+
+ clearMarkQueue();
+ clearMarkObservers();
+}
+
+if (this.enqueueMark)
+ runtest(grayKeyMap);
+
+// Cause a key to be marked black *during gray marking*, by first marking a
+// delegate black, then marking the map and key gray. When the key is scanned,
+// it should be seen to be a CCW of a black delegate and so should itself be
+// marked black.
+//
+// The bad behavior being prevented is:
+//
+// 1. You wrap an object in a CCW and use it as a weakmap key to some
+// information.
+// 2. You keep a strong reference to the object (in its compartment).
+// 3. The only references to the CCW are gray, and are in fact part of a cycle.
+// 4. The CC runs and discards the CCW.
+// 5. You look up the object in the weakmap again. This creates a new wrapper
+// to use as a key. It is not in the weakmap, so the information you stored
+// before is not found. (It may have even been collected, if you had no
+// other references to it.)
+//
+function blackDuringGray() {
+ const g = newGlobal({newCompartment: true});
+ const vals = {};
+ vals.map = new WeakMap();
+ vals.key = g.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+
+ g.delegate = vals.key;
+ addMarkObservers([vals.map, vals.key]);
+ g.addMarkObservers([vals.key]);
+ addMarkObservers([vals.val]);
+ // Mark observers: map, key, delegate, value
+
+ gc();
+
+ g.enqueueMark(vals.key); // Mark the delegate black
+ enqueueMark("yield"); // checkpoint 1
+
+ // Mark the map gray. This will scan through all entries, find our key, and
+ // mark it black because its delegate is black.
+ enqueueMark("set-color-gray");
+ enqueueMark(vals.map); // Mark the map gray
+
+ vals.map = null;
+ vals.val = null;
+ vals.key = null;
+ g.delegate = null;
+
+ const showmarks = () => {
+ print("[map,key,delegate,value] marked " + JSON.stringify(getMarks()));
+ };
+
+ print("Starting incremental GC");
+ startGCMarking();
+ // Checkpoint 1, after marking delegate black
+ showmarks();
+ var marks = getMarks();
+ assertEq(marks[0], "unmarked", "map is not marked yet");
+ assertEq(marks[1], "unmarked", "key is not marked yet");
+ assertEq(marks[2], "black", "delegate is black");
+ assertEq(marks[3], "unmarked", "values is not marked yet");
+
+ finishgc();
+ showmarks();
+ marks = getMarks();
+ assertEq(marks[0], "gray", "map is gray");
+ assertEq(marks[1], "gray", "gray map + black delegate should mark key gray");
+ assertEq(marks[2], "black", "delegate is still black");
+ assertEq(marks[3], "gray", "gray map + gray key => gray value");
+
+ clearMarkQueue();
+ clearMarkObservers();
+ grayRoot().length = 0;
+ g.eval("grayRoot().length = 0");
+}
+
+if (this.enqueueMark)
+ runtest(blackDuringGray);
+
+// Same as above, except relying on the implicit edge from delegate -> key.
+function blackDuringGrayImplicit() {
+ const g = newGlobal({newCompartment: true});
+ const vals = {};
+ vals.map = new WeakMap();
+ vals.key = g.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.map.set(vals.key, vals.val);
+
+ g.delegate = vals.key;
+ addMarkObservers([vals.map, vals.key]);
+ g.addMarkObservers([vals.key]);
+ addMarkObservers([vals.val]);
+ // Mark observers: map, key, delegate, value
+
+ gc();
+
+ // Mark the map gray. This will scan through all entries, find our key, and
+ // add implicit edges from delegate -> key and delegate -> value.
+ enqueueMark("set-color-gray");
+ enqueueMark(vals.map); // Mark the map gray
+ enqueueMark("yield"); // checkpoint 1
+
+ enqueueMark("set-color-black");
+ g.enqueueMark(vals.key); // Mark the delegate black, propagating to key.
+
+ vals.map = null;
+ vals.val = null;
+ vals.key = null;
+ g.delegate = null;
+
+ const showmarks = () => {
+ print("[map,key,delegate,value] marked " + JSON.stringify(getMarks()));
+ };
+
+ print("Starting incremental GC");
+ startGCMarking();
+ // Checkpoint 1, after marking map gray
+ showmarks();
+ var marks = getMarks();
+ assertEq(marks[0], "gray", "map is gray");
+ assertEq(marks[1], "unmarked", "key is not marked yet");
+ assertEq(marks[2], "unmarked", "delegate is not marked yet");
+ assertEq(marks[3], "unmarked", "value is not marked yet");
+
+ finishgc();
+ showmarks();
+ marks = getMarks();
+ assertEq(marks[0], "gray", "map is gray");
+ assertEq(marks[1], "gray", "gray map + black delegate should mark key gray");
+ assertEq(marks[2], "black", "delegate is black");
+ assertEq(marks[3], "gray", "gray map + gray key => gray value via delegate -> value");
+
+ clearMarkQueue();
+ clearMarkObservers();
+ grayRoot().length = 0;
+ g.eval("grayRoot().length = 0");
+}
+
+if (this.enqueueMark)
+ runtest(blackDuringGrayImplicit);
diff --git a/js/src/jit-test/tests/gc/weak-marking-varying.js b/js/src/jit-test/tests/gc/weak-marking-varying.js
new file mode 100644
index 0000000000..76dc39dfb2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weak-marking-varying.js
@@ -0,0 +1,90 @@
+// |jit-test| allow-unhandlable-oom
+
+// Crash test. Try lots of different combinations of mark colors and
+// sequencing, and rely on the in-code asserts to detect problems.
+
+// This test requires precise control over GC timing. The SM(cgc) job will fail
+// without this, because startgc() asserts if an incremental GC is already in
+// progress.
+gczeal(0);
+
+function global(where) {
+ if (where == 'same-realm')
+ return globalThis;
+ if (where == 'same-compartment')
+ return newGlobal();
+ if (where == 'same-zone')
+ newGlobal({sameZoneAs: {}});
+ return newGlobal({newCompartment: true});
+}
+
+function varying(mapColor, keyColor, delegateColor, order, where) {
+ const vals = {};
+ const g = global(where);
+
+ vals.m = new WeakMap();
+ vals.key = g.eval("Object.create(null)");
+ vals.val = Object.create(null);
+ vals.m.set(vals.key, vals.val);
+
+ addMarkObservers([vals.m, vals.key]);
+ g.delegate = vals.key;
+ g.eval('addMarkObservers([delegate])');
+ g.delegate = null;
+ addMarkObservers([vals.val]);
+
+ for (const action of order) {
+ if (action == 'key' && ['black', 'gray'].includes(keyColor)) {
+ enqueueMark(`set-color-${keyColor}`);
+ enqueueMark(vals.key);
+ enqueueMark("unset-color");
+ enqueueMark("yield");
+ } else if (action == 'map' && ['black', 'gray'].includes(mapColor)) {
+ enqueueMark(`set-color-${mapColor}`);
+ enqueueMark(vals.m);
+ enqueueMark("drain");
+ enqueueMark("unset-color");
+ enqueueMark("yield");
+ } else if (action == 'delegate' && ['black', 'gray'].includes(delegateColor)) {
+ g.delegate = vals.key;
+ g.eval(`enqueueMark("set-color-${delegateColor}")`);
+ g.eval('enqueueMark(delegate)');
+ g.eval('enqueueMark("unset-color")');
+ g.eval('enqueueMark("yield")');
+ g.delegate = null;
+ }
+ }
+
+ vals.m = vals.key = vals.val = null;
+
+ if (delegateColor == 'uncollected')
+ schedulezone({});
+ startgc(100000);
+ print(' ' + getMarks().join("/"));
+ gcslice(100000);
+ print(' ' + getMarks().join("/"));
+ finishgc();
+ print(' ' + getMarks().join("/"));
+
+ clearMarkQueue();
+ clearMarkObservers();
+}
+
+if (this.enqueueMark) {
+ for (const mapColor of ['gray', 'black']) {
+ for (const keyColor of ['gray', 'black', 'unmarked']) {
+ for (const delegateColor of ['gray', 'black', 'unmarked', 'uncollected']) {
+ for (const order of [['map', 'key'],
+ ['key', 'map'],
+ ['map', 'delegate'],
+ ['delegate', 'map']])
+ {
+ for (const where of ['same-realm', 'same-compartment', 'same-zone', 'other-zone']) {
+ print(`\nRunning variant map/key/delegate=${mapColor}/${keyColor}/${delegateColor}, key is ${where}, order: ${order.join(" ")}`);
+ varying(mapColor, keyColor, delegateColor, order, where);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/gc/weakRef_in_promise.js b/js/src/jit-test/tests/gc/weakRef_in_promise.js
new file mode 100644
index 0000000000..2448ebcd8a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakRef_in_promise.js
@@ -0,0 +1,18 @@
+// https://github.com/tc39/proposal-weakrefs/issues/39
+// Weakref should keep the target until the end of current Job, that includes
+// microtask(Promise).
+let wr;
+{
+ let obj = {};
+ wr = new WeakRef(obj);
+ obj = null;
+}
+// obj is out of block scope now, should be GCed.
+
+gc();
+
+assertEq(undefined == wr.deref(), false);
+Promise.resolve().then(() => {
+ assertEq(undefined == wr.deref(), false);
+});
+
diff --git a/js/src/jit-test/tests/gc/weakRefs-basic.js b/js/src/jit-test/tests/gc/weakRefs-basic.js
new file mode 100644
index 0000000000..6900ae0c52
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakRefs-basic.js
@@ -0,0 +1,81 @@
+assertEq('WeakRef' in this, true);
+
+function checkPropertyDescriptor(obj, property, writable, enumerable,
+ configurable) {
+ let desc = Object.getOwnPropertyDescriptor(obj, property);
+ assertEq(typeof desc, "object");
+ assertEq(desc.writable, writable);
+ assertEq(desc.enumerable, enumerable);
+ assertEq(desc.configurable, configurable);
+}
+
+function assertThrowsTypeError(thunk) {
+ let error;
+ try {
+ thunk();
+ } catch (e) {
+ error = e;
+ }
+ assertEq(error instanceof TypeError, true);
+}
+
+assertEq(typeof this.WeakRef, "function");
+
+// https://tc39.es/proposal-weakrefs/
+
+// 1.1.1.1
+// If NewTarget is undefined, throw a TypeError exception.
+assertThrowsTypeError(() => new WeakRef());
+
+// 1.1.1.2
+// If Type(target) is not Object, throw a TypeError exception.
+assertThrowsTypeError(() => new WeakRef(1));
+assertThrowsTypeError(() => new WeakRef(true));
+assertThrowsTypeError(() => new WeakRef("string"));
+assertThrowsTypeError(() => new WeakRef(Symbol()));
+assertThrowsTypeError(() => new WeakRef(null));
+assertThrowsTypeError(() => new WeakRef(undefined));
+new WeakRef({});
+
+// 1.2
+// The WeakRef constructor has a [[Prototype]] internal slot whose value is the
+// intrinsic object %FunctionPrototype%.
+assertEq(Object.getPrototypeOf(WeakRef), Function.prototype);
+
+// 1.2.1
+// The initial value of WeakRef.prototype is the intrinsic %WeakRefPrototype%
+// object.
+// This property has the attributes { [[Writable]]: false, [[Enumerable]]: false
+// , [[Configurable]]: false }.
+checkPropertyDescriptor(WeakRef, 'prototype', false, false, false);
+
+// 1.3
+// The WeakRef prototype object has a [[Prototype]] internal slot whose value is
+// the intrinsic object %ObjectPrototype%.
+let proto = WeakRef.prototype;
+assertEq(Object.getPrototypeOf(proto), Object.prototype);
+
+// 1.3.1
+// The initial value of WeakRef.prototype.constructor is the intrinsic object
+// %WeakRef%.
+assertEq(proto.constructor, WeakRef);
+
+// 1.3.2
+// WeakRef.prototype.deref ()
+assertEq(proto.hasOwnProperty('deref'), true);
+assertEq(typeof proto.deref, 'function');
+
+// 1.3.3
+// The initial value of the @@toStringTag property is the String value
+// "WeakRef".
+// This property has the attributes { [[Writable]]: false, [[Enumerable]]: false,
+// [[Configurable]]: true }.
+assertEq(proto[Symbol.toStringTag], "WeakRef");
+checkPropertyDescriptor(proto, Symbol.toStringTag, false, false, true);
+
+// 1.4
+// WeakRef instances are ordinary objects that inherit properties from the
+// WeakRef prototype
+let weakRef = new WeakRef({});
+assertEq(Object.getPrototypeOf(weakRef), proto);
+
diff --git a/js/src/jit-test/tests/gc/weakRefs.js b/js/src/jit-test/tests/gc/weakRefs.js
new file mode 100644
index 0000000000..cb7dcb829a
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakRefs.js
@@ -0,0 +1,122 @@
+// https://tc39.es/proposal-weakrefs/#sec-keepduringjob
+// When the abstract operation KeepDuringJob is called with a target object
+// reference, it adds the target to an identity Set that will point strongly at
+// the target until the end of the current Job.
+//
+// https://tc39.es/proposal-weakrefs/#sec-weakref-invariants
+// When WeakRef.prototype.deref is called, the referent (if it's not already
+// dead) is kept alive so that subsequent, synchronous accesses also return the
+// object.
+
+function testSameCompartmentWeakRef(
+ targetReachable,
+ weakRefReachable) {
+
+ let target = {};
+
+ let weakref = new WeakRef(target);
+ assertEq(weakref.deref(), target);
+
+ if (!targetReachable) {
+ target = undefined;
+ }
+
+ if (!weakRefReachable) {
+ weakRef = undefined;
+ }
+
+ clearKeptObjects();
+ gc();
+
+ if (weakRefReachable) {
+ if (targetReachable) {
+ assertEq(weakref.deref(), target);
+ } else {
+ assertEq(weakref.deref(), undefined);
+ }
+ }
+}
+
+let serial = 0;
+
+function testCrossCompartmentWeakRef(
+ targetReachable,
+ weakRefReachable,
+ collectTargetZone,
+ collectWeakRefZone,
+ sameZone) {
+
+ gc();
+
+ let id = serial++;
+ let global = newGlobal(sameZone ? {sameZoneAs: this} : {newCompartment: true});
+ global.eval('var target = {};');
+ global.target.id = id;
+
+ let weakref = new WeakRef(global.target);
+ assertEq(weakref.deref(), global.target);
+
+ if (!targetReachable) {
+ global.target = undefined;
+ }
+
+ if (!weakRefReachable) {
+ weakRef = undefined;
+ }
+
+ if (collectTargetZone || collectWeakRefZone) {
+ clearKeptObjects();
+
+ if (collectTargetZone) {
+ schedulezone(global);
+ }
+ if (collectWeakRefZone) {
+ schedulezone(this);
+ }
+
+ // Incremental GC so we use sweep groups. Shrinking GC to test updating
+ // pointers.
+ startgc(1, 'shrinking');
+ while (gcstate() !== 'NotActive') {
+ gcslice(1000, {dontStart: true});
+ }
+ }
+
+ if (!(collectWeakRefZone && !weakRefReachable)) {
+ if (collectTargetZone && !targetReachable) {
+ assertEq(weakref.deref(), undefined);
+ } else if (targetReachable) {
+ assertEq(weakref.deref(), global.target);
+ } else {
+ // Target is not strongly reachable but hasn't been collected yet. We
+ // can get it back through deref() but must check it based on properties.
+ assertEq(weakref.deref() !== undefined, true);
+ assertEq(weakref.deref().id, id);
+ }
+ }
+}
+
+gczeal(0);
+
+for (let targetReachable of [true, false]) {
+ for (let weakRefReachable of [true, false]) {
+ testSameCompartmentWeakRef(targetReachable, weakRefReachable);
+ }
+}
+
+for (let targetReachable of [true, false]) {
+ for (let weakRefReachable of [true, false]) {
+ for (let collectTargetZone of [true, false]) {
+ for (let collectWeakRefZone of [true, false]) {
+ for (let sameZone of [true, false]) {
+ if (sameZone && (collectTargetZone != collectWeakRefZone)) {
+ continue;
+ }
+
+ testCrossCompartmentWeakRef(targetReachable, weakRefReachable,
+ collectTargetZone, collectWeakRefZone, sameZone);
+ }
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/gc/weakmap-expose.js b/js/src/jit-test/tests/gc/weakmap-expose.js
new file mode 100644
index 0000000000..45ebc7d5a9
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakmap-expose.js
@@ -0,0 +1,43 @@
+// Test that WeakMap.get() doesn't return a gray GC thing.
+
+function checkNotGray(value) {
+ // Assigning a gray GC thing to an object propery will assert.
+ let test = {};
+ test.property = value;
+}
+
+// 1. Make a black WeakMap with a gray key and gray value.
+
+gczeal(0);
+
+let key = {};
+let value = {};
+
+let map = new WeakMap();
+map.set(key, value);
+
+let gray = grayRoot();
+gray.key = key;
+
+addMarkObservers([map, key, value]);
+
+gray = null;
+key = null;
+value = null;
+
+gc();
+
+let marks = getMarks();
+assertEq(marks[0], "black");
+assertEq(marks[1], "gray");
+assertEq(marks[2], "gray");
+
+// 2. Get our key back, which will expose it and mark it black.
+
+key = nondeterministicGetWeakMapKeys(map)[0];
+checkNotGray(key);
+
+// 3. Look up the value in the map and check it's not gray.
+
+value = map.get(key);
+checkNotGray(value);
diff --git a/js/src/jit-test/tests/gc/weakmap-nursery-value.js b/js/src/jit-test/tests/gc/weakmap-nursery-value.js
new file mode 100644
index 0000000000..9c61b56632
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakmap-nursery-value.js
@@ -0,0 +1,35 @@
+// Bug 1715471
+
+// This test relies on triggering GC at precise points. GC zeal modes
+// interfere with this.
+gczeal(0);
+
+// This test requires enqueueMark, which is only available in a debugging
+// build.
+if (!this.enqueueMark) quit(0);
+
+var wm = new WeakMap();
+
+// Force materialization of the map struct. Otherwise it would be allocated
+// black during the incremental GC when we do wm.set.
+wm.set({}, {});
+
+var tenuredKey = Object.create(null);
+gc(); // Tenure the WeakMap and key.
+
+// Enter the GC, since otherwise the minor GC at the beginning would tenure our
+// value before it had a chance to be entered into the ephemeron edge list.
+startgc(1);
+while (gcstate() === "Prepare") gcslice(1);
+
+// Create a WeakMap entry with a value in the nursery.
+var nurseryValue = Object.create(null);
+wm.set(tenuredKey, nurseryValue);
+
+// We want to mark the weakmap first, before the key or value are marked, so
+// that the tenuredKey -> nurseryValue edge will be added to the ephemeron edge
+// table.
+enqueueMark(wm);
+
+gcslice(1000);
+minorgc();
diff --git a/js/src/jit-test/tests/gc/weakmark-remap.js b/js/src/jit-test/tests/gc/weakmark-remap.js
new file mode 100644
index 0000000000..f3e8e8f995
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakmark-remap.js
@@ -0,0 +1,16 @@
+g1 = newGlobal({ sameZoneAs: this });
+
+// Turn on the object metadata builder because it will create internal weakmap
+// entries for all objects created from here on.
+enableShellAllocationMetadataBuilder();
+
+// Construct a CCW for g1.Object, which will internally create the object
+// metadata table and add an entry to it.
+g1.Object;
+
+// Begin an incremental GC
+gczeal(0);
+startgc(1);
+
+// Call a scary function that does weird stuff.
+recomputeWrappers();
diff --git a/js/src/jit-test/tests/gc/weakmark-remap2.js b/js/src/jit-test/tests/gc/weakmark-remap2.js
new file mode 100644
index 0000000000..19e7338b5d
--- /dev/null
+++ b/js/src/jit-test/tests/gc/weakmark-remap2.js
@@ -0,0 +1,21 @@
+gczeal(0);
+
+var g1 = newGlobal({ sameZoneAs: this });
+
+// Turn on the object metadata builder because it will create internal weakmap
+// entries for all objects created from here on.
+enableShellAllocationMetadataBuilder();
+
+// Construct a pair of error objects. Perhaps this fills out the metadata table?
+new Error();
+new Error();
+
+// Construct a CCW for g1.Object, which will internally create the object
+// metadata table and add an entry to it.
+try { g1.Object } catch(e) { }
+
+// Begin an incremental GC
+startgc(1);
+
+// Call a scary function that does weird stuff.
+recomputeWrappers();