DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (6863f516ba38)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef vm_Debugger_h
#define vm_Debugger_h

#include "mozilla/DoublyLinkedList.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Range.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Vector.h"

#include "ds/TraceableFifo.h"
#include "gc/Barrier.h"
#include "gc/WeakMap.h"
#include "js/Debug.h"
#include "js/GCVariant.h"
#include "js/HashTable.h"
#include "js/Promise.h"
#include "js/Utility.h"
#include "js/Wrapper.h"
#include "proxy/DeadObjectProxy.h"
#include "vm/GlobalObject.h"
#include "vm/JSContext.h"
#include "vm/Realm.h"
#include "vm/SavedStacks.h"
#include "vm/Stack.h"

namespace js {

/**
 * Tells how the JS engine should resume debuggee execution after firing a
 * debugger hook.  Most debugger hooks get to choose how the debuggee proceeds;
 * see js/src/doc/Debugger/Conventions.md under "Resumption Values".
 *
 * Debugger::processHandlerResult() translates between JavaScript values and
 * this enum.
 *
 * The values `ResumeMode::Throw` and `ResumeMode::Return` are always
 * associated with a value (the exception value or return value). Sometimes
 * this is represented as an explicit `JS::Value` variable or parameter,
 * declared alongside the `ResumeMode`. In other cases, especially when
 * ResumeMode is used as a return type (as in Debugger::onEnterFrame), the
 * value is stashed in `cx`'s pending exception slot or the topmost frame's
 * return value slot.
 */
enum class ResumeMode {
  /**
   * The debuggee should continue unchanged.
   *
   * This corresponds to a resumption value of `undefined`.
   */
  Continue,

  /**
   * Throw an exception in the debuggee.
   *
   * This corresponds to a resumption value of `{throw: <value>}`.
   */
  Throw,

  /**
   * Terminate the debuggee, as if it had been cancelled via the "slow
   * script" ribbon.
   *
   * This corresponds to a resumption value of `null`.
   */
  Terminate,

  /**
   * Force the debuggee to return from the current frame.
   *
   * This corresponds to a resumption value of `{return: <value>}`.
   */
  Return,
};

class AbstractGeneratorObject;
class Breakpoint;
class DebuggerMemory;
class PromiseObject;
class ScriptedOnStepHandler;
class ScriptedOnPopHandler;
class WasmInstanceObject;

typedef HashSet<WeakHeapPtrGlobalObject,
                MovableCellHasher<WeakHeapPtrGlobalObject>, ZoneAllocPolicy>
    WeakGlobalObjectSet;

#ifdef DEBUG
extern void CheckDebuggeeThing(JSScript* script, bool invisibleOk);

extern void CheckDebuggeeThing(LazyScript* script, bool invisibleOk);

extern void CheckDebuggeeThing(JSObject* obj, bool invisibleOk);
#endif

/*
 * A weakmap from GC thing keys to JSObject values that supports the keys being
 * in different compartments to the values. All values must be in the same
 * compartment.
 *
 * The purpose of this is to allow the garbage collector to easily find edges
 * from debuggee object compartments to debugger compartments when calculating
 * the compartment groups.  Note that these edges are the inverse of the edges
 * stored in the cross compartment map.
 *
 * The current implementation results in all debuggee object compartments being
 * swept in the same group as the debugger.  This is a conservative approach,
 * and compartments may be unnecessarily grouped, however it results in a
 * simpler and faster implementation.
 *
 * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
 * debugger compartments. If it is false, we assert that such entries are never
 * created.
 *
 * Also note that keys in these weakmaps can be in any compartment, debuggee or
 * not, because they are not deleted when a compartment is no longer a
 * debuggee: the values need to maintain object identity across add/remove/add
 * transitions. (Frames are an exception to the rule. Existing Debugger.Frame
 * objects are killed when debugging is disabled for their compartment, and if
 * it's re-enabled later, new Frame objects are created.)
 */
template <class UnbarrieredKey, bool InvisibleKeysOk = false>
class DebuggerWeakMap
    : private WeakMap<HeapPtr<UnbarrieredKey>, HeapPtr<JSObject*>> {
 private:
  typedef HeapPtr<UnbarrieredKey> Key;
  typedef HeapPtr<JSObject*> Value;

  typedef HashMap<JS::Zone*, uintptr_t, DefaultHasher<JS::Zone*>,
                  ZoneAllocPolicy>
      CountMap;

  JS::Compartment* compartment;

 public:
  typedef WeakMap<Key, Value> Base;

  explicit DebuggerWeakMap(JSContext* cx)
      : Base(cx), compartment(cx->compartment()) {}

 public:
  // Expose those parts of HashMap public interface that are used by Debugger
  // methods.

  using Entry = typename Base::Entry;
  using Ptr = typename Base::Ptr;
  using AddPtr = typename Base::AddPtr;
  using Range = typename Base::Range;
  using Enum = typename Base::Enum;
  using Lookup = typename Base::Lookup;

  // Expose WeakMap public interface.

  using Base::all;
  using Base::has;
  using Base::lookup;
  using Base::lookupForAdd;
  using Base::remove;
  using Base::trace;

  template <typename KeyInput, typename ValueInput>
  bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
    MOZ_ASSERT(v->compartment() == this->compartment);
#ifdef DEBUG
    CheckDebuggeeThing(k, InvisibleKeysOk);
#endif
    MOZ_ASSERT(!Base::has(k));
    bool ok = Base::relookupOrAdd(p, k, v);
    return ok;
  }

  // Remove entries whose keys satisfy the given predicate.
  template <typename Predicate>
  void removeIf(Predicate test) {
    for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
      JSObject* key = e.front().key();
      if (test(key)) {
        e.removeFront();
      }
    }
  }

 public:
  template <void(traceValueEdges)(JSTracer*, JSObject*)>
  void traceCrossCompartmentEdges(JSTracer* tracer) {
    for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
      traceValueEdges(tracer, e.front().value());
      Key key = e.front().key();
      TraceEdge(tracer, &key, "Debugger WeakMap key");
      if (key != e.front().key()) {
        e.rekeyFront(key);
      }
      key.unsafeSet(nullptr);
    }
  }

 private:
#ifdef JS_GC_ZEAL
  // Let the weak map marking verifier know that this map can
  // contain keys in other zones.
  virtual bool allowKeysInOtherZones() const override { return true; }
#endif
};

class LeaveDebuggeeNoExecute;

// Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
// certain whitelisted operations to execute code.
//
// WARNING
// WARNING Do not use this unless you know what you are doing!
// WARNING
class AutoSuppressDebuggeeNoExecuteChecks {
  EnterDebuggeeNoExecute** stack_;
  EnterDebuggeeNoExecute* prev_;

 public:
  explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext* cx) {
    stack_ = &cx->noExecuteDebuggerTop.ref();
    prev_ = *stack_;
    *stack_ = nullptr;
  }

  ~AutoSuppressDebuggeeNoExecuteChecks() {
    MOZ_ASSERT(!*stack_);
    *stack_ = prev_;
  }
};

class MOZ_RAII EvalOptions {
  JS::UniqueChars filename_;
  unsigned lineno_ = 1;

 public:
  EvalOptions() = default;
  ~EvalOptions() = default;
  const char* filename() const { return filename_.get(); }
  unsigned lineno() const { return lineno_; }
  MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
  void setLineno(unsigned lineno) { lineno_ = lineno; }
};

/*
 * Env is the type of what ES5 calls "lexical environments" (runtime activations
 * of lexical scopes). This is currently just JSObject, and is implemented by
 * CallObject, LexicalEnvironmentObject, and WithEnvironmentObject, among
 * others--but environments and objects are really two different concepts.
 */
typedef JSObject Env;

// One of a real JSScript, a real LazyScript, or synthesized.
//
// If synthesized, the referent is one of the following:
//
//   1. A WasmInstanceObject, denoting a synthesized toplevel wasm module
//      script.
//   2. A wasm JSFunction, denoting a synthesized wasm function script.
//      NYI!
typedef mozilla::Variant<JSScript*, LazyScript*, WasmInstanceObject*>
    DebuggerScriptReferent;

// Either a ScriptSourceObject, for ordinary JS, or a WasmInstanceObject,
// denoting the synthesized source of a wasm module.
typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*>
    DebuggerSourceReferent;

class Debugger : private mozilla::LinkedListElement<Debugger> {
  friend class Breakpoint;
  friend class DebuggerMemory;
  friend struct JSRuntime::GlobalObjectWatchersLinkAccess<Debugger>;
  friend class SavedStacks;
  friend class ScriptedOnStepHandler;
  friend class ScriptedOnPopHandler;
  friend class mozilla::LinkedListElement<Debugger>;
  friend class mozilla::LinkedList<Debugger>;
  friend bool(::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
  friend bool(::JS::dbg::IsDebugger)(JSObject&);
  friend bool(::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&,
                                             MutableHandleObjectVector);
  friend bool JS::dbg::FireOnGarbageCollectionHookRequired(JSContext* cx);
  friend bool JS::dbg::FireOnGarbageCollectionHook(
      JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data);

 public:
  enum Hook {
    OnDebuggerStatement,
    OnExceptionUnwind,
    OnNewScript,
    OnEnterFrame,
    OnNewGlobalObject,
    OnNewPromise,
    OnPromiseSettled,
    OnGarbageCollection,
    HookCount
  };
  enum {
    JSSLOT_DEBUG_PROTO_START,
    JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
    JSSLOT_DEBUG_ENV_PROTO,
    JSSLOT_DEBUG_OBJECT_PROTO,
    JSSLOT_DEBUG_SCRIPT_PROTO,
    JSSLOT_DEBUG_SOURCE_PROTO,
    JSSLOT_DEBUG_MEMORY_PROTO,
    JSSLOT_DEBUG_PROTO_STOP,
    JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
    JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
    JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
    JSSLOT_DEBUG_COUNT
  };

  class ExecutionObservableSet {
   public:
    typedef HashSet<Zone*>::Range ZoneRange;

    virtual Zone* singleZone() const { return nullptr; }
    virtual JSScript* singleScriptForZoneInvalidation() const {
      return nullptr;
    }
    virtual const HashSet<Zone*>* zones() const { return nullptr; }

    virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
    virtual bool shouldMarkAsDebuggee(FrameIter& iter) const = 0;
  };

  // This enum is converted to and compare with bool values; NotObserving
  // must be 0 and Observing must be 1.
  enum IsObserving { NotObserving = 0, Observing = 1 };

  // Return true if the given realm is a debuggee of this debugger,
  // false otherwise.
  bool isDebuggeeUnbarriered(const Realm* realm) const;

  // Return true if this Debugger observed a debuggee that participated in the
  // GC identified by the given GC number. Return false otherwise.
  // May return false negatives if we have hit OOM.
  bool observedGC(uint64_t majorGCNumber) const {
    return observedGCs.has(majorGCNumber);
  }

  // Notify this Debugger that one or more of its debuggees is participating
  // in the GC identified by the given GC number.
  bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
    return observedGCs.put(majorGCNumber);
  }

  bool isEnabled() const { return enabled; }

  static SavedFrame* getObjectAllocationSite(JSObject& obj);

  struct AllocationsLogEntry {
    AllocationsLogEntry(HandleObject frame, mozilla::TimeStamp when,
                        const char* className, HandleAtom ctorName, size_t size,
                        bool inNursery)
        : frame(frame),
          when(when),
          className(className),
          ctorName(ctorName),
          size(size),
          inNursery(inNursery) {
      MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>() ||
                               IsDeadProxyObject(frame));
    }

    HeapPtr<JSObject*> frame;
    mozilla::TimeStamp when;
    const char* className;
    HeapPtr<JSAtom*> ctorName;
    size_t size;
    bool inNursery;

    void trace(JSTracer* trc) {
      TraceNullableEdge(trc, &frame, "Debugger::AllocationsLogEntry::frame");
      TraceNullableEdge(trc, &ctorName,
                        "Debugger::AllocationsLogEntry::ctorName");
    }
  };

  // Barrier methods so we can have WeakHeapPtr<Debugger*>.
  static void readBarrier(Debugger* dbg) {
    InternalBarrierMethods<JSObject*>::readBarrier(dbg->object);
  }
  static void writeBarrierPost(Debugger** vp, Debugger* prev, Debugger* next) {}
#ifdef DEBUG
  static void assertThingIsNotGray(Debugger* dbg) { return; }
#endif

 private:
  GCPtrNativeObject object; /* The Debugger object. Strong reference. */
  WeakGlobalObjectSet
      debuggees; /* Debuggee globals. Cross-compartment weak references. */
  JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
  js::GCPtrObject uncaughtExceptionHook; /* Strong reference. */
  bool enabled;
  bool allowUnobservedAsmJS;

  // Whether to enable code coverage on the Debuggee.
  bool collectCoverageInfo;

  template <typename T>
  struct DebuggerLinkAccess {
    static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) {
      return aThis->debuggerLink;
    }
  };

  // List of all js::Breakpoints in this debugger.
  using BreakpointList =
      mozilla::DoublyLinkedList<js::Breakpoint,
                                DebuggerLinkAccess<js::Breakpoint>>;
  BreakpointList breakpoints;

  // The set of GC numbers for which one or more of this Debugger's observed
  // debuggees participated in.
  using GCNumberSet =
      HashSet<uint64_t, DefaultHasher<uint64_t>, ZoneAllocPolicy>;
  GCNumberSet observedGCs;

  using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;

  AllocationsLog allocationsLog;
  bool trackingAllocationSites;
  double allocationSamplingProbability;
  size_t maxAllocationsLogLength;
  bool allocationsLogOverflowed;

  static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;

  MOZ_MUST_USE bool appendAllocationSite(JSContext* cx, HandleObject obj,
                                         HandleSavedFrame frame,
                                         mozilla::TimeStamp when);

  /*
   * Recompute the set of debuggee zones based on the set of debuggee globals.
   */
  void recomputeDebuggeeZoneSet();

  /*
   * Return true if there is an existing object metadata callback for the
   * given global's compartment that will prevent our instrumentation of
   * allocations.
   */
  static bool cannotTrackAllocations(const GlobalObject& global);

  /*
   * Return true if the given global is being observed by at least one
   * Debugger that is tracking allocations.
   */
  static bool isObservedByDebuggerTrackingAllocations(
      const GlobalObject& global);

  /*
   * Add allocations tracking for objects allocated within the given
   * debuggee's compartment. The given debuggee global must be observed by at
   * least one Debugger that is enabled and tracking allocations.
   */
  static MOZ_MUST_USE bool addAllocationsTracking(
      JSContext* cx, Handle<GlobalObject*> debuggee);

  /*
   * Remove allocations tracking for objects allocated within the given
   * global's compartment. This is a no-op if there are still Debuggers
   * observing this global and who are tracking allocations.
   */
  static void removeAllocationsTracking(GlobalObject& global);

  /*
   * Add or remove allocations tracking for all debuggees.
   */
  MOZ_MUST_USE bool addAllocationsTrackingForAllDebuggees(JSContext* cx);
  void removeAllocationsTrackingForAllDebuggees();

  /*
   * If this Debugger is enabled, and has a onNewGlobalObject handler, then
   * this link is inserted into the list headed by
   * JSRuntime::onNewGlobalObjectWatchers.
   */
  mozilla::DoublyLinkedListElement<Debugger> onNewGlobalObjectWatchersLink;

  /*
   * Map from stack frames that are currently on the stack to Debugger.Frame
   * instances.
   *
   * The keys are always live stack frames. We drop them from this map as
   * soon as they leave the stack (see slowPathOnLeaveFrame) and in
   * removeDebuggee.
   *
   * We don't trace the keys of this map (the frames are on the stack and
   * thus necessarily live), but we do trace the values. It's like a WeakMap
   * that way, but since stack frames are not gc-things, the implementation
   * has to be different.
   */
  typedef HashMap<AbstractFramePtr, HeapPtr<DebuggerFrame*>,
                  DefaultHasher<AbstractFramePtr>, ZoneAllocPolicy>
      FrameMap;
  FrameMap frames;

  /*
   * Map from generator objects to their Debugger.Frame instances.
   *
   * When a Debugger.Frame is created for a generator frame, it is added to
   * this map and remains there for the lifetime of the generator, whether
   * that frame is on the stack at the moment or not.  This is in addition to
   * the entry in `frames` that exists as long as the generator frame is on
   * the stack.
   *
   * We need to keep the Debugger.Frame object alive to deliver it to the
   * onEnterFrame handler on resume, and to retain onStep and onPop hooks.
   *
   * An entry is present in this table when:
   * -   both the debuggee generator object and the Debugger.Frame object exist
   * -   the Debugger.Frame's owner is still an enabled debugger of
   *     the debuggee compartment
   * regardless of whether the frame is currently suspended. (This list is
   * meant to explain why we update the table in the particular places where
   * we do so.)
   */
  typedef DebuggerWeakMap<JSObject*> GeneratorWeakMap;
  GeneratorWeakMap generatorFrames;

  /* An ephemeral map from JSScript* to Debugger.Script instances. */
  typedef DebuggerWeakMap<JSScript*> ScriptWeakMap;
  ScriptWeakMap scripts;

  using LazyScriptWeakMap = DebuggerWeakMap<LazyScript*>;
  LazyScriptWeakMap lazyScripts;

  using LazyScriptVector = JS::GCVector<LazyScript*>;

  // The map from debuggee source script objects to their Debugger.Source
  // instances.
  typedef DebuggerWeakMap<JSObject*, true> SourceWeakMap;
  SourceWeakMap sources;

  // The map from debuggee objects to their Debugger.Object instances.
  typedef DebuggerWeakMap<JSObject*> ObjectWeakMap;
  ObjectWeakMap objects;

  // The map from debuggee Envs to Debugger.Environment instances.
  ObjectWeakMap environments;

  // The map from WasmInstanceObjects to synthesized Debugger.Script
  // instances.
  typedef DebuggerWeakMap<WasmInstanceObject*> WasmInstanceWeakMap;
  WasmInstanceWeakMap wasmInstanceScripts;

  // The map from WasmInstanceObjects to synthesized Debugger.Source
  // instances.
  WasmInstanceWeakMap wasmInstanceSources;

  // Keep track of tracelogger last drained identifiers to know if there are
  // lost events.
#ifdef NIGHTLY_BUILD
  uint32_t traceLoggerLastDrainedSize;
  uint32_t traceLoggerLastDrainedIteration;
#endif
  uint32_t traceLoggerScriptedCallsLastDrainedSize;
  uint32_t traceLoggerScriptedCallsLastDrainedIteration;

  class QueryBase;
  class ScriptQuery;
  class SourceQuery;
  class ObjectQuery;

  MOZ_MUST_USE bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
  void removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
                            WeakGlobalObjectSet::Enum* debugEnum);

  enum class CallUncaughtExceptionHook { No, Yes };

  /*
   * Apply the resumption information in (resumeMode, vp) to `frame` in
   * anticipation of returning to the debuggee.
   *
   * This is the usual path for returning from the debugger to the debuggee
   * when we have a resumption value to apply. This does final checks on the
   * result value and exits the debugger's realm by calling `ar.reset()`.
   * Some hooks don't call this because they don't allow the debugger to
   * control resumption; those just call `ar.reset()` and return.
   */
  ResumeMode leaveDebugger(mozilla::Maybe<AutoRealm>& ar,
                           AbstractFramePtr frame,
                           const mozilla::Maybe<HandleValue>& maybeThisv,
                           CallUncaughtExceptionHook callHook,
                           ResumeMode resumeMode, MutableHandleValue vp);

  /*
   * Report and clear the pending exception on ar.context, if any, and return
   * ResumeMode::Terminate.
   */
  ResumeMode reportUncaughtException(mozilla::Maybe<AutoRealm>& ar);

  /*
   * Cope with an error or exception in a debugger hook.
   *
   * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
   * addition, vp is given, then parse the value returned by
   * uncaughtExceptionHook as a resumption value.
   *
   * If there is no uncaughtExceptionHook, or if it fails, report and clear
   * the pending exception on ar.context and return ResumeMode::Terminate.
   *
   * This always calls `ar.reset()`; ar is a parameter because this method
   * must do some things in the debugger realm and some things in the
   * debuggee realm.
   */
  ResumeMode handleUncaughtException(mozilla::Maybe<AutoRealm>& ar);
  ResumeMode handleUncaughtException(
      mozilla::Maybe<AutoRealm>& ar, MutableHandleValue vp,
      const mozilla::Maybe<HandleValue>& thisVForCheck = mozilla::Nothing(),
      AbstractFramePtr frame = NullFramePtr());

  ResumeMode handleUncaughtExceptionHelper(
      mozilla::Maybe<AutoRealm>& ar, MutableHandleValue* vp,
      const mozilla::Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame);

  /*
   * Handle the result of a hook that is expected to return a resumption
   * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is
   * called when we return from a debugging hook to debuggee code. The
   * interpreter wants a (ResumeMode, Value) pair telling it how to proceed.
   *
   * Precondition: ar is entered. We are in the debugger compartment.
   *
   * Postcondition: This called ar.reset(). See handleUncaughtException.
   *
   * If `success` is false, the hook failed. If an exception is pending in
   * ar.context(), return handleUncaughtException(ar, vp, callhook).
   * Otherwise just return ResumeMode::Terminate.
   *
   * If `success` is true, there must be no exception pending in ar.context().
   * `rv` may be:
   *
   *     undefined - Return `ResumeMode::Continue` to continue execution
   *         normally.
   *
   *     {return: value} or {throw: value} - Call unwrapDebuggeeValue to
   *         unwrap `value`. Store the result in `*vp` and return
   *         `ResumeMode::Return` or `ResumeMode::Throw`. The interpreter
   *         will force the current frame to return or throw an exception.
   *
   *     null - Return `ResumeMode::Terminate` to terminate the debuggee with
   *         an uncatchable error.
   *
   *     anything else - Make a new TypeError the pending exception and
   *         return handleUncaughtException(ar, vp, callHook).
   */
  ResumeMode processHandlerResult(mozilla::Maybe<AutoRealm>& ar, bool success,
                                  const Value& rv, AbstractFramePtr frame,
                                  jsbytecode* pc, MutableHandleValue vp);

  ResumeMode processParsedHandlerResult(mozilla::Maybe<AutoRealm>& ar,
                                        AbstractFramePtr frame, jsbytecode* pc,
                                        bool success, ResumeMode resumeMode,
                                        MutableHandleValue vp);

  GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);

  static void traceObject(JSTracer* trc, JSObject* obj);

  void trace(JSTracer* trc);
  friend struct js::GCManagedDeletePolicy<Debugger>;

  void traceForMovingGC(JSTracer* trc);
  void traceCrossCompartmentEdges(JSTracer* tracer);

  static const ClassOps classOps_;

 public:
  static const Class class_;

 private:
  static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args,
                                       Debugger& dbg, Hook which);
  static MOZ_MUST_USE bool setHookImpl(JSContext* cx, CallArgs& args,
                                       Debugger& dbg, Hook which);

  static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
  static bool setEnabled(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnNewScript(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnNewScript(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
  static bool getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
  static bool setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
  static bool getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
  static bool setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
  static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
  static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
  static bool getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
  static bool setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
  static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
  static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
  static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
  static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
  static bool removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp);
  static bool hasDebuggee(JSContext* cx, unsigned argc, Value* vp);
  static bool getDebuggees(JSContext* cx, unsigned argc, Value* vp);
  static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
  static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
  static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
  static bool findSources(JSContext* cx, unsigned argc, Value* vp);
  static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
  static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
  static bool makeGlobalObjectReference(JSContext* cx, unsigned argc,
                                        Value* vp);
  static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc,
                                          Value* vp);
  static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc,
                                          Value* vp);
  static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
  static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
  static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
  static bool recordReplayProcessKind(JSContext* cx, unsigned argc, Value* vp);
#ifdef NIGHTLY_BUILD
  static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
  static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
#endif
  static bool adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp);
  static bool adoptSource(JSContext* cx, unsigned argc, Value* vp);
  static bool construct(JSContext* cx, unsigned argc, Value* vp);
  static const JSPropertySpec properties[];
  static const JSFunctionSpec methods[];
  static const JSFunctionSpec static_methods[];

  static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx,
                                                       AbstractFramePtr frame,
                                                       bool suspending = false);
  static bool updateExecutionObservabilityOfFrames(
      JSContext* cx, const ExecutionObservableSet& obs, IsObserving observing);
  static bool updateExecutionObservabilityOfScripts(
      JSContext* cx, const ExecutionObservableSet& obs, IsObserving observing);
  static bool updateExecutionObservability(JSContext* cx,
                                           ExecutionObservableSet& obs,
                                           IsObserving observing);

  template <typename FrameFn /* void (DebuggerFrame*) */>
  static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);

  /*
   * Return a vector containing all Debugger.Frame instances referring to
   * |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
   * compute it ourselves from |frame|.
   */
  using DebuggerFrameVector = GCVector<DebuggerFrame*>;
  static MOZ_MUST_USE bool getDebuggerFrames(
      AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames);

 public:
  static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(
      JSContext* cx, AbstractFramePtr osrSourceFrame);

  // Public for DebuggerScript_setBreakpoint.
  static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(
      JSContext* cx, JSScript* script);

  // Whether the Debugger instance needs to observe all non-AOT JS
  // execution of its debugees.
  IsObserving observesAllExecution() const;

  // Whether the Debugger instance needs to observe AOT-compiled asm.js
  // execution of its debuggees.
  IsObserving observesAsmJS() const;

  // Whether the Debugger instance needs to observe coverage of any JavaScript
  // execution.
  IsObserving observesCoverage() const;

 private:
  static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(
      JSContext* cx, AbstractFramePtr frame);
  static MOZ_MUST_USE bool ensureExecutionObservabilityOfRealm(
      JSContext* cx, JS::Realm* realm);

  static bool hookObservesAllExecution(Hook which);

  MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(
      JSContext* cx, IsObserving observing);
  MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx,
                                                      IsObserving observing);
  void updateObservesAsmJSOnDebuggees(IsObserving observing);

  JSObject* getHook(Hook hook) const;
  bool hasAnyLiveHooks(JSRuntime* rt) const;

  static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx,
                                                  HandleScript script);
  static ResumeMode slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
  static ResumeMode slowPathOnResumeFrame(JSContext* cx,
                                          AbstractFramePtr frame);
  static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx,
                                                AbstractFramePtr frame,
                                                jsbytecode* pc, bool ok);
  static MOZ_MUST_USE bool slowPathOnNewGenerator(
      JSContext* cx, AbstractFramePtr frame,
      Handle<AbstractGeneratorObject*> genObj);
  static ResumeMode slowPathOnDebuggerStatement(JSContext* cx,
                                                AbstractFramePtr frame);
  static ResumeMode slowPathOnExceptionUnwind(JSContext* cx,
                                              AbstractFramePtr frame);
  static void slowPathOnNewScript(JSContext* cx, HandleScript script);
  static void slowPathOnNewWasmInstance(
      JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
  static void slowPathOnNewGlobalObject(JSContext* cx,
                                        Handle<GlobalObject*> global);
  static MOZ_MUST_USE bool slowPathOnLogAllocationSite(
      JSContext* cx, HandleObject obj, HandleSavedFrame frame,
      mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs);
  static void slowPathPromiseHook(JSContext* cx, Hook hook,
                                  Handle<PromiseObject*> promise);

  template <typename HookIsEnabledFun /* bool (Debugger*) */,
            typename FireHookFun /* ResumeMode (Debugger*) */>
  static ResumeMode dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
                                 FireHookFun fireHook);

  ResumeMode fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
  ResumeMode fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
  ResumeMode fireEnterFrame(JSContext* cx, MutableHandleValue vp);
  ResumeMode fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global,
                                 MutableHandleValue vp);
  ResumeMode firePromiseHook(JSContext* cx, Hook hook, HandleObject promise,
                             MutableHandleValue vp);

  NativeObject* newVariantWrapper(JSContext* cx,
                                  Handle<DebuggerScriptReferent> referent) {
    return newDebuggerScript(cx, referent);
  }
  NativeObject* newVariantWrapper(JSContext* cx,
                                  Handle<DebuggerSourceReferent> referent) {
    return newDebuggerSource(cx, referent);
  }

  /*
   * Helper function to help wrap Debugger objects whose referents may be
   * variants. Currently Debugger.Script and Debugger.Source referents may
   * be variants.
   *
   * Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource
   * whenever possible.
   */
  template <typename ReferentVariant, typename Referent, typename Map>
  JSObject* wrapVariantReferent(JSContext* cx, Map& map,
                                Handle<CrossCompartmentKey> key,
                                Handle<ReferentVariant> referent);
  JSObject* wrapVariantReferent(JSContext* cx,
                                Handle<DebuggerScriptReferent> referent);
  JSObject* wrapVariantReferent(JSContext* cx,
                                Handle<DebuggerSourceReferent> referent);

  /*
   * Allocate and initialize a Debugger.Script instance whose referent is
   * |referent|.
   */
  NativeObject* newDebuggerScript(JSContext* cx,
                                  Handle<DebuggerScriptReferent> referent);

  /*
   * Allocate and initialize a Debugger.Source instance whose referent is
   * |referent|.
   */
  NativeObject* newDebuggerSource(JSContext* cx,
                                  Handle<DebuggerSourceReferent> referent);

  /*
   * Receive a "new script" event from the engine. A new script was compiled
   * or deserialized.
   */
  void fireNewScript(JSContext* cx,
                     Handle<DebuggerScriptReferent> scriptReferent);

  /*
   * Receive a "garbage collection" event from the engine. A GC cycle with the
   * given data was recently completed.
   */
  void fireOnGarbageCollectionHook(
      JSContext* cx, const JS::dbg::GarbageCollectionEvent::Ptr& gcData);

  inline Breakpoint* firstBreakpoint() const;

  static MOZ_MUST_USE bool replaceFrameGuts(JSContext* cx,
                                            AbstractFramePtr from,
                                            AbstractFramePtr to,
                                            ScriptFrameIter& iter);

 public:
  Debugger(JSContext* cx, NativeObject* dbg);
  ~Debugger();

  inline const js::GCPtrNativeObject& toJSObject() const;
  inline js::GCPtrNativeObject& toJSObjectRef();
  static inline Debugger* fromJSObject(const JSObject* obj);
  static Debugger* fromChildJSObject(JSObject* obj);

  Zone* zone() const { return toJSObject()->zone(); }

  bool hasMemory() const;
  DebuggerMemory& memory() const;

  WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }

  /*** Methods for interaction with the GC. *******************************/

  /*
   * A Debugger object is live if:
   *   * the Debugger JSObject is live (Debugger::trace handles this case); OR
   *   * it is in the middle of dispatching an event (the event dispatching
   *     code roots it in this case); OR
   *   * it is enabled, and it is debugging at least one live compartment,
   *     and at least one of the following is true:
   *       - it has a debugger hook installed
   *       - it has a breakpoint set on a live script
   *       - it has a watchpoint set on a live object.
   *
   * Debugger::markIteratively handles the last case. If it finds any Debugger
   * objects that are definitely live but not yet marked, it marks them and
   * returns true. If not, it returns false.
   */
  static void traceIncomingCrossCompartmentEdges(JSTracer* tracer);
  static MOZ_MUST_USE bool markIteratively(GCMarker* marker);
  static void traceAllForMovingGC(JSTracer* trc);
  static void sweepAll(FreeOp* fop);
  static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
  static MOZ_MUST_USE bool findSweepGroupEdges(JSRuntime* rt);
#ifdef DEBUG
  static bool isDebuggerCrossCompartmentEdge(JSObject* obj,
                                             const js::gc::Cell* cell);
#endif

  // Checks it the current compartment is allowed to execute code.
  static inline MOZ_MUST_USE bool checkNoExecute(JSContext* cx,
                                                 HandleScript script);

  /*
   * Announce to the debugger that the context has entered a new JavaScript
   * frame, |frame|. Call whatever hooks have been registered to observe new
   * frames.
   */
  static inline ResumeMode onEnterFrame(JSContext* cx, AbstractFramePtr frame);

  /*
   * Like onEnterFrame, but for resuming execution of a generator or async
   * function. `frame` is a new baseline or interpreter frame, but abstractly
   * it can be identified with a particular generator frame that was
   * suspended earlier.
   *
   * There is no separate user-visible Debugger.onResumeFrame hook; this
   * fires .onEnterFrame (again, since we're re-entering the frame).
   *
   * Unfortunately, the interpreter and the baseline JIT arrange for this to
   * be called in different ways. The interpreter calls it from JSOP_RESUME,
   * immediately after pushing the resumed frame; the JIT calls it from
   * JSOP_AFTERYIELD, just after the generator resumes. The difference
   * should not be user-visible.
   */
  static inline ResumeMode onResumeFrame(JSContext* cx, AbstractFramePtr frame);

  /*
   * Announce to the debugger a |debugger;| statement on has been
   * encountered on the youngest JS frame on |cx|. Call whatever hooks have
   * been registered to observe this.
   *
   * Note that this method is called for all |debugger;| statements,
   * regardless of the frame's debuggee-ness.
   */
  static inline ResumeMode onDebuggerStatement(JSContext* cx,
                                               AbstractFramePtr frame);

  /*
   * Announce to the debugger that an exception has been thrown and propagated
   * to |frame|. Call whatever hooks have been registered to observe this.
   */
  static inline ResumeMode onExceptionUnwind(JSContext* cx,
                                             AbstractFramePtr frame);

  /*
   * Announce to the debugger that the thread has exited a JavaScript frame,
   * |frame|. If |ok| is true, the frame is returning normally; if |ok| is
   * false, the frame is throwing an exception or terminating.
   *
   * Change cx's current exception and |frame|'s return value to reflect the
   * changes in behavior the hooks request, if any. Return the new error/success
   * value.
   *
   * This function may be called twice for the same outgoing frame; only the
   * first call has any effect. (Permitting double calls simplifies some
   * cases where an onPop handler's resumption value changes a return to a
   * throw, or vice versa: we can redirect to a complete copy of the
   * alternative path, containing its own call to onLeaveFrame.)
   */
  static inline MOZ_MUST_USE bool onLeaveFrame(JSContext* cx,
                                               AbstractFramePtr frame,
                                               jsbytecode* pc, bool ok);

  /*
   * Announce to the debugger that a generator object has been created,
   * via JSOP_GENERATOR.
   *
   * This does not fire user hooks, but it's needed for debugger bookkeeping.
   */
  static inline MOZ_MUST_USE bool onNewGenerator(
      JSContext* cx, AbstractFramePtr frame,
      Handle<AbstractGeneratorObject*> genObj);

  static inline void onNewScript(JSContext* cx, HandleScript script);
  static inline void onNewWasmInstance(
      JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
  static inline void onNewGlobalObject(JSContext* cx,
                                       Handle<GlobalObject*> global);
  static inline MOZ_MUST_USE bool onLogAllocationSite(JSContext* cx,
                                                      JSObject* obj,
                                                      HandleSavedFrame frame,
                                                      mozilla::TimeStamp when);
  static ResumeMode onTrap(JSContext* cx, MutableHandleValue vp);
  static ResumeMode onSingleStep(JSContext* cx, MutableHandleValue vp);
  static MOZ_MUST_USE bool handleBaselineOsr(JSContext* cx,
                                             InterpreterFrame* from,
                                             jit::BaselineFrame* to);
  static MOZ_MUST_USE bool handleIonBailout(JSContext* cx,
                                            jit::RematerializedFrame* from,
                                            jit::BaselineFrame* to);
  static void handleUnrecoverableIonBailoutError(
      JSContext* cx, jit::RematerializedFrame* frame);
  static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame,
                                    HandleValue rval);
  static bool hasLiveHook(GlobalObject* global, Hook which);
  static bool inFrameMaps(AbstractFramePtr frame);

  // Notify any Debugger instances observing this promise's global that a new
  // promise was allocated.
  static inline void onNewPromise(JSContext* cx,
                                  Handle<PromiseObject*> promise);

  // Notify any Debugger instances observing this promise's global that the
  // promise has settled (ie, it has either been fulfilled or rejected). Note
  // that this is *not* equivalent to the promise resolution (ie, the promise's
  // fate getting locked in) because you can resolve a promise with another
  // pending promise, in which case neither promise has settled yet.
  //
  // This should never be called on the same promise more than once, because a
  // promise can only make the transition from unsettled to settled once.
  static inline void onPromiseSettled(JSContext* cx,
                                      Handle<PromiseObject*> promise);

  /*** Functions for use by Debugger.cpp. *********************************/

  inline bool observesEnterFrame() const;
  inline bool observesNewScript() const;
  inline bool observesNewGlobalObject() const;
  inline bool observesGlobal(GlobalObject* global) const;
  bool observesFrame(AbstractFramePtr frame) const;
  bool observesFrame(const FrameIter& iter) const;
  bool observesScript(JSScript* script) const;
  bool observesWasm(wasm::Instance* instance) const;

  /*
   * If env is nullptr, call vp->setNull() and return true. Otherwise, find
   * or create a Debugger.Environment object for the given Env. On success,
   * store the Environment object in *vp and return true.
   */
  MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
                                    MutableHandleValue vp);
  MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
                                    MutableHandleDebuggerEnvironment result);

  /*
   * Like cx->compartment()->wrap(cx, vp), but for the debugger realm.
   *
   * Preconditions: *vp is a value from a debuggee realm; cx is in the
   * debugger's compartment.
   *
   * If *vp is an object, this produces a (new or existing) Debugger.Object
   * wrapper for it. Otherwise this is the same as Compartment::wrap.
   *
   * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
   * of the form { optimizedOut: true }.
   *
   * If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
   * arguments, this produces a plain object of the form { missingArguments:
   * true }.
   *
   * If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an
   * unaccessible uninitialized binding, this produces a plain object of the
   * form { uninitialized: true }.
   */
  MOZ_MUST_USE bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
  MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, HandleObject obj,
                                       MutableHandleDebuggerObject result);

  /*
   * Unwrap a Debug.Object, without rewrapping it for any particular debuggee
   * compartment.
   *
   * Preconditions: cx is in the debugger compartment. *vp is a value in that
   * compartment. (*vp should be a "debuggee value", meaning it is the
   * debugger's reflection of a value in the debuggee.)
   *
   * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
   * is an object, throw a TypeError, because it is not a debuggee
   * value. Otherwise *vp is a primitive, so leave it alone.
   *
   * When passing values from the debuggee to the debugger:
   *     enter debugger compartment;
   *     call wrapDebuggeeValue;  // compartment- and debugger-wrapping
   *
   * When passing values from the debugger to the debuggee:
   *     call unwrapDebuggeeValue;  // debugger-unwrapping
   *     enter debuggee realm;
   *     call cx->compartment()->wrap;  // compartment-rewrapping
   *
   * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
   * two different kinds of symmetry at work: regardless of which direction
   * we're going, we want any exceptions to be created and thrown in the
   * debugger compartment--mirror symmetry. But compartment wrapping always
   * happens in the target compartment--rotational symmetry.)
   */
  MOZ_MUST_USE bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
  MOZ_MUST_USE bool unwrapDebuggeeObject(JSContext* cx,
                                         MutableHandleObject obj);
  MOZ_MUST_USE bool unwrapPropertyDescriptor(
      JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc);

  /*
   * Store the Debugger.Frame object for iter in *vp/result.
   *
   * If this Debugger does not already have a Frame object for the frame
   * `iter` points to, a new Frame object is created, and `iter`'s private
   * data is copied into it.
   */
  MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
                             MutableHandleValue vp);
  MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
                             MutableHandleDebuggerFrame result);

  /*
   * Set |*resumeMode| and |*value| to a (ResumeMode, Value) pair reflecting a
   * standard SpiderMonkey call state: a boolean success value |ok|, a return
   * value |rv|, and a context |cx| that may or may not have an exception set.
   * If an exception was pending on |cx|, it is cleared (and |ok| is asserted
   * to be false). On exceptional returns, exnStack will be set to any stack
   * associated with the original throw, if available.
   */
  static void resultToCompletion(JSContext* cx, bool ok, const Value& rv,
                                 ResumeMode* resumeMode,
                                 MutableHandleValue value,
                                 MutableHandleSavedFrame exnStack);

  /*
   * Set |*result| to a JavaScript completion value corresponding to
   * |resumeMode| and |value|. |value| should be the return value or exception
   * value, not wrapped as a debuggee value. When throwing an exception,
   * |exnStack| may be set to the stack when the value was thrown. |cx| must be
   * in the debugger compartment.
   */
  MOZ_MUST_USE bool newCompletionValue(JSContext* cx, ResumeMode resumeMode,
                                       const Value& value, SavedFrame* exnStack,
                                       MutableHandleValue result);

  /*
   * Precondition: we are in the debuggee realm (ar is entered) and ok is true
   * if the operation in the debuggee realm succeeded, false on error or
   * exception.
   *
   * Postcondition: we are in the debugger realm, having called `ar.reset()`
   * even if an error occurred.
   *
   * On success, a completion value is in vp and ar.context does not have a
   * pending exception. (This ordinarily returns true even if the ok argument
   * is false.)
   */
  MOZ_MUST_USE bool receiveCompletionValue(mozilla::Maybe<AutoRealm>& ar,
                                           bool ok, HandleValue val,
                                           MutableHandleValue vp);

  /*
   * Return the Debugger.Script object for |script|, or create a new one if
   * needed. The context |cx| must be in the debugger realm; |script| must be
   * a script in a debuggee realm.
   */
  JSObject* wrapScript(JSContext* cx, HandleScript script);

  JSObject* wrapLazyScript(JSContext* cx, Handle<LazyScript*> script);

  /*
   * Return the Debugger.Script object for |wasmInstance| (the toplevel
   * script), synthesizing a new one if needed. The context |cx| must be in
   * the debugger compartment; |wasmInstance| must be a WasmInstanceObject in
   * the debuggee realm.
   */
  JSObject* wrapWasmScript(JSContext* cx,
                           Handle<WasmInstanceObject*> wasmInstance);

  /*
   * Return the Debugger.Source object for |source|, or create a new one if
   * needed. The context |cx| must be in the debugger compartment; |source|
   * must be a script source object in a debuggee realm.
   */
  JSObject* wrapSource(JSContext* cx, js::HandleScriptSourceObject source);

  /*
   * Return the Debugger.Source object for |wasmInstance| (the entire module),
   * synthesizing a new one if needed. The context |cx| must be in the
   * debugger compartment; |wasmInstance| must be a WasmInstanceObject in the
   * debuggee realm.
   */
  JSObject* wrapWasmSource(JSContext* cx,
                           Handle<WasmInstanceObject*> wasmInstance);

  /*
   * Add a link between the given generator object and a Debugger.Frame
   * object.  This link is used to make sure the same Debugger.Frame stays
   * associated with a given generator object (or async function activation),
   * even while it is suspended and removed from the stack.
   *
   * The context `cx` and `frameObj` must be in the debugger realm, and
   * `genObj` must be in a debuggee realm.
   *
   * `frameObj` must be this `Debugger`'s debug wrapper for the generator or
   * async function call associated with `genObj`. This activation may
   * or may not actually be on the stack right now.
   */
  MOZ_MUST_USE bool addGeneratorFrame(JSContext* cx,
                                      Handle<AbstractGeneratorObject*> genObj,
                                      HandleDebuggerFrame frameObj);

 private:
  Debugger(const Debugger&) = delete;
  Debugger& operator=(const Debugger&) = delete;
};

enum class DebuggerEnvironmentType { Declarative, With, Object };

class DebuggerEnvironment : public NativeObject {
 public:
  enum { OWNER_SLOT };

  static const unsigned RESERVED_SLOTS = 1;

  static const Class class_;

  static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor,
                                 Handle<GlobalObject*> global);
  static DebuggerEnvironment* create(JSContext* cx, HandleObject proto,
                                     HandleObject referent,
                                     HandleNativeObject debugger);

  DebuggerEnvironmentType type() const;
  MOZ_MUST_USE bool getParent(JSContext* cx,
                              MutableHandleDebuggerEnvironment result) const;
  MOZ_MUST_USE bool getObject(JSContext* cx,
                              MutableHandleDebuggerObject result) const;
  MOZ_MUST_USE bool getCallee(JSContext* cx,
                              MutableHandleDebuggerObject result) const;
  bool isDebuggee() const;
  bool isOptimized() const;

  static MOZ_MUST_USE bool getNames(JSContext* cx,
                                    HandleDebuggerEnvironment environment,
                                    MutableHandle<IdVector> result);
  static MOZ_MUST_USE bool find(JSContext* cx,
                                HandleDebuggerEnvironment environment,
                                HandleId id,
                                MutableHandleDebuggerEnvironment result);
  static MOZ_MUST_USE bool getVariable(JSContext* cx,
                                       HandleDebuggerEnvironment environment,
                                       HandleId id, MutableHandleValue result);
  static MOZ_MUST_USE bool setVariable(JSContext* cx,
                                       HandleDebuggerEnvironment environment,
                                       HandleId id, HandleValue value);

 private:
  static const ClassOps classOps_;

  static const JSPropertySpec properties_[];
  static const JSFunctionSpec methods_[];

  Env* referent() const {
    Env* env = static_cast<Env*>(getPrivate());
    MOZ_ASSERT(env);
    return env;
  }

  Debugger* owner() const;

  bool requireDebuggee(JSContext* cx) const;

  static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);

  static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool parentGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool objectGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool inspectableGetter(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool optimizedOutGetter(JSContext* cx, unsigned argc,
                                              Value* vp);

  static MOZ_MUST_USE bool namesMethod(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool findMethod(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool getVariableMethod(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool setVariableMethod(JSContext* cx, unsigned argc,
                                             Value* vp);
};

enum class DebuggerFrameType { Eval, Global, Call, Module, WasmCall };

enum class DebuggerFrameImplementation { Interpreter, Baseline, Ion, Wasm };

/*
 * A Handler represents a reference to a handler function. These handler
 * functions are called by the Debugger API to notify the user of certain
 * events. For each event type, we define a separate subclass of Handler. This
 * allows users to define a single reference to an object that implements
 * multiple handlers, by inheriting from the appropriate subclasses.
 *
 * A Handler can be stored on a reflection object, in which case the reflection
 * object becomes responsible for managing the lifetime of the Handler. To aid
 * with this, the Handler base class defines several methods, which are to be
 * called by the reflection object at the appropriate time (see below).
 */
struct Handler {
  virtual ~Handler() {}

  /*
   * If the Handler is a reference to a callable JSObject, this method returns
   * the latter. This allows the Handler to be used from JS. Otherwise, this
   * method returns nullptr.
   */
  virtual JSObject* object() const = 0;

  /*
   * Drops the reference to the handler. This method will be called by the
   * reflection object on which the reference is stored when the former is
   * finalized, or the latter replaced.
   */
  virtual void drop() = 0;

  /*
   * Traces the reference to the handler. This method will be called
   * by the reflection object on which the reference is stored whenever the
   * former is traced.
   */
  virtual void trace(JSTracer* tracer) = 0;
};

class DebuggerArguments : public NativeObject {
 public:
  static const Class class_;

  static DebuggerArguments* create(JSContext* cx, HandleObject proto,
                                   HandleDebuggerFrame frame);

 private:
  enum { FRAME_SLOT };

  static const unsigned RESERVED_SLOTS = 1;
};

/*
 * An OnStepHandler represents a handler function that is called when a small
 * amount of progress is made in a frame.
 */
struct OnStepHandler : Handler {
  /*
   * If we have made a small amount of progress in a frame, this method is
   * called with the frame as argument. If succesful, this method should
   * return true, with `resumeMode` and `vp` set to a resumption value
   * specifiying how execution should continue.
   */
  virtual bool onStep(JSContext* cx, HandleDebuggerFrame frame,
                      ResumeMode& resumeMode, MutableHandleValue vp) = 0;
};

class ScriptedOnStepHandler final : public OnStepHandler {
 public:
  explicit ScriptedOnStepHandler(JSObject* object);
  virtual JSObject* object() const override;
  virtual void drop() override;
  virtual void trace(JSTracer* tracer) override;
  virtual bool onStep(JSContext* cx, HandleDebuggerFrame frame,
                      ResumeMode& resumeMode, MutableHandleValue vp) override;

 private:
  HeapPtr<JSObject*> object_;
};

/*
 * An OnPopHandler represents a handler function that is called just before a
 * frame is popped.
 */
struct OnPopHandler : Handler {
  /*
   * If a frame is about the be popped, this method is called with the frame
   * as argument, and `resumeMode` and `vp` set to a completion value specifying
   * how this frame's execution completed. If successful, this method should
   * return true, with `resumeMode` and `vp` set to a resumption value
   * specifying how execution should continue.
   */
  virtual bool onPop(JSContext* cx, HandleDebuggerFrame frame,
                     ResumeMode& resumeMode, MutableHandleValue vp,
                     HandleSavedFrame exnStack) = 0;
};

class ScriptedOnPopHandler final : public OnPopHandler {
 public:
  explicit ScriptedOnPopHandler(JSObject* object);
  virtual JSObject* object() const override;
  virtual void drop() override;
  virtual void trace(JSTracer* tracer) override;
  virtual bool onPop(JSContext* cx, HandleDebuggerFrame frame,
                     ResumeMode& resumeMode, MutableHandleValue vp,
                     HandleSavedFrame exnStack) override;

 private:
  HeapPtr<JSObject*> object_;
};

class DebuggerFrame : public NativeObject {
  friend class DebuggerArguments;
  friend class ScriptedOnStepHandler;
  friend class ScriptedOnPopHandler;

 public:
  static const Class class_;

  enum {
    OWNER_SLOT = 0,
    ARGUMENTS_SLOT,
    ONSTEP_HANDLER_SLOT,
    ONPOP_HANDLER_SLOT,
    RESERVED_SLOTS,
  };

  static void trace(JSTracer* trc, JSObject* obj);

  static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor,
                                 Handle<GlobalObject*> global);
  static DebuggerFrame* create(JSContext* cx, HandleObject proto,
                               const FrameIter& iter,
                               HandleNativeObject debugger);

  static MOZ_MUST_USE bool getScript(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool getArguments(JSContext* cx,
                                        HandleDebuggerFrame frame,
                                        MutableHandleDebuggerArguments result);
  static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
                                     MutableHandleDebuggerObject result);
  static MOZ_MUST_USE bool getIsConstructing(JSContext* cx,
                                             HandleDebuggerFrame frame,
                                             bool& result);
  static MOZ_MUST_USE bool getEnvironment(
      JSContext* cx, HandleDebuggerFrame frame,
      MutableHandleDebuggerEnvironment result);
  static bool getIsGenerator(HandleDebuggerFrame frame);
  static MOZ_MUST_USE bool getOffset(JSContext* cx, HandleDebuggerFrame frame,
                                     size_t& result);
  static MOZ_MUST_USE bool getOlder(JSContext* cx, HandleDebuggerFrame frame,
                                    MutableHandleDebuggerFrame result);
  static MOZ_MUST_USE bool getThis(JSContext* cx, HandleDebuggerFrame frame,
                                   MutableHandleValue result);
  static DebuggerFrameType getType(HandleDebuggerFrame frame);
  static DebuggerFrameImplementation getImplementation(
      HandleDebuggerFrame frame);
  static MOZ_MUST_USE bool setOnStepHandler(JSContext* cx,
                                            HandleDebuggerFrame frame,
                                            OnStepHandler* handler);

  static MOZ_MUST_USE bool eval(JSContext* cx, HandleDebuggerFrame frame,
                                mozilla::Range<const char16_t> chars,
                                HandleObject bindings,
                                const EvalOptions& options,
                                ResumeMode& resumeMode,
                                MutableHandleValue value,
                                MutableHandleSavedFrame exnStack);

  MOZ_MUST_USE bool requireLive(JSContext* cx);
  static MOZ_MUST_USE DebuggerFrame* checkThis(JSContext* cx,
                                               const CallArgs& args,
                                               const char* fnname,
                                               bool checkLive);

  bool isLive() const;
  OnStepHandler* onStepHandler() const;
  OnPopHandler* onPopHandler() const;
  void setOnPopHandler(OnPopHandler* handler);

  /*
   * Called after a generator/async frame is resumed, before exposing this
   * Debugger.Frame object to any hooks.
   */
  bool resume(const FrameIter& iter);

  bool hasAnyLiveHooks() const;

 private:
  static const ClassOps classOps_;

  static const JSPropertySpec properties_[];
  static const JSFunctionSpec methods_[];

  static void finalize(FreeOp* fop, JSObject* obj);

  static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
  static MOZ_MUST_USE bool getFrameIter(JSContext* cx,
                                        HandleDebuggerFrame frame,
                                        mozilla::Maybe<FrameIter>& result);
  static MOZ_MUST_USE bool requireScriptReferent(JSContext* cx,
                                                 HandleDebuggerFrame frame);

  static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);

  static MOZ_MUST_USE bool argumentsGetter(JSContext* cx, unsigned argc,
                                           Value* vp);
  static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool constructingGetter(JSContext* cx, unsigned argc,
                                              Value* vp);
  static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc,
                                           Value* vp);
  static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool implementationGetter(JSContext* cx, unsigned argc,
                                                Value* vp);
  static MOZ_MUST_USE bool onStepGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool onStepSetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool onPopGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool onPopSetter(JSContext* cx, unsigned argc, Value* vp);

  static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc,
                                                  Value* vp);

  Debugger* owner() const;

 public:
  FrameIter::Data* frameIterData() const;
  void freeFrameIterData(FreeOp* fop);
  void maybeDecrementFrameScriptStepModeCount(FreeOp* fop,
                                              AbstractFramePtr frame);
};

class DebuggerObject : public NativeObject {
 public:
  static const Class class_;

  static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
                                 HandleObject debugCtor);
  static DebuggerObject* create(JSContext* cx, HandleObject proto,
                                HandleObject obj, HandleNativeObject debugger);

  // Properties
  static MOZ_MUST_USE bool getClassName(JSContext* cx,
                                        HandleDebuggerObject object,
                                        MutableHandleString result);
  static MOZ_MUST_USE bool getParameterNames(
      JSContext* cx, HandleDebuggerObject object,
      MutableHandle<StringVector> result);
  static MOZ_MUST_USE bool getBoundTargetFunction(
      JSContext* cx, HandleDebuggerObject object,
      MutableHandleDebuggerObject result);
  static MOZ_MUST_USE bool getBoundThis(JSContext* cx,
                                        HandleDebuggerObject object,
                                        MutableHandleValue result);
  static MOZ_MUST_USE bool getBoundArguments(JSContext* cx,
                                             HandleDebuggerObject object,
                                             MutableHandle<ValueVector> result);
  static MOZ_MUST_USE bool getAllocationSite(JSContext* cx,
                                             HandleDebuggerObject object,
                                             MutableHandleObject result);
  static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx,
                                               HandleDebuggerObject object,
                                               MutableHandleString result);
  static MOZ_MUST_USE bool getErrorNotes(JSContext* cx,
                                         HandleDebuggerObject object,
                                         MutableHandleValue result);
  static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx,
                                              HandleDebuggerObject object,
                                              MutableHandleValue result);
  static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx,
                                                HandleDebuggerObject object,
                                                MutableHandleValue result);
  static MOZ_MUST_USE bool getScriptedProxyTarget(
      JSContext* cx, HandleDebuggerObject object,
      MutableHandleDebuggerObject result);
  static MOZ_MUST_USE bool getScriptedProxyHandler(
      JSContext* cx, HandleDebuggerObject object,
      MutableHandleDebuggerObject result);
  static MOZ_MUST_USE bool getPromiseValue(JSContext* cx,
                                           HandleDebuggerObject object,
                                           MutableHandleValue result);
  static MOZ_MUST_USE bool getPromiseReason(JSContext* cx,
                                            HandleDebuggerObject object,
                                            MutableHandleValue result);

  // Methods
  static MOZ_MUST_USE bool isExtensible(JSContext* cx,
                                        HandleDebuggerObject object,
                                        bool& result);
  static MOZ_MUST_USE bool isSealed(JSContext* cx, HandleDebuggerObject object,
                                    bool& result);
  static MOZ_MUST_USE bool isFrozen(JSContext* cx, HandleDebuggerObject object,
                                    bool& result);
  static MOZ_MUST_USE bool getProperty(JSContext* cx,
                                       HandleDebuggerObject object, HandleId id,
                                       HandleValue receiver,
                                       MutableHandleValue result);
  static MOZ_MUST_USE bool setProperty(JSContext* cx,
                                       HandleDebuggerObject object, HandleId id,
                                       HandleValue value, HandleValue receiver,
                                       MutableHandleValue result);
  static MOZ_MUST_USE bool getPrototypeOf(JSContext* cx,
                                          HandleDebuggerObject object,
                                          MutableHandleDebuggerObject result);
  static MOZ_MUST_USE bool getOwnPropertyNames(JSContext* cx,
                                               HandleDebuggerObject object,
                                               MutableHandle<IdVector> result);
  static MOZ_MUST_USE bool getOwnPropertySymbols(
      JSContext* cx, HandleDebuggerObject object,
      MutableHandle<IdVector> result);
  static MOZ_MUST_USE bool getOwnPropertyDescriptor(
      JSContext* cx, HandleDebuggerObject object, HandleId id,
      MutableHandle<PropertyDescriptor> desc);
  static MOZ_MUST_USE bool preventExtensions(JSContext* cx,
                                             HandleDebuggerObject object);
  static MOZ_MUST_USE bool seal(JSContext* cx, HandleDebuggerObject object);
  static MOZ_MUST_USE bool freeze(JSContext* cx, HandleDebuggerObject object);
  static MOZ_MUST_USE bool defineProperty(JSContext* cx,
                                          HandleDebuggerObject object,
                                          HandleId id,
                                          Handle<PropertyDescriptor> desc);
  static MOZ_MUST_USE bool defineProperties(
      JSContext* cx, HandleDebuggerObject object, Handle<IdVector> ids,
      Handle<PropertyDescriptorVector> descs);
  static MOZ_MUST_USE bool deleteProperty(JSContext* cx,
                                          HandleDebuggerObject object,
                                          HandleId id, ObjectOpResult& result);
  static MOZ_MUST_USE bool call(JSContext* cx, HandleDebuggerObject object,
                                HandleValue thisv, Handle<ValueVector> args,
                                MutableHandleValue result);
  static MOZ_MUST_USE bool forceLexicalInitializationByName(
      JSContext* cx, HandleDebuggerObject object, HandleId id, bool& result);
  static MOZ_MUST_USE bool executeInGlobal(
      JSContext* cx, HandleDebuggerObject object,
      mozilla::Range<const char16_t> chars, HandleObject bindings,
      const EvalOptions& options, ResumeMode& resumeMode,
      MutableHandleValue value, MutableHandleSavedFrame exnStack);
  static MOZ_MUST_USE bool makeDebuggeeValue(JSContext* cx,
                                             HandleDebuggerObject object,
                                             HandleValue value,
                                             MutableHandleValue result);
  static MOZ_MUST_USE bool unsafeDereference(JSContext* cx,
                                             HandleDebuggerObject object,
                                             MutableHandleObject result);
  static MOZ_MUST_USE bool unwrap(JSContext* cx, HandleDebuggerObject object,
                                  MutableHandleDebuggerObject result);

  // Infallible properties
  bool isCallable() const;
  bool isFunction() const;
  bool isDebuggeeFunction() const;
  bool isBoundFunction() const;
  bool isArrowFunction() const;
  bool isAsyncFunction() const;
  bool isGeneratorFunction() const;
  bool isGlobal() const;
  bool isScriptedProxy() const;
  bool isPromise() const;
  JSAtom* name(JSContext* cx) const;
  JSAtom* displayName(JSContext* cx) const;
  JS::PromiseState promiseState() const;
  double promiseLifetime() const;
  double promiseTimeToResolution() const;

 private:
  enum { OWNER_SLOT };

  static const unsigned RESERVED_SLOTS = 1;

  static const ClassOps classOps_;

  static const JSPropertySpec properties_[];
  static const JSPropertySpec promiseProperties_[];
  static const JSFunctionSpec methods_[];

  JSObject* referent() const {
    JSObject* obj = (JSObject*)getPrivate();
    MOZ_ASSERT(obj);
    return obj;
  }

  Debugger* owner() const;
  PromiseObject* promise() const;

  static MOZ_MUST_USE bool requireGlobal(JSContext* cx,
                                         HandleDebuggerObject object);
  static MOZ_MUST_USE bool requirePromise(JSContext* cx,
                                          HandleDebuggerObject object);
  static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);

  // JSNative properties
  static MOZ_MUST_USE bool callableGetter(JSContext* cx, unsigned argc,
                                          Value* vp);
  static MOZ_MUST_USE bool isBoundFunctionGetter(JSContext* cx, unsigned argc,
                                                 Value* vp);
  static MOZ_MUST_USE bool isArrowFunctionGetter(JSContext* cx, unsigned argc,
                                                 Value* vp);
  static MOZ_MUST_USE bool isAsyncFunctionGetter(JSContext* cx, unsigned argc,
                                                 Value* vp);
  static MOZ_MUST_USE bool isGeneratorFunctionGetter(JSContext* cx,
                                                     unsigned argc, Value* vp);
  static MOZ_MUST_USE bool protoGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool classGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool nameGetter(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool displayNameGetter(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool parameterNamesGetter(JSContext* cx, unsigned argc,
                                                Value* vp);
  static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx,
                                                     unsigned argc, Value* vp);
  static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc,
                                           Value* vp);
  static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc,
                                                Value* vp);
  static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc,
                                                Value* vp);
  static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc,
                                                  Value* vp);
  static MOZ_MUST_USE bool errorNotesGetter(JSContext* cx, unsigned argc,
                                            Value* vp);
  static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc,
                                                 Value* vp);
  static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc,
                                                   Value* vp);
  static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc,
                                         Value* vp);
  static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc,
                                              Value* vp);
  static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc,
                                           Value* vp);
  static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc,
                                              Value* vp);
  static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc,
                                              Value* vp);
  static MOZ_MUST_USE bool promiseReasonGetter(JSContext* cx, unsigned argc,
                                               Value* vp);
  static MOZ_MUST_USE bool promiseLifetimeGetter(JSContext* cx, unsigned argc,
                                                 Value* vp);
  static MOZ_MUST_USE bool promiseTimeToResolutionGetter(JSContext* cx,
                                                         unsigned argc,
                                                         Value* vp);
  static MOZ_MUST_USE bool promiseAllocationSiteGetter(JSContext* cx,
                                                       unsigned argc,
                                                       Value* vp);
  static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx,
                                                       unsigned argc,
                                                       Value* vp);
  static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc,
                                           Value* vp);
  static MOZ_MUST_USE bool promiseDependentPromisesGetter(JSContext* cx,
                                                          unsigned argc,
                                                          Value* vp);

  // JSNative methods
  static MOZ_MUST_USE bool isExtensibleMethod(JSContext* cx, unsigned argc,
                                              Value* vp);
  static MOZ_MUST_USE bool isSealedMethod(JSContext* cx, unsigned argc,
                                          Value* vp);
  static MOZ_MUST_USE bool isFrozenMethod(JSContext* cx, unsigned argc,
                                          Value* vp);
  static MOZ_MUST_USE bool getPropertyMethod(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool setPropertyMethod(JSContext* cx, unsigned argc,
                                             Value* vp);
  static MOZ_MUST_USE bool getOwnPropertyNamesMethod(JSContext* cx,
                                                     unsigned argc, Value* vp);
  static MOZ_MUST_USE bool getOwnPropertySymbolsMethod(JSContext* cx,
                                                       unsigned argc,
                                                       Value* vp);
  static MOZ_MUST_USE bool getOwnPropertyDescriptorMethod(JSContext* cx,
                                                          unsigned argc,
                                                          Value* vp);
  static MOZ_MUST_USE bool preventExtensionsMethod(JSContext* cx, unsigned argc,
                                                   Value* vp);
  static MOZ_MUST_USE bool sealMethod(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool freezeMethod(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool definePropertyMethod(JSContext* cx, unsigned argc,
                                                Value* vp);
  static MOZ_MUST_USE bool definePropertiesMethod(JSContext* cx, unsigned argc,
                                                  Value* vp);
  static MOZ_MUST_USE bool deletePropertyMethod(JSContext* cx, unsigned argc,
                                                Value* vp);
  static MOZ_MUST_USE bool callMethod(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool applyMethod(JSContext* cx, unsigned argc, Value* vp);
  static MOZ_MUST_USE bool asEnvironmentMethod(JSContext* cx, unsigned argc,
                                               Value* vp);
  static MOZ_MUST_USE bool forceLexicalInitializationByNameMethod(JSContext* cx,
                                                                  unsigned argc,
                                                                  Value* vp);
  static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc,
                                                 Value* vp);
  static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx,
                                                             unsigned argc,
                                                             Value* vp);
  static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc,
                                                   Value* vp);
  static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc,
                                                   Value* vp);
  static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc,
                                        Value* vp);
  static MOZ_MUST_USE bool getErrorReport(JSContext* cx,
                                          HandleObject maybeError,
                                          JSErrorReport*& report);
};

class JSBreakpointSite;
class WasmBreakpoint;
class WasmBreakpointSite;

class BreakpointSite {
  friend class Breakpoint;
  friend class ::JSScript;
  friend class Debugger;

 public:
  enum class Type { JS, Wasm };

 private:
  Type type_;

  template <typename T>
  struct SiteLinkAccess {
    static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) {
      return aThis->siteLink;
    }
  };

  // List of all js::Breakpoints at this instruction.
  using BreakpointList =
      mozilla::DoublyLinkedList<js::Breakpoint, SiteLinkAccess<js::Breakpoint>>;
  BreakpointList breakpoints;
  size_t enabledCount; /* number of breakpoints in the list that are enabled */

 protected:
  virtual void recompile(FreeOp* fop) = 0;
  bool isEnabled() const { return enabledCount > 0; }

 public:
  BreakpointSite(Type type);
  Breakpoint* firstBreakpoint() const;
  virtual ~BreakpointSite() {}
  bool hasBreakpoint(Breakpoint* bp);
  Type type() const { return type_; }

  void inc(FreeOp* fop);
  void dec(FreeOp* fop);
  bool isEmpty() const;
  virtual void destroyIfEmpty(FreeOp* fop) = 0;

  inline JSBreakpointSite* asJS();
  inline WasmBreakpointSite* asWasm();
};

/*
 * Each Breakpoint is a member of two linked lists: its debugger's list and its
 * site's list.
 *
 * GC rules:
 *   - script is live, breakpoint exists, and debugger is enabled
 *      ==> debugger is live
 *   - script is live, breakpoint exists, and debugger is live
 *      ==> retain the breakpoint and the handler object is live
 *
 * Debugger::markIteratively implements these two rules. It uses
 * Debugger::hasAnyLiveHooks to check for rule 1.
 *
 * Nothing else causes a breakpoint to be retained, so if its script or
 * debugger is collected, the breakpoint is destroyed during GC sweep phase,
 * even if the debugger compartment isn't being GC'd. This is implemented in
 * Zone::sweepBreakpoints.
 */
class Breakpoint {
  friend class Debugger;
  friend class BreakpointSite;

 public:
  Debugger* const debugger;
  BreakpointSite* const site;

 private:
  /*
   * |handler| is marked unconditionally during minor GC so a post barrier is
   * not required.
   */
  js::PreBarrieredObject handler;

  /**
   * Link elements for each list this breakpoint can be in.
   */
  mozilla::DoublyLinkedListElement<Breakpoint> debuggerLink;
  mozilla::DoublyLinkedListElement<Breakpoint> siteLink;

 public:
  Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler);

  enum MayDestroySite { False, True };
  void destroy(FreeOp* fop,
               MayDestroySite mayDestroySite = MayDestroySite::True);

  Breakpoint* nextInDebugger();
  Breakpoint* nextInSite();
  JSObject* getHandler() const { return handler; }
  PreBarrieredObject& getHandlerRef() { return handler; }

  inline WasmBreakpoint* asWasm();
};

class JSBreakpointSite : public BreakpointSite {
 public:
  JSScript* script;
  jsbytecode* const pc;

 protected:
  void recompile(FreeOp* fop) override;

 public:
  JSBreakpointSite(JSScript* script, jsbytecode* pc);

  void destroyIfEmpty(FreeOp* fop) override;
};

inline JSBreakpointSite* BreakpointSite::asJS() {
  MOZ_ASSERT(type() == Type::JS);
  return static_cast<JSBreakpointSite*>(this);
}

class WasmBreakpointSite : public BreakpointSite {
 public:
  wasm::DebugState* debug;
  uint32_t offset;

 protected:
  void recompile(FreeOp* fop) override;

 public:
  WasmBreakpointSite(wasm::DebugState* debug, uint32_t offset);

  void destroyIfEmpty(FreeOp* fop) override;
};

inline WasmBreakpointSite* BreakpointSite::asWasm() {
  MOZ_ASSERT(type() == Type::Wasm);
  return static_cast<WasmBreakpointSite*>(this);
}

class WasmBreakpoint : public Breakpoint {
 public:
  WasmInstanceObject* wasmInstance;

  WasmBreakpoint(Debugger* debugger, WasmBreakpointSite* site,
                 JSObject* handler, WasmInstanceObject* wasmInstance_)
      : Breakpoint(debugger, site, handler), wasmInstance(wasmInstance_) {}
};

inline WasmBreakpoint* Breakpoint::asWasm() {
  MOZ_ASSERT(site && site->type() == BreakpointSite::Type::Wasm);
  return static_cast<WasmBreakpoint*>(this);
}

Breakpoint* Debugger::firstBreakpoint() const {
  if (breakpoints.isEmpty()) {
    return nullptr;
  }
  return &(*breakpoints.begin());
}

const js::GCPtrNativeObject& Debugger::toJSObject() const {
  MOZ_ASSERT(object);
  return object;
}

js::GCPtrNativeObject& Debugger::toJSObjectRef() {
  MOZ_ASSERT(object);
  return object;
}

bool Debugger::observesEnterFrame() const {
  return enabled && getHook(OnEnterFrame);
}

bool Debugger::observesNewScript() const {
  return enabled && getHook(OnNewScript);
}

bool Debugger::observesNewGlobalObject() const {
  return enabled && getHook(OnNewGlobalObject);
}

bool Debugger::observesGlobal(GlobalObject* global) const {
  WeakHeapPtr<GlobalObject*> debuggee(global);
  return debuggees.has(debuggee);
}

/* static */ void Debugger::onNewScript(JSContext* cx, HandleScript script) {
  // We early return in slowPathOnNewScript for self-hosted scripts, so we can
  // ignore those in our assertion here.
  MOZ_ASSERT_IF(!script->realm()->creationOptions().invisibleToDebugger() &&
                    !script->selfHosted(),
                script->realm()->firedOnNewGlobalObject);

  // The script may not be ready to be interrogated by the debugger.
  if (script->hideScriptFromDebugger()) {
    return;
  }

  if (script->realm()->isDebuggee()) {
    slowPathOnNewScript(cx, script);
  }
}

/* static */ void Debugger::onNewGlobalObject(JSContext* cx,
                                              Handle<GlobalObject*> global) {
  MOZ_ASSERT(!global->realm()->firedOnNewGlobalObject);
#ifdef DEBUG
  global->realm()->firedOnNewGlobalObject = true;
#endif
  if (!cx->runtime()->onNewGlobalObjectWatchers().isEmpty()) {
    Debugger::slowPathOnNewGlobalObject(cx, global);
  }
}

/* static */ bool Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj,
                                                HandleSavedFrame frame,
                                                mozilla::TimeStamp when) {
  GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
  if (!dbgs || dbgs->empty()) {
    return true;
  }
  RootedObject hobj(cx, obj);
  return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
}

MOZ_MUST_USE bool ReportObjectRequired(JSContext* cx);

} /* namespace js */

namespace JS {

template <>
struct DeletePolicy<js::Debugger>
    : public js::GCManagedDeletePolicy<js::Debugger> {};

} /* namespace JS */

#endif /* vm_Debugger_h */